first commit for opensource
first commit for opensource
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright (C) 2016 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 "qcloud.h"
|
||||
|
||||
__QCLOUD_STATIC__ void coap_auth_callback(void *message, void *context)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY
|
||||
|
||||
QCLOUD_POINTER_SANITY_CHECK_RTN(message);
|
||||
QCLOUD_POINTER_SANITY_CHECK_RTN(context);
|
||||
|
||||
coap_message_t *msg = NULL;
|
||||
qcloud_coap_client_t *client = NULL;
|
||||
|
||||
msg = (coap_message_t *)message;
|
||||
client = (qcloud_coap_client_t *)context;
|
||||
|
||||
if (msg->code_class != COAP_CODE_CLASS_SUCCESS ||
|
||||
msg->code_detail != COAP_CODE_DETAIL_205_CONTENT) {
|
||||
client->auth_state = QCLOUD_COAP_AUTH_STATE_FAIL;
|
||||
QCLOUD_LOG_E("auth token failed, code_class: %d code_detail: %d", msg->code_class, msg->code_detail);
|
||||
return;
|
||||
}
|
||||
|
||||
QCLOUD_LOG_I("auth token success, code_class: %d code_detail: %d", msg->code_class, msg->code_detail);
|
||||
|
||||
if (msg->payload_len == 0 ||
|
||||
msg->payload == NULL ||
|
||||
strlen(msg->payload) == 0) {
|
||||
client->auth_state = QCLOUD_COAP_AUTH_STATE_FAIL;
|
||||
QCLOUD_LOG_E("auth token response empty");
|
||||
} else {
|
||||
client->auth_token_len = msg->payload_len;
|
||||
client->auth_token = osal_malloc(client->auth_token_len);
|
||||
strncpy(client->auth_token, msg->payload,client->auth_token_len);
|
||||
client->auth_state = QCLOUD_COAP_AUTH_STATE_SUCCESS;
|
||||
QCLOUD_LOG_D("auth_token_len = %d, auth_token = %.*s", client->auth_token_len, client->auth_token_len, client->auth_token);
|
||||
}
|
||||
|
||||
QCLOUD_FUNC_EXIT
|
||||
}
|
||||
|
||||
__QCLOUD_INTERNAL__ qcloud_err_t coap_auth(qcloud_coap_client_t *client, char *connection_id)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY
|
||||
|
||||
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_COAP_NULL);
|
||||
|
||||
int len;
|
||||
qcloud_err_t rc;
|
||||
char message_token[8] = {0};
|
||||
coap_message_t connect_msg = COAP_MESSAGE_INITIALIZER;
|
||||
|
||||
coap_message_init(&connect_msg);
|
||||
|
||||
coap_message_type_set(&connect_msg, COAP_MSG_TYPE_CON);
|
||||
coap_message_code_set(&connect_msg, COAP_CODE_CLASS_REQ, COAP_REQUEST_METHOD_POST);
|
||||
|
||||
coap_message_id_set(&connect_msg, coap_glue_packet_id_generate(client));
|
||||
|
||||
len = coap_message_token_get(client, message_token);
|
||||
coap_message_token_set(&connect_msg, message_token, len);
|
||||
|
||||
coap_message_option_add(&connect_msg, COAP_MSG_OPTION_CODE_URI_PATH, strlen(client->auth_uri), client->auth_uri);
|
||||
|
||||
coap_message_option_add(&connect_msg, COAP_MSG_OPTION_CODE_NEED_RESP, 1, "0");
|
||||
|
||||
coap_message_callback_set(&connect_msg, coap_auth_callback);
|
||||
coap_message_context_set(&connect_msg, client);
|
||||
|
||||
connect_msg.payload_len = sizeof(client->auth_id);
|
||||
connect_msg.payload = (char *)osal_malloc(connect_msg.payload_len);
|
||||
if (!connect_msg.payload) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_INVAL);
|
||||
}
|
||||
|
||||
coap_message_payload_set(&connect_msg, client->auth_id, connect_msg.payload_len);
|
||||
|
||||
rc = coap_glue_msg_send(client, &connect_msg);
|
||||
|
||||
osal_free(connect_msg.payload);
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(rc)
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@@ -0,0 +1,306 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright (C) 2016 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 "qcloud.h"
|
||||
|
||||
__QCLOUD_STATIC__ qcloud_err_t coap_client_network_host_construct(qcloud_network_t *network, qcloud_device_t *device)
|
||||
{
|
||||
int server_len;
|
||||
char coap_server[QCLOUD_SERVER_DOMAIN_MAX];
|
||||
|
||||
memset(network->host, 0, sizeof(network->host));
|
||||
server_len = osal_snprintf(coap_server, sizeof(coap_server), "%s.%s", device->product_id, qcloud_coap_server);
|
||||
if (server_len < 0 || server_len > QCLOUD_SERVER_DOMAIN_MAX - 1) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
|
||||
}
|
||||
memcpy(network->host, coap_server, sizeof(network->host));
|
||||
|
||||
network->port = qcloud_coap_port;
|
||||
|
||||
return QCLOUD_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ qcloud_err_t coap_client_network_init(qcloud_network_t *network, qcloud_device_t *device)
|
||||
{
|
||||
#if (QCLOUD_CFG_TLS_EN > 0u)
|
||||
QCLOUD_FUNC_EXIT_RC_IF_NOT(qcloud_tls_init(&network->tls_opt, device), QCLOUD_ERR_SUCCESS, QCLOUD_ERR_FAILURE);
|
||||
QCLOUD_FUNC_EXIT_RC_IF_NOT(qcloud_network_dtls_init(network), QCLOUD_ERR_SUCCESS, QCLOUD_ERR_FAILURE);
|
||||
#else
|
||||
QCLOUD_FUNC_EXIT_RC_IF_NOT(qcloud_network_udp_init(network), QCLOUD_ERR_SUCCESS, QCLOUD_ERR_FAILURE);
|
||||
#endif
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC_IF_NOT(coap_client_network_host_construct(network, device), QCLOUD_ERR_SUCCESS, QCLOUD_ERR_FAILURE);
|
||||
|
||||
return QCLOUD_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ uint16_t coap_client_random_packet_id_generate(void)
|
||||
{
|
||||
#define PACKET_ID_MAX (65535)
|
||||
|
||||
srand((unsigned)osal_timer_current_sec());
|
||||
return rand() % (PACKET_ID_MAX + 1) + 1;
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ qcloud_err_t coap_client_construct(qcloud_coap_client_t *client,
|
||||
qcloud_device_t *device,
|
||||
coap_event_handler_fn_t handler)
|
||||
{
|
||||
int len = 0;
|
||||
|
||||
client->auth_state = QCLOUD_COAP_AUTH_STATE_NONE;
|
||||
client->command_timeout = QCLOUD_COAP_COMMAND_TIMEOUT;
|
||||
client->message_token = 0;
|
||||
|
||||
client->event_handler.handler = handler;
|
||||
|
||||
// packet id 取随机数 1- 65536
|
||||
client->packet_id = coap_client_random_packet_id_generate();
|
||||
client->auth_token = NULL;
|
||||
client->auth_token_len = 0;
|
||||
client->retransmit_max = 1;
|
||||
|
||||
len = osal_snprintf(client->auth_uri, sizeof(client->auth_uri), "%s/%s/%s", device->product_id, device->device_name, QCLOUD_COAP_AUTH_URI);
|
||||
if (len < 0 || len >= QCLOUD_COAP_AUTH_URI_MAX) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_INVAL);
|
||||
}
|
||||
|
||||
qcloud_list_init(&client->message_list);
|
||||
|
||||
if ((client->message_list_lock = osal_mutex_create()) == NULL) {
|
||||
goto errout;
|
||||
}
|
||||
|
||||
if ((client->tx_lock = osal_mutex_create()) == NULL) {
|
||||
goto errout;
|
||||
}
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
|
||||
|
||||
errout:
|
||||
if (client->message_list_lock) {
|
||||
osal_mutex_destroy(client->message_list_lock);
|
||||
}
|
||||
|
||||
if (client->tx_lock) {
|
||||
osal_mutex_destroy(client->tx_lock);
|
||||
}
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE)
|
||||
}
|
||||
|
||||
__QCLOUD_API__ qcloud_err_t qcloud_coap_client_create(qcloud_coap_client_t *client,
|
||||
qcloud_device_t *device,
|
||||
coap_event_handler_fn_t handler)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(device, QCLOUD_ERR_INVAL);
|
||||
|
||||
memset(client, 0, sizeof(qcloud_coap_client_t));
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC_IF_NOT(coap_client_network_init(&client->network, device), QCLOUD_ERR_SUCCESS, QCLOUD_ERR_FAILURE);
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC_IF_NOT(coap_client_construct(client, device, handler), QCLOUD_ERR_SUCCESS, QCLOUD_ERR_FAILURE);
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
|
||||
}
|
||||
|
||||
__QCLOUD_API__ qcloud_err_t qcloud_coap_client_connect(qcloud_coap_client_t *client)
|
||||
{
|
||||
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
|
||||
int len;
|
||||
qcloud_err_t rc;
|
||||
char connection_id[QCLOUD_COAP_CONNECT_ID_MAX + 1];
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC_IF_NOT(rc = client->network.connect(&client->network), QCLOUD_ERR_SUCCESS, rc);
|
||||
|
||||
coap_glue_connect_id_generate(connection_id);
|
||||
|
||||
len = osal_snprintf(client->auth_id, sizeof(client->auth_id), "%s;%s", QCLOUD_APPID, connection_id);
|
||||
if (len < 0 || len >= QCLOUD_COAP_AUTH_ID_MAX) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_INVAL);
|
||||
}
|
||||
|
||||
coap_auth(client, connection_id);
|
||||
|
||||
while (client->auth_state == QCLOUD_COAP_AUTH_STATE_NONE) {
|
||||
qcloud_coap_client_yield(client, 200);
|
||||
}
|
||||
|
||||
if (client->auth_state != QCLOUD_COAP_AUTH_STATE_SUCCESS) {
|
||||
QCLOUD_LOG_I("auth failed");
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
|
||||
}
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
|
||||
}
|
||||
|
||||
__QCLOUD_API__ qcloud_err_t qcloud_coap_client_yield(qcloud_coap_client_t *client, uint32_t timeout_ms)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
|
||||
return coap_glue_spin(client, timeout_ms);
|
||||
}
|
||||
|
||||
__QCLOUD_API__ qcloud_err_t qcloud_coap_client_destroy(qcloud_coap_client_t *client)
|
||||
{
|
||||
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
|
||||
if (client->network.is_connected && client->network.is_connected(&client->network)) {
|
||||
client->network.disconnect(&client->network);
|
||||
}
|
||||
|
||||
coap_glue_message_list_destroy(client);
|
||||
|
||||
osal_mutex_destroy(client->tx_lock);
|
||||
osal_mutex_destroy(client->message_list_lock);
|
||||
|
||||
if (client->auth_token) {
|
||||
osal_free(client->auth_token);
|
||||
client->auth_token = NULL;
|
||||
}
|
||||
|
||||
client->auth_token_len = 0;
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
|
||||
}
|
||||
|
||||
|
||||
__QCLOUD_API__ qcloud_err_t qcloud_coap_client_msg_send(qcloud_coap_client_t *client,
|
||||
char *topic,
|
||||
coap_send_opt_t *send_opt)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY
|
||||
|
||||
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(topic, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(send_opt, QCLOUD_ERR_INVAL);
|
||||
|
||||
int len;
|
||||
qcloud_err_t rc;
|
||||
char message_token[8] = {0};
|
||||
coap_message_t send_msg = COAP_MESSAGE_INITIALIZER;
|
||||
|
||||
if (strlen(topic) > QCLOUD_COAP_URI_MAX) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_MAX_TOPIC_LENGTH);
|
||||
}
|
||||
|
||||
coap_message_init(&send_msg);
|
||||
|
||||
coap_message_type_set(&send_msg, COAP_MSG_TYPE_CON);
|
||||
coap_message_code_set(&send_msg, COAP_CODE_CLASS_REQ, COAP_REQUEST_METHOD_POST);
|
||||
|
||||
coap_message_id_set(&send_msg, coap_glue_packet_id_generate(client));
|
||||
|
||||
len = coap_message_token_get(client, message_token);
|
||||
coap_message_token_set(&send_msg, message_token, len);
|
||||
|
||||
send_msg.payload = (char *)osal_malloc(send_opt->payload_len);
|
||||
if (!send_msg.payload) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_INVAL);
|
||||
}
|
||||
|
||||
coap_message_payload_set(&send_msg, send_opt->payload, send_opt->payload_len);
|
||||
|
||||
coap_message_option_add(&send_msg, COAP_MSG_OPTION_CODE_URI_PATH, strlen(topic), topic);
|
||||
coap_message_option_add(&send_msg, COAP_MSG_OPTION_CODE_AUTH_TOKEN, client->auth_token_len, client->auth_token);
|
||||
|
||||
if (send_opt->resp_cb) {
|
||||
coap_message_option_add(&send_msg, COAP_MSG_OPTION_CODE_NEED_RESP, 1, "1");
|
||||
coap_message_callback_set(&send_msg, send_opt->resp_cb);
|
||||
} else {
|
||||
coap_message_option_add(&send_msg, COAP_MSG_OPTION_CODE_NEED_RESP, 1, "0");
|
||||
}
|
||||
|
||||
coap_message_context_set(&send_msg, send_opt->context);
|
||||
|
||||
rc = coap_glue_msg_send(client, &send_msg);
|
||||
osal_free(send_msg.payload);
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(rc)
|
||||
}
|
||||
|
||||
__QCLOUD_API__ uint16_t qcloud_coap_msg_id_get(coap_message_t *message)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY
|
||||
|
||||
if (!message) {
|
||||
return COAP_MSG_ID_MAX;
|
||||
}
|
||||
|
||||
return message->id;
|
||||
}
|
||||
|
||||
__QCLOUD_API__ qcloud_err_t qcloud_coap_msg_payload_get(coap_message_t *message, char **payload, int *payload_len)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY
|
||||
|
||||
QCLOUD_POINTER_SANITY_CHECK(message, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(payload, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(payload_len, QCLOUD_ERR_INVAL);
|
||||
|
||||
if (message->code_class != COAP_CODE_CLASS_SUCCESS ||
|
||||
message->code_detail != COAP_CODE_DETAIL_205_CONTENT) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE)
|
||||
}
|
||||
|
||||
*payload = message->payload;
|
||||
*payload_len = message->payload_len;
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS)
|
||||
}
|
||||
|
||||
__QCLOUD_API__ coap_event_type_t qcloud_coap_event_type_get(coap_message_t *message)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY
|
||||
|
||||
QCLOUD_POINTER_SANITY_CHECK(message, COAP_EVENT_TYPE_UNAUTHORIZED);
|
||||
|
||||
switch (message->code_class) {
|
||||
case COAP_CODE_CLASS_SUCCESS:
|
||||
return COAP_EVENT_TYPE_RECEIVE_RESPCONTENT;
|
||||
|
||||
case COAP_CODE_CLASS_SERVER_ERR:
|
||||
return COAP_EVENT_TYPE_INTERNAL_SERVER_ERROR;
|
||||
|
||||
case COAP_CODE_CLASS_INTERNAL_ERR:
|
||||
return COAP_EVENT_TYPE_SEPRESP_TIMEOUT;
|
||||
|
||||
case COAP_CODE_CLASS_CLIENT_ERR:
|
||||
if (message->code_detail == COAP_CODE_DETAIL_401_UNAUTHORIZED) {
|
||||
return COAP_EVENT_TYPE_UNAUTHORIZED;
|
||||
} else {
|
||||
return COAP_EVENT_TYPE_FORBIDDEN;
|
||||
}
|
||||
|
||||
default:
|
||||
QCLOUD_LOG_E("not supported code class: %d", message->code_class);
|
||||
return COAP_EVENT_TYPE_ACK_TIMEOUT;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@@ -0,0 +1,276 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright (C) 2016 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 "qcloud.h"
|
||||
|
||||
/**
|
||||
* @brief Free an option structure that was allocated by coap_msg_op_new
|
||||
*
|
||||
* @param[in,out] op Pointer to the option structure
|
||||
*/
|
||||
__QCLOUD_STATIC__ void coap_message_option_destroy(coap_msg_option_t *option)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY
|
||||
|
||||
if (option->val) {
|
||||
osal_free(option->val);
|
||||
}
|
||||
osal_free(option);
|
||||
|
||||
QCLOUD_FUNC_EXIT
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deinitialise an option linked-list structure
|
||||
*
|
||||
* @param[in,out] list Pointer to an option linked-list structure
|
||||
*/
|
||||
__QCLOUD_STATIC__ void coap_message_option_list_destroy(coap_message_t *message)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY
|
||||
|
||||
QCLOUD_POINTER_SANITY_CHECK_RTN(message);
|
||||
|
||||
qcloud_list_t *curr, *next;
|
||||
coap_msg_option_t *option;
|
||||
|
||||
if (qcloud_list_empty(&message->option_list)) {
|
||||
QCLOUD_FUNC_EXIT;
|
||||
}
|
||||
|
||||
QCLOUD_LIST_FOR_EACH_SAFE(curr, next, &message->option_list) {
|
||||
option = QCLOUD_LIST_ENTRY(curr, coap_msg_option_t, list);
|
||||
|
||||
coap_message_option_destroy(option);
|
||||
}
|
||||
|
||||
QCLOUD_FUNC_EXIT;
|
||||
}
|
||||
|
||||
__QCLOUD_INTERNAL__ void coap_message_init(coap_message_t *message)
|
||||
{
|
||||
message->version = COAP_VERSION;
|
||||
qcloud_list_init(&message->option_list);
|
||||
}
|
||||
|
||||
__QCLOUD_INTERNAL__ int coap_message_token_get(qcloud_coap_client_t *client, char *buf)
|
||||
{
|
||||
uint32_t token;
|
||||
|
||||
token = client->message_token;
|
||||
|
||||
buf[0] = ((token & 0x00FF) >> 0);
|
||||
buf[1] = ((token & 0xFF00) >> 8);
|
||||
buf[2] = ((token & 0xFF0000) >> 16);
|
||||
buf[3] = ((token & 0xFF000000) >> 24);
|
||||
|
||||
++client->message_token;
|
||||
|
||||
return sizeof(uint32_t);
|
||||
}
|
||||
|
||||
__QCLOUD_INTERNAL__ qcloud_err_t coap_message_type_set(coap_message_t *message, uint8_t type)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY
|
||||
|
||||
message->type = type;
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS)
|
||||
}
|
||||
|
||||
__QCLOUD_INTERNAL__ qcloud_err_t coap_message_code_set(coap_message_t *message, uint32_t code_class, uint32_t code_detail)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY
|
||||
|
||||
if (code_class > COAP_MSG_CODE_CLASS_MAX) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_INVAL)
|
||||
}
|
||||
|
||||
if (code_detail > COAP_MSG_CODE_DETAIL_MAX) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_INVAL)
|
||||
}
|
||||
|
||||
message->code_class = code_class;
|
||||
message->code_detail = code_detail;
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS)
|
||||
}
|
||||
|
||||
__QCLOUD_INTERNAL__ qcloud_err_t coap_message_id_set(coap_message_t *message, uint16_t id)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY
|
||||
|
||||
if (id > COAP_MSG_ID_MAX) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_INVAL)
|
||||
}
|
||||
|
||||
message->id = id;
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS)
|
||||
}
|
||||
|
||||
__QCLOUD_INTERNAL__ qcloud_err_t coap_message_token_set(coap_message_t *message, char *buf, uint8_t len)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY
|
||||
|
||||
if (len > COAP_MSG_TOKEN_MAX) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_INVAL)
|
||||
}
|
||||
|
||||
memcpy(message->token, buf, len);
|
||||
message->token_len = len;
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS)
|
||||
}
|
||||
|
||||
__QCLOUD_INTERNAL__ qcloud_err_t coap_message_payload_set(coap_message_t *message, char *buf, size_t len)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY
|
||||
|
||||
if (len > 0 && !message->payload) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE)
|
||||
}
|
||||
|
||||
message->payload_len = 0;
|
||||
|
||||
if (len > 0) {
|
||||
memcpy(message->payload, buf, len);
|
||||
message->payload_len = len;
|
||||
}
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS)
|
||||
}
|
||||
|
||||
__QCLOUD_INTERNAL__ coap_msg_option_t *coap_message_option_construct(uint16_t option_code, uint32_t len, const char *val)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY
|
||||
|
||||
char *this_val = NULL;
|
||||
coap_msg_option_t *option = NULL;
|
||||
|
||||
option = (coap_msg_option_t *)osal_malloc(sizeof(coap_msg_option_t));
|
||||
if (!option) {
|
||||
QCLOUD_LOG_E("memory alloc failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
this_val = (char *)osal_malloc(len);
|
||||
if (!this_val) {
|
||||
osal_free(option);
|
||||
QCLOUD_LOG_E("memory alloc failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
option->option_code = option_code;
|
||||
option->val_len = len;
|
||||
option->val = this_val;
|
||||
|
||||
memcpy(option->val, val, len);
|
||||
|
||||
qcloud_list_init(&option->list);
|
||||
|
||||
return option;
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ void coap_message_option_do_add(coap_message_t *message, coap_msg_option_t *option)
|
||||
{
|
||||
coap_msg_option_t *iter;
|
||||
qcloud_list_t *curr, *option_list;
|
||||
|
||||
option_list = &message->option_list;
|
||||
/* keep option_code in ascending order */
|
||||
QCLOUD_LIST_FOR_EACH(curr, option_list) {
|
||||
iter = QCLOUD_LIST_ENTRY(curr, coap_msg_option_t, list);
|
||||
if (option->option_code <= iter->option_code) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
qcloud_list_add_tail(&option->list, curr);
|
||||
}
|
||||
|
||||
__QCLOUD_INTERNAL__ qcloud_err_t coap_message_option_add(coap_message_t *message, coap_msg_opt_code_t option_code, uint32_t len, const char *val)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY
|
||||
|
||||
QCLOUD_POINTER_SANITY_CHECK(message, QCLOUD_ERR_INVAL);
|
||||
|
||||
coap_msg_option_t *option = NULL;
|
||||
|
||||
option = coap_message_option_construct(option_code, len, val);
|
||||
if (!option) {
|
||||
QCLOUD_LOG_E("option alloc failed.");
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE)
|
||||
}
|
||||
|
||||
coap_message_option_do_add(message, option);
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS)
|
||||
}
|
||||
|
||||
__QCLOUD_INTERNAL__ qcloud_err_t coap_message_callback_set(coap_message_t *message, coap_resp_callback_t callback)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY
|
||||
|
||||
message->resp_cb = callback;
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS)
|
||||
}
|
||||
|
||||
__QCLOUD_INTERNAL__ qcloud_err_t coap_message_context_set(coap_message_t *message, void *context)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY
|
||||
|
||||
message->context = context;
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS)
|
||||
}
|
||||
|
||||
__QCLOUD_INTERNAL__ void coap_message_destroy(coap_message_t *message)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY
|
||||
|
||||
coap_message_option_list_destroy(message);
|
||||
|
||||
if (message->payload) {
|
||||
osal_free(message->payload);
|
||||
}
|
||||
|
||||
memset(message, 0, sizeof(coap_message_t));
|
||||
|
||||
QCLOUD_FUNC_EXIT
|
||||
}
|
||||
|
||||
__QCLOUD_INTERNAL__ void coap_message_dump(coap_message_t* message)
|
||||
{
|
||||
QCLOUD_LOG_I("version = %u", message->version);
|
||||
QCLOUD_LOG_I("type = %d", message->type);
|
||||
QCLOUD_LOG_I("code_class = %u", message->code_class);
|
||||
QCLOUD_LOG_I("code_detail = %u", message->code_detail);
|
||||
QCLOUD_LOG_I("id = %d", message->id);
|
||||
QCLOUD_LOG_I("payload_len = %d", message->payload_len);
|
||||
QCLOUD_LOG_I("payload: %s", message->payload);
|
||||
|
||||
QCLOUD_LOG_I("token_len = %u", message->token_len);
|
||||
QCLOUD_LOG_I("token: %s", message->token);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@@ -0,0 +1,368 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright (C) 2016 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 "qcloud.h"
|
||||
|
||||
#define COAP_SWAP_UINT16(n) ((((uint16_t)(n) & 0xff00) >> 8) | (((uint16_t)(n) & 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
|
||||
*/
|
||||
__QCLOUD_STATIC__ qcloud_err_t coap_message_verify(coap_message_t *message)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY
|
||||
|
||||
if (COAP_MSG_IS_EMPTY(message)) {
|
||||
/* empty message */
|
||||
if (message->type == COAP_MSG_TYPE_NON ||
|
||||
message->token_len != 0 ||
|
||||
!qcloud_list_empty(&message->option_list) ||
|
||||
message->payload_len != 0) {
|
||||
QCLOUD_LOG_E("message option not empty");
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_COAP_DATA_SIZE)
|
||||
}
|
||||
}
|
||||
|
||||
/* non-empty message */
|
||||
if (message->type == COAP_MSG_TYPE_RST) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_COAP_DATA_SIZE)
|
||||
}
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_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
|
||||
*/
|
||||
__QCLOUD_STATIC__ qcloud_err_t coap_message_option_list_add(coap_message_t *message, uint16_t option_code, uint32_t len, const char *val)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY
|
||||
|
||||
coap_msg_option_t *option = NULL;
|
||||
|
||||
option = coap_message_option_construct(option_code, len, val);
|
||||
if (!option) {
|
||||
QCLOUD_LOG_E("allocate new option failed.");
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE)
|
||||
}
|
||||
|
||||
qcloud_list_add_tail(&option->list, &message->option_list);
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_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
|
||||
*/
|
||||
__QCLOUD_STATIC__ int coap_message_deserialize_header(coap_message_t *message, char *buf, size_t len)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY
|
||||
|
||||
char *p = buf;
|
||||
|
||||
if (len < 4) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_COAP_DATA_SIZE);
|
||||
}
|
||||
|
||||
message->version = (p[0] >> 6) & 0x03;
|
||||
if (message->version != COAP_VERSION) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_INVAL);
|
||||
}
|
||||
|
||||
message->type = (p[0] >> 4) & 0x03;
|
||||
message->token_len = p[0] & 0x0f;
|
||||
if (message->token_len > sizeof(message->token)) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_COAP_DATA_SIZE)
|
||||
}
|
||||
|
||||
message->code_detail = p[1] & 0x1f;
|
||||
message->code_class = (p[1] >> 5) & 0x07;
|
||||
|
||||
if (message->code_class != COAP_CODE_CLASS_REQ &&
|
||||
message->code_class != COAP_CODE_CLASS_SUCCESS &&
|
||||
message->code_class != COAP_CODE_CLASS_CLIENT_ERR &&
|
||||
message->code_class != COAP_CODE_CLASS_SERVER_ERR) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_COAP_BADMSG)
|
||||
}
|
||||
|
||||
message->id = COAP_SWAP_UINT16(*((uint16_t *)(&p[2])));
|
||||
p += 4;
|
||||
len -= 4;
|
||||
|
||||
QCLOUD_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
|
||||
*/
|
||||
__QCLOUD_STATIC__ int coap_message_deserialize_token(coap_message_t *message, char *buf, size_t len)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY
|
||||
|
||||
if (len < message->token_len) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_COAP_DATA_SIZE)
|
||||
}
|
||||
|
||||
memcpy(message->token, buf, message->token_len);
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(message->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
|
||||
*/
|
||||
__QCLOUD_STATIC__ int coap_message_deserialize_option(coap_message_t *message, char *buf, size_t len)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY
|
||||
|
||||
char *p = buf;
|
||||
qcloud_err_t rc;
|
||||
uint16_t option_code_delta = 0, option_len = 0, option_code = 0;
|
||||
|
||||
if (len < 1) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_COAP_DATA_SIZE)
|
||||
}
|
||||
|
||||
option_code_delta = (p[0] >> 4) & 0x0f;
|
||||
option_len = p[0] & 0x0f;
|
||||
if ((option_code_delta == 15) || (option_len == 15)) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_COAP_DATA_SIZE)
|
||||
}
|
||||
|
||||
p++;
|
||||
len--;
|
||||
if (option_code_delta == 13) {
|
||||
if (len < 1) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_COAP_DATA_SIZE)
|
||||
}
|
||||
option_code_delta += p[0];
|
||||
p++;
|
||||
len--;
|
||||
} else if (option_code_delta == 14) {
|
||||
if (len < 2) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_COAP_DATA_SIZE)
|
||||
}
|
||||
option_code_delta = 269 + COAP_SWAP_UINT16(*((uint16_t *)(&p[0])));
|
||||
p += 2;
|
||||
len -= 2;
|
||||
}
|
||||
|
||||
if (option_len == 13) {
|
||||
if (len < 1) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_COAP_DATA_SIZE)
|
||||
}
|
||||
option_len += p[0];
|
||||
p++;
|
||||
len--;
|
||||
} else if (option_len == 14) {
|
||||
if (len < 2) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_COAP_DATA_SIZE)
|
||||
}
|
||||
option_len = 269 + COAP_SWAP_UINT16(*((uint16_t *)(&p[0])));
|
||||
p += 2;
|
||||
len -= 2;
|
||||
}
|
||||
if (len < option_len) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_COAP_DATA_SIZE)
|
||||
}
|
||||
|
||||
option_code += option_code_delta;
|
||||
|
||||
rc = coap_message_option_list_add(message, option_code, option_len, p);
|
||||
QCLOUD_FUNC_EXIT_RC_IF_NOT(rc, QCLOUD_ERR_SUCCESS, -1);
|
||||
|
||||
p += option_len;
|
||||
|
||||
QCLOUD_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
|
||||
*/
|
||||
__QCLOUD_STATIC__ int coap_message_deserialize_options(coap_message_t *message, char *buf, size_t buf_len)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY
|
||||
|
||||
int len = 0;
|
||||
char *p = buf;
|
||||
|
||||
while ((p[0] & 0xff) != 0xff && buf_len != 0) {
|
||||
len = coap_message_deserialize_option(message, p, buf_len);
|
||||
if (len < 0) {
|
||||
return len;
|
||||
}
|
||||
|
||||
p += len;
|
||||
buf_len -= len;
|
||||
}
|
||||
|
||||
QCLOUD_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
|
||||
*/
|
||||
__QCLOUD_STATIC__ int coap_message_deserialize_payload(coap_message_t *message, char *buf, size_t len)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY
|
||||
|
||||
char *p = buf;
|
||||
|
||||
if (len == 0) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS)
|
||||
}
|
||||
|
||||
if ((p[0] & 0xff) != 0xff) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_COAP_DATA_SIZE)
|
||||
}
|
||||
|
||||
p++;
|
||||
len--;
|
||||
if (len == 0) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_COAP_DATA_SIZE)
|
||||
}
|
||||
|
||||
message->payload = (char *)osal_malloc(len);
|
||||
|
||||
if (!message->payload){
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_INVAL)
|
||||
}
|
||||
|
||||
memcpy(message->payload, p, len);
|
||||
message->payload_len = len;
|
||||
p += len;
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(p - buf)
|
||||
}
|
||||
|
||||
__QCLOUD_INTERNAL__ qcloud_err_t coap_message_deserialize(coap_message_t *message, char *buf, size_t buf_len)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY
|
||||
|
||||
int len = 0;
|
||||
char *p = buf;
|
||||
|
||||
len = coap_message_deserialize_header(message, p, buf_len);
|
||||
if (len < 0) {
|
||||
QCLOUD_LOG_E("coap_message_deserialize_header failed, num:%lu", len);
|
||||
goto errout;
|
||||
}
|
||||
|
||||
p += len;
|
||||
buf_len -= len;
|
||||
len = coap_message_deserialize_token(message, p, buf_len);
|
||||
if (len < 0) {
|
||||
QCLOUD_LOG_E("coap_message_deserialize_token failed, num:%lu", len);
|
||||
goto errout;
|
||||
}
|
||||
|
||||
p += len;
|
||||
buf_len -= len;
|
||||
len = coap_message_deserialize_options(message, p, buf_len);
|
||||
if (len < 0) {
|
||||
QCLOUD_LOG_E("coap_message_deserialize_options failed, num:%lu", len);
|
||||
goto errout;
|
||||
}
|
||||
|
||||
p += len;
|
||||
buf_len -= len;
|
||||
len = coap_message_deserialize_payload(message, p, buf_len);
|
||||
if (len < 0) {
|
||||
QCLOUD_LOG_E("coap_message_deserialize_payload failed, num:%lu", len);
|
||||
goto errout;
|
||||
}
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(coap_message_verify(message))
|
||||
|
||||
errout:
|
||||
coap_message_destroy(message);
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_COAP_INTERNAL)
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@@ -0,0 +1,489 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright (C) 2016 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 "qcloud.h"
|
||||
|
||||
__QCLOUD_INTERNAL__ void coap_glue_connect_id_generate(char *conn_id)
|
||||
{
|
||||
int i = 0, flag;
|
||||
|
||||
srand((unsigned)osal_timer_current_sec());
|
||||
|
||||
for (i = 0; i < QCLOUD_COAP_CONNECT_ID_MAX - 1; ++i) {
|
||||
flag = rand() % 3;
|
||||
|
||||
switch (flag) {
|
||||
case 0:
|
||||
conn_id[i] = (rand() % 26) + 'a';
|
||||
break;
|
||||
|
||||
case 1:
|
||||
conn_id[i] = (rand() % 26) + 'A';
|
||||
break;
|
||||
|
||||
case 2:
|
||||
conn_id[i] = (rand() % 10) + '0';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
conn_id[QCLOUD_COAP_CONNECT_ID_MAX - 1] = '\0';
|
||||
}
|
||||
|
||||
__QCLOUD_INTERNAL__ uint16_t coap_glue_packet_id_generate(qcloud_coap_client_t *client)
|
||||
{
|
||||
#define PACKET_ID_MAX (65535)
|
||||
|
||||
uint16_t packet_id = client->packet_id;
|
||||
|
||||
client->packet_id = (packet_id == PACKET_ID_MAX ? 1 : (packet_id + 1));
|
||||
return client->packet_id;
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ coap_event_type_t coap_glue_event_type_get(coap_message_t *message, coap_incoming_msg_type_t msg_type)
|
||||
{
|
||||
if (message->code_class == COAP_CODE_CLASS_SUCCESS &&
|
||||
message->code_detail == COAP_CODE_DETAIL_205_CONTENT) {
|
||||
if (msg_type == COAP_INCOMING_MSG_TYPE_RESP) {
|
||||
return COAP_EVENT_TYPE_RECEIVE_RESPCONTENT;
|
||||
} else {
|
||||
return COAP_EVENT_TYPE_RECEIVE_ACK;
|
||||
}
|
||||
} else if (message->code_class == COAP_CODE_CLASS_CLIENT_ERR &&
|
||||
message->code_detail == COAP_CODE_DETAIL_401_UNAUTHORIZED) {
|
||||
return COAP_EVENT_TYPE_UNAUTHORIZED;
|
||||
} else if (message->code_class == COAP_CODE_CLASS_CLIENT_ERR &&
|
||||
message->code_detail == COAP_CODE_DETAIL_403_FORBIDDEN) {
|
||||
return COAP_EVENT_TYPE_FORBIDDEN;
|
||||
} else {
|
||||
return COAP_EVENT_TYPE_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ void coap_glue_msg_sent_info_destroy(coap_msg_sent_info_t *msg_sent_info)
|
||||
{
|
||||
qcloud_list_del(&msg_sent_info->list);
|
||||
osal_free(msg_sent_info->message);
|
||||
osal_free(msg_sent_info);
|
||||
}
|
||||
|
||||
__QCLOUD_INTERNAL__ qcloud_err_t coap_glue_message_list_destroy(qcloud_coap_client_t *client)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
|
||||
qcloud_list_t *curr, *next;
|
||||
coap_msg_sent_info_t *msg_sent_info = NULL;
|
||||
|
||||
if (qcloud_list_empty(&client->message_list)) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
|
||||
}
|
||||
|
||||
osal_mutex_lock(client->message_list_lock);
|
||||
|
||||
QCLOUD_LIST_FOR_EACH_SAFE(curr, next, &client->message_list) {
|
||||
msg_sent_info = QCLOUD_LIST_ENTRY(curr, coap_msg_sent_info_t, list);
|
||||
|
||||
coap_glue_msg_sent_info_destroy(msg_sent_info);
|
||||
}
|
||||
|
||||
osal_mutex_unlock(client->message_list_lock);
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ qcloud_err_t coap_glue_message_list_scan(qcloud_coap_client_t *client)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
|
||||
int ret;
|
||||
size_t write_len;
|
||||
qcloud_list_t *curr, *next;
|
||||
coap_event_t event;
|
||||
coap_message_t message = {0};
|
||||
coap_msg_sent_info_t *msg_sent_info = NULL;
|
||||
|
||||
if (qcloud_list_empty(&client->message_list)) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
|
||||
}
|
||||
|
||||
osal_mutex_lock(client->message_list_lock);
|
||||
|
||||
QCLOUD_LIST_FOR_EACH_SAFE(curr, next, &client->message_list) {
|
||||
msg_sent_info = QCLOUD_LIST_ENTRY(curr, coap_msg_sent_info_t, list);
|
||||
|
||||
if (!osal_timer_is_expired(&msg_sent_info->timer)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (msg_sent_info->transmit_count < client->retransmit_max &&
|
||||
!msg_sent_info->is_acked) {
|
||||
osal_timer_init(&msg_sent_info->timer);
|
||||
osal_timer_countdown_ms(&msg_sent_info->timer, client->command_timeout);
|
||||
++msg_sent_info->transmit_count;
|
||||
|
||||
ret = client->network.write(&client->network, msg_sent_info->message, msg_sent_info->message_len,
|
||||
osal_timer_remain(&msg_sent_info->timer), &write_len);
|
||||
if (ret == QCLOUD_ERR_SUCCESS) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QCLOUD_LOG_E("retansmit the message id %d failed.", msg_sent_info->message_id);
|
||||
} else if (msg_sent_info->resp_cb) {
|
||||
message.type = COAP_MSG_TYPE_ACK;
|
||||
message.context = msg_sent_info->context;
|
||||
message.code_class = COAP_CODE_CLASS_INTERNAL_ERR;
|
||||
message.code_detail = COAP_CODE_DETAIL_600_TIMEOUT;
|
||||
message.id = msg_sent_info->message_id;
|
||||
msg_sent_info->resp_cb(&message, msg_sent_info->context);
|
||||
} else if (client->event_handler.handler) {
|
||||
if (msg_sent_info->is_acked) {
|
||||
event.type = COAP_EVENT_TYPE_SEPRESP_TIMEOUT;
|
||||
} else {
|
||||
event.type = COAP_EVENT_TYPE_ACK_TIMEOUT;
|
||||
}
|
||||
event.message = (void *)(&msg_sent_info->message_id);
|
||||
client->event_handler.handler(client->event_handler.context, &event);
|
||||
} else {
|
||||
QCLOUD_LOG_E("response and event callback both null");
|
||||
}
|
||||
|
||||
coap_glue_msg_sent_info_destroy(msg_sent_info);
|
||||
}
|
||||
|
||||
osal_mutex_unlock(client->message_list_lock);
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ qcloud_err_t coap_glue_message_list_unrecord(qcloud_coap_client_t *client, coap_message_t *message, coap_incoming_msg_type_t msg_type)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
|
||||
coap_event_t event;
|
||||
coap_event_type_t event_type;
|
||||
qcloud_list_t *curr, *next;
|
||||
coap_msg_sent_info_t *msg_sent_info = NULL;
|
||||
|
||||
if (qcloud_list_empty(&client->message_list)) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
|
||||
}
|
||||
|
||||
osal_mutex_lock(client->message_list_lock);
|
||||
|
||||
QCLOUD_LIST_FOR_EACH_SAFE(curr, next, &client->message_list) {
|
||||
msg_sent_info = QCLOUD_LIST_ENTRY(curr, coap_msg_sent_info_t, list);
|
||||
|
||||
if (msg_type == COAP_INCOMING_MSG_TYPE_ACK &&
|
||||
msg_sent_info->message_id == message->id) {
|
||||
msg_sent_info->is_acked = QCLOUD_TRUE;
|
||||
osal_timer_init(&msg_sent_info->timer);
|
||||
osal_timer_countdown_ms(&msg_sent_info->timer, client->command_timeout);
|
||||
}
|
||||
|
||||
if (msg_type == COAP_INCOMING_MSG_TYPE_RESP) {
|
||||
if (msg_sent_info->token_len != 0 &&
|
||||
msg_sent_info->token_len == message->token_len &&
|
||||
memcmp(msg_sent_info->token, message->token, message->token_len) == 0) {
|
||||
|
||||
message->context = msg_sent_info->context;
|
||||
|
||||
if (msg_sent_info->resp_cb) {
|
||||
msg_sent_info->resp_cb(message, msg_sent_info->context);
|
||||
} else if (client->event_handler.handler) { // event handle process
|
||||
event_type = coap_glue_event_type_get(message, COAP_INCOMING_MSG_TYPE_RESP);
|
||||
event.type = event_type;
|
||||
event.message = message;
|
||||
|
||||
message->id = msg_sent_info->message_id;
|
||||
client->event_handler.handler(client->event_handler.context, &event);
|
||||
} else {
|
||||
QCLOUD_LOG_E("response and event callback both null");
|
||||
}
|
||||
|
||||
coap_glue_msg_sent_info_destroy(msg_sent_info);
|
||||
}
|
||||
}
|
||||
|
||||
if (msg_type == COAP_INCOMING_MSG_TYPE_PIGGY) {
|
||||
if (msg_sent_info->message_id == message->id) {
|
||||
message->context = msg_sent_info->context;
|
||||
|
||||
if (msg_sent_info->resp_cb) {
|
||||
msg_sent_info->resp_cb(message, msg_sent_info->context);
|
||||
} else if (client->event_handler.handler) { // event handle process
|
||||
event_type = coap_glue_event_type_get(message, COAP_INCOMING_MSG_TYPE_PIGGY);
|
||||
event.type = event_type;
|
||||
event.message = (void *)(&message->id);
|
||||
client->event_handler.handler(client->event_handler.context, &event);
|
||||
} else {
|
||||
QCLOUD_LOG_E("response and event callback both null");
|
||||
}
|
||||
|
||||
coap_glue_msg_sent_info_destroy(msg_sent_info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
osal_mutex_unlock(client->message_list_lock);
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 向服务器发送确认消息
|
||||
*
|
||||
* @param[in,out] client 指向客户端结构的指针
|
||||
* @param[in] msg 指向消息结构的指针
|
||||
*
|
||||
* @returns Operation status
|
||||
* @retval 0 Success
|
||||
* @retval <0 Error
|
||||
*/
|
||||
__QCLOUD_STATIC__ qcloud_err_t coap_glue_resp_ack_send(qcloud_coap_client_t *client, uint16_t id)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY
|
||||
|
||||
qcloud_err_t rc;
|
||||
coap_message_t ack_msg = COAP_MESSAGE_INITIALIZER;
|
||||
|
||||
coap_message_init(&ack_msg);
|
||||
|
||||
rc = coap_message_type_set(&ack_msg, COAP_MSG_TYPE_ACK);
|
||||
if (rc != QCLOUD_ERR_SUCCESS) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = coap_message_id_set(&ack_msg, id);
|
||||
if (rc != QCLOUD_ERR_SUCCESS) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = coap_glue_msg_send(client, &ack_msg);
|
||||
|
||||
out:
|
||||
coap_message_destroy(&ack_msg);
|
||||
QCLOUD_FUNC_EXIT_RC(rc)
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ qcloud_err_t coap_glue_piggy_message_handle(qcloud_coap_client_t *client, coap_message_t *message)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(coap_glue_message_list_unrecord(client, message, COAP_INCOMING_MSG_TYPE_PIGGY));
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ qcloud_err_t coap_glue_ack_message_handle(qcloud_coap_client_t *client, coap_message_t *message)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(coap_glue_message_list_unrecord(client, message, COAP_INCOMING_MSG_TYPE_ACK));
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ qcloud_err_t coap_glue_resp_message_handle(qcloud_coap_client_t *client, coap_message_t *message)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY
|
||||
|
||||
qcloud_err_t rc;
|
||||
|
||||
if (message->type == COAP_MSG_TYPE_CON) {
|
||||
rc = coap_glue_resp_ack_send(client, message->id);
|
||||
QCLOUD_LOG_D("send ack message for id: %d, rc: %d", message->id, rc);
|
||||
}
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(coap_glue_message_list_unrecord(client, message, COAP_INCOMING_MSG_TYPE_RESP));
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ qcloud_err_t coap_glue_incoming_message_handle(qcloud_coap_client_t *client, uint8_t *buf, size_t buf_len)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY
|
||||
|
||||
qcloud_err_t rc = QCLOUD_ERR_SUCCESS;
|
||||
coap_message_t incoming_msg;
|
||||
|
||||
memset(&incoming_msg, 0x00, sizeof(coap_message_t));
|
||||
|
||||
rc = coap_message_deserialize(&incoming_msg, (char *)buf, buf_len);
|
||||
if (rc != QCLOUD_ERR_SUCCESS) {
|
||||
QCLOUD_LOG_E("deserialize msg failed: %d", rc);
|
||||
QCLOUD_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
if (incoming_msg.type == COAP_MSG_TYPE_ACK && COAP_MSG_IS_EMPTY_ACK(&incoming_msg)) { // empty ACK
|
||||
QCLOUD_LOG_D("receive ACK msg, id %d", incoming_msg.id);
|
||||
coap_glue_ack_message_handle(client, &incoming_msg);
|
||||
} else if (incoming_msg.type == COAP_MSG_TYPE_ACK && !COAP_MSG_IS_EMPTY(&incoming_msg)) { // piggy Response
|
||||
QCLOUD_LOG_D("receive piggy ACK msg, id %d", incoming_msg.id);
|
||||
coap_glue_piggy_message_handle(client, &incoming_msg);
|
||||
} else if (incoming_msg.type == COAP_MSG_TYPE_CON && COAP_MSG_IS_EMPTY_RSP(&incoming_msg)) { // payload Response
|
||||
QCLOUD_LOG_D("receive response msg, id: %d", incoming_msg.id);
|
||||
coap_glue_resp_message_handle(client, &incoming_msg);
|
||||
} else if (incoming_msg.type == COAP_MSG_TYPE_NON && COAP_MSG_IS_EMPTY_RSP(&incoming_msg)) { // payload Response
|
||||
QCLOUD_LOG_D("receive response msg, id: %d", incoming_msg.id);
|
||||
coap_glue_resp_message_handle(client, &incoming_msg);
|
||||
} else {
|
||||
QCLOUD_LOG_E("msg type not recgonized");
|
||||
}
|
||||
|
||||
if (incoming_msg.payload) {
|
||||
osal_free(incoming_msg.payload);
|
||||
incoming_msg.payload_len = 0;
|
||||
}
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ coap_msg_sent_info_t *coap_glue_message_sent_info_construct(qcloud_coap_client_t *client, coap_message_t *message, int len)
|
||||
{
|
||||
coap_msg_sent_info_t *msg_sent_info = NULL;
|
||||
|
||||
msg_sent_info = (coap_msg_sent_info_t *)osal_malloc(sizeof(coap_msg_sent_info_t));
|
||||
if (!msg_sent_info) {
|
||||
QCLOUD_LOG_E("memory alloc failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
msg_sent_info->message = osal_malloc(len);
|
||||
if (!msg_sent_info->message) {
|
||||
QCLOUD_LOG_E("memory alloc failed");
|
||||
osal_free(msg_sent_info);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
msg_sent_info->is_acked = QCLOUD_FALSE;
|
||||
msg_sent_info->context = message->context;
|
||||
msg_sent_info->message_id = message->id;
|
||||
msg_sent_info->resp_cb = message->resp_cb;
|
||||
msg_sent_info->message_len = len;
|
||||
msg_sent_info->token_len = message->token_len;
|
||||
|
||||
memcpy(msg_sent_info->token, message->token, message->token_len);
|
||||
memcpy(msg_sent_info->message, client->tx_buffer, len);
|
||||
|
||||
if (message->type == COAP_MSG_TYPE_CON) {
|
||||
msg_sent_info->transmit_count = 0;
|
||||
osal_timer_init(&msg_sent_info->timer);
|
||||
osal_timer_countdown_ms(&msg_sent_info->timer, client->command_timeout);
|
||||
}
|
||||
|
||||
return msg_sent_info;
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ qcloud_err_t coap_glue_message_list_record(qcloud_coap_client_t *client, coap_message_t *message, int len)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY
|
||||
|
||||
coap_msg_sent_info_t *msg_sent_info = NULL;
|
||||
|
||||
msg_sent_info = coap_glue_message_sent_info_construct(client, message, len);
|
||||
if (!msg_sent_info) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS)
|
||||
}
|
||||
|
||||
osal_mutex_lock(client->message_list_lock);
|
||||
qcloud_list_add(&msg_sent_info->list, &client->message_list);
|
||||
osal_mutex_unlock(client->message_list_lock);
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS)
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ qcloud_err_t coap_glue_recv(qcloud_coap_client_t *client, uint32_t timeout_ms)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY
|
||||
|
||||
size_t read_len = 0;
|
||||
qcloud_err_t rc;
|
||||
|
||||
rc = client->network.read(&client->network, client->rx_buffer, sizeof(client->rx_buffer), timeout_ms, &read_len);
|
||||
|
||||
switch (rc) {
|
||||
case QCLOUD_ERR_SUCCESS:
|
||||
rc = coap_glue_incoming_message_handle(client, client->rx_buffer, read_len);
|
||||
break;
|
||||
|
||||
case QCLOUD_ERR_SSL_READ_TIMEOUT:
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(rc)
|
||||
}
|
||||
|
||||
__QCLOUD_INTERNAL__ qcloud_err_t coap_glue_spin(qcloud_coap_client_t *client, uint32_t timeout_ms)
|
||||
{
|
||||
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
|
||||
qcloud_err_t rc;
|
||||
|
||||
rc = coap_glue_recv(client, timeout_ms);
|
||||
if (rc != QCLOUD_ERR_SUCCESS && rc != QCLOUD_ERR_SSL_READ_TIMEOUT) {
|
||||
QCLOUD_FUNC_EXIT_RC(rc)
|
||||
}
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(coap_glue_message_list_scan(client));
|
||||
}
|
||||
|
||||
__QCLOUD_INTERNAL__ qcloud_err_t coap_glue_msg_send(qcloud_coap_client_t *client, coap_message_t *message)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY
|
||||
|
||||
int len = 0;
|
||||
qcloud_err_t rc;
|
||||
size_t write_len = 0;
|
||||
|
||||
osal_mutex_lock(client->tx_lock);
|
||||
|
||||
len = coap_message_serialize(message, (char*)client->tx_buffer, sizeof(client->tx_buffer));
|
||||
if (len < 0) {
|
||||
QCLOUD_LOG_E("failed to serialize coap message");
|
||||
osal_mutex_unlock(client->tx_lock);
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE)
|
||||
}
|
||||
|
||||
rc = client->network.write(&client->network, client->tx_buffer, len, 0, &write_len);
|
||||
if (rc != QCLOUD_ERR_SUCCESS) {
|
||||
QCLOUD_LOG_E("coap net fail to write rc: %d", rc);
|
||||
osal_mutex_unlock(client->tx_lock);
|
||||
QCLOUD_FUNC_EXIT_RC(rc)
|
||||
}
|
||||
|
||||
if (message->type == COAP_MSG_TYPE_CON &&
|
||||
message->code_class == COAP_CODE_CLASS_REQ) {
|
||||
rc = coap_glue_message_list_record(client, message, write_len);
|
||||
QCLOUD_LOG_I("add message id: %d, rc: %d", message->id, rc);
|
||||
} else {
|
||||
QCLOUD_LOG_I("message donot need retransmit");
|
||||
}
|
||||
|
||||
osal_mutex_unlock(client->tx_lock);
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(rc)
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@@ -0,0 +1,292 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright (C) 2016 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 "qcloud.h"
|
||||
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
__QCLOUD_STATIC__ int coap_message_serialize_header(coap_message_t *message, char *buf, size_t len)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY
|
||||
|
||||
if (len < 4) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
buf[0] = (char)((COAP_VERSION << 6)
|
||||
| ((message->type & 0x03) << 4)
|
||||
| (message->token_len & 0x0f));
|
||||
buf[1] = (char)(((message->code_class & 0x07) << 5)
|
||||
| (message->code_detail & 0x1f));
|
||||
|
||||
buf[2] = (message->id & 0xFF00) >> 8;
|
||||
buf[3] = (message->id & 0x00FF);
|
||||
|
||||
QCLOUD_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
|
||||
*/
|
||||
__QCLOUD_STATIC__ int coap_message_serialize_token(coap_message_t *message, char *buf, size_t len)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY
|
||||
|
||||
if (len < message->token_len) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(buf, message->token, message->token_len);
|
||||
|
||||
return 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
|
||||
*/
|
||||
__QCLOUD_STATIC__ int coap_message_option_format(coap_msg_option_t *option, uint16_t prev_option_code, char *buf, size_t buf_len)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY
|
||||
|
||||
char *p = NULL;
|
||||
uint32_t len = 0;
|
||||
uint16_t option_code_delta = 0;
|
||||
|
||||
p = buf;
|
||||
|
||||
option_code_delta = option->option_code - prev_option_code;
|
||||
len++;
|
||||
|
||||
/* option delta */
|
||||
if (option_code_delta >= 269) {
|
||||
len += 2;
|
||||
} else if (option_code_delta >= 13) {
|
||||
len += 1;
|
||||
}
|
||||
|
||||
/* option length */
|
||||
if (option->val_len >= 269) {
|
||||
len += 2;
|
||||
} else if (option->option_code >= 13) {
|
||||
len += 1;
|
||||
}
|
||||
|
||||
/* option value */
|
||||
len += option->val_len;
|
||||
if (len > buf_len) {
|
||||
return QCLOUD_ERR_COAP_DATA_SIZE;
|
||||
}
|
||||
|
||||
/* option delta */
|
||||
if (option_code_delta >= 269) {
|
||||
p[0] = 14 << 4;
|
||||
} else if (option_code_delta >= 13) {
|
||||
p[0] = 13 << 4;
|
||||
} else {
|
||||
p[0] = option_code_delta << 4;
|
||||
}
|
||||
|
||||
/* option length */
|
||||
if (option->val_len >= 269) {
|
||||
p[0] |= 14;
|
||||
} else if (option->val_len >= 13) {
|
||||
p[0] |= 13;
|
||||
} else {
|
||||
p[0] |= option->val_len;
|
||||
}
|
||||
|
||||
p++;
|
||||
buf_len--;
|
||||
|
||||
/* option delta extended */
|
||||
if (option_code_delta >= 269) {
|
||||
*p = (uint8_t)(((option_code_delta - 269) & 0xFF00) >> 8);
|
||||
*(p + 1) = (uint8_t)(((option_code_delta - 269) & 0x00FF));
|
||||
p += 2;
|
||||
buf_len -= 2;
|
||||
} else if (option_code_delta >= 13) {
|
||||
p[0] = option_code_delta - 13;
|
||||
p++;
|
||||
buf_len--;
|
||||
}
|
||||
|
||||
/* option length extended */
|
||||
if (option->val_len >= 269) {
|
||||
*p = (unsigned char)(((option->val_len - 269) & 0xFF00) >> 8);
|
||||
*(p + 1) = (unsigned char)(((option->val_len - 269) & 0x00FF));
|
||||
p += 2;
|
||||
buf_len -= 2;
|
||||
} else if (option->val_len >= 13) {
|
||||
p[0] = option->val_len - 13;
|
||||
p++;
|
||||
buf_len--;
|
||||
}
|
||||
|
||||
/* option value */
|
||||
memcpy(p, option->val, option->val_len);
|
||||
p += option->val_len;
|
||||
|
||||
return 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
|
||||
*/
|
||||
__QCLOUD_STATIC__ int coap_message_serialize_options(coap_message_t *message, char *buf, size_t buf_len)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
int len;
|
||||
char *p = NULL;
|
||||
uint16_t prev_option_code = 0;
|
||||
qcloud_list_t *curr;
|
||||
coap_msg_option_t *option;
|
||||
|
||||
p = buf;
|
||||
|
||||
QCLOUD_LIST_FOR_EACH(curr, &message->option_list) {
|
||||
option = QCLOUD_LIST_ENTRY(curr, coap_msg_option_t, list);
|
||||
|
||||
// coap_message_option_destroy(option);
|
||||
len = coap_message_option_format(option, prev_option_code, p, buf_len);
|
||||
if (len < 0) {
|
||||
return len;
|
||||
}
|
||||
|
||||
p += len;
|
||||
buf_len -= len;
|
||||
prev_option_code = option->option_code;
|
||||
}
|
||||
|
||||
return 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
|
||||
*/
|
||||
__QCLOUD_STATIC__ int coap_message_serialize_payload(coap_message_t *message, char *buf, size_t buf_len)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
if (message->payload_len == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (message->payload_len + 1 > buf_len) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
buf[0] = 0xff;
|
||||
memcpy(&buf[1], message->payload, message->payload_len);
|
||||
|
||||
return message->payload_len + 1;
|
||||
}
|
||||
|
||||
__QCLOUD_INTERNAL__ int coap_message_serialize(coap_message_t *message, char *buf, size_t buf_len)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
int len = 0;
|
||||
char *p = buf;
|
||||
|
||||
if (!message || !buf) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
len = coap_message_serialize_header(message, p, buf_len);
|
||||
if (len < 0) {
|
||||
QCLOUD_LOG_E("serialize header fail len: %lu", len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
p += len;
|
||||
buf_len -= len;
|
||||
len = coap_message_serialize_token(message, p, buf_len);
|
||||
if (len < 0) {
|
||||
QCLOUD_LOG_E("erialize token fail len: %lu", len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
p += len;
|
||||
buf_len -= len;
|
||||
len = coap_message_serialize_options(message, p, buf_len);
|
||||
if (len < 0) {
|
||||
QCLOUD_LOG_E("serialize options fail len: %lu", len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
p += len;
|
||||
buf_len -= len;
|
||||
len = coap_message_serialize_payload(message, p, buf_len);
|
||||
if (len < 0) {
|
||||
QCLOUD_LOG_E("coap_message_serialize_payload fail num=%lu", len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
p += len;
|
||||
|
||||
return p - buf;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@@ -0,0 +1,553 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright (C) 2016 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 "qcloud.h"
|
||||
|
||||
__QCLOUD_STATIC__ void event_reply_destroy(qcloud_event_reply_t *reply)
|
||||
{
|
||||
qcloud_list_del(&reply->list);
|
||||
osal_free(reply);
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ void event_reply_list_destroy(qcloud_event_client_t *client)
|
||||
{
|
||||
qcloud_list_t *curr, *next;
|
||||
qcloud_event_reply_t *reply;
|
||||
|
||||
if (qcloud_list_empty(&client->reply_list)) {
|
||||
return;
|
||||
}
|
||||
|
||||
osal_mutex_lock(client->reply_list_lock);
|
||||
|
||||
QCLOUD_LIST_FOR_EACH_SAFE(curr, next, &client->reply_list) {
|
||||
reply = QCLOUD_LIST_ENTRY(curr, qcloud_event_reply_t, list);
|
||||
event_reply_destroy(reply);
|
||||
}
|
||||
|
||||
osal_mutex_unlock(client->reply_list_lock);
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ void event_reply_handler(void *client, mqtt_incoming_msg_t *message, void *private_data)
|
||||
{
|
||||
QCLOUD_POINTER_SANITY_CHECK_RTN(message);
|
||||
QCLOUD_POINTER_SANITY_CHECK_RTN(private_data);
|
||||
|
||||
int32_t return_code;
|
||||
char *client_token = NULL, *status = NULL;
|
||||
qcloud_list_t *curr, *next;
|
||||
qcloud_event_client_t *event_client;
|
||||
qcloud_event_reply_t *reply;
|
||||
|
||||
event_client = (qcloud_event_client_t *)private_data;
|
||||
|
||||
QCLOUD_LOG_I("msg recved, topic: %.*s, payload: %.*s", message->topic_len, message->topic, message->payload_len, (char *) message->payload);
|
||||
|
||||
// 解析事件回复中的clientToken
|
||||
if (!shadow_json_client_token_parse((char *)message->payload, &client_token)) {
|
||||
QCLOUD_LOG_E("client token parse fail!");
|
||||
return;
|
||||
}
|
||||
|
||||
// 解析事件回复中的处理结果
|
||||
if (!event_json_return_code_parse((char *)message->payload, &return_code)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!event_json_status_parse((char *)message->payload, &status)) {
|
||||
QCLOUD_LOG_D("no status return");
|
||||
}
|
||||
|
||||
QCLOUD_LOG_D("event token:%s code:%d status:%s", client_token, return_code, status);
|
||||
|
||||
if (qcloud_list_empty(&event_client->reply_list)) {
|
||||
return;
|
||||
}
|
||||
|
||||
osal_mutex_lock(event_client->reply_list_lock);
|
||||
|
||||
QCLOUD_LIST_FOR_EACH_SAFE(curr, next, &event_client->reply_list) {
|
||||
reply = QCLOUD_LIST_ENTRY(curr, qcloud_event_reply_t, list);
|
||||
|
||||
if (osal_timer_is_expired(&reply->timer)) {
|
||||
QCLOUD_LOG_E("event[%s] timeout", reply->client_token);
|
||||
event_reply_destroy(reply);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strcmp(client_token, reply->client_token) == 0) { // client_token matches
|
||||
if (reply->handler) {
|
||||
reply->handler(client, message);
|
||||
}
|
||||
event_reply_destroy(reply);
|
||||
}
|
||||
}
|
||||
|
||||
osal_mutex_unlock(event_client->reply_list_lock);
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ qcloud_event_reply_t *event_reply_construct(qcloud_event_client_t *client,
|
||||
uint32_t timeout_ms,
|
||||
event_reply_handler_fn_t handler)
|
||||
{
|
||||
qcloud_err_t rc;
|
||||
int rc_snprintf;
|
||||
qcloud_event_reply_t *event_reply;
|
||||
qcloud_shadow_client_t *shadow_client;
|
||||
|
||||
shadow_client = client->shadow_client;
|
||||
|
||||
event_reply = (qcloud_event_reply_t *)osal_malloc(sizeof(qcloud_event_reply_t));
|
||||
if (!event_reply) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
event_reply->handler = handler;
|
||||
|
||||
osal_timer_init(&event_reply->timer);
|
||||
osal_timer_countdown_ms(&event_reply->timer, timeout_ms);
|
||||
|
||||
memset(event_reply->client_token, 0, sizeof(event_reply->client_token));
|
||||
|
||||
++shadow_client->token_num;
|
||||
|
||||
rc_snprintf = osal_snprintf(event_reply->client_token, QCLOUD_EVENT_TOKEN_MAX, "%s-%u",
|
||||
shadow_client->device_product_id, shadow_client->token_num);
|
||||
rc = shadow_json_snprintf_rc2errno(rc_snprintf, QCLOUD_EVENT_TOKEN_MAX);
|
||||
if (rc != QCLOUD_ERR_SUCCESS) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
qcloud_list_init(&event_reply->list);
|
||||
|
||||
qcloud_list_add(&event_reply->list, &client->reply_list);
|
||||
|
||||
return event_reply;
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ qcloud_err_t event_json_init(qcloud_event_client_t *client,
|
||||
char *json_doc,
|
||||
size_t json_doc_size,
|
||||
int event_count,
|
||||
event_reply_handler_fn_t handler,
|
||||
uint32_t reply_timeout_ms)
|
||||
{
|
||||
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(json_doc, QCLOUD_ERR_INVAL);
|
||||
|
||||
int rc_snprintf = 0;
|
||||
qcloud_event_reply_t *event_reply = NULL;
|
||||
|
||||
event_reply = event_reply_construct(client, reply_timeout_ms, handler);
|
||||
QCLOUD_FUNC_EXIT_RC_IF(event_reply, NULL, QCLOUD_ERR_INVAL);
|
||||
|
||||
memset(json_doc, 0, json_doc_size);
|
||||
|
||||
if (event_count > 1) {
|
||||
rc_snprintf = osal_snprintf(json_doc, json_doc_size, "{\"method\":\"%s\", \"clientToken\":\"%s\", ", \
|
||||
EVENT_FIELD_POSTS, event_reply->client_token);
|
||||
} else {
|
||||
rc_snprintf = osal_snprintf(json_doc, json_doc_size, "{\"method\":\"%s\", \"clientToken\":\"%s\", ", \
|
||||
EVENT_FIELD_POST, event_reply->client_token);
|
||||
}
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(shadow_json_snprintf_rc2errno(rc_snprintf, json_doc_size));
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ qcloud_err_t event_multi_json_construct(char *json_doc,
|
||||
size_t json_doc_size,
|
||||
size_t remain_size,
|
||||
int event_count,
|
||||
qcloud_event_t *events[])
|
||||
{
|
||||
int i = 0, j = 0;
|
||||
qcloud_err_t rc;
|
||||
int rc_snprintf;
|
||||
qcloud_event_t *event;
|
||||
shadow_dev_property_t *dev_property;
|
||||
|
||||
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size, "\"events\":[");
|
||||
rc = shadow_json_snprintf_rc2errno(rc_snprintf, remain_size);
|
||||
QCLOUD_FUNC_EXIT_RC_IF_NOT(rc, QCLOUD_ERR_SUCCESS, rc);
|
||||
|
||||
if ((remain_size = json_doc_size - strlen(json_doc)) <= 1) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_JSON_BUFFER_TOO_SHORT);
|
||||
}
|
||||
|
||||
for (i = 0; i < event_count; ++i) {
|
||||
event = events[i];
|
||||
if (!event) {
|
||||
QCLOUD_LOG_E("%dth/%d null event", i, event_count);
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_INVAL);
|
||||
}
|
||||
|
||||
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size,
|
||||
"{\"eventId\":\"%s\", \"type\":\"%s\", \"timestamp\":%u000, \"params\":{",\
|
||||
event->event_name, event->type, event->timestamp);
|
||||
rc = shadow_json_snprintf_rc2errno(rc_snprintf, remain_size);
|
||||
QCLOUD_FUNC_EXIT_RC_IF_NOT(rc, QCLOUD_ERR_SUCCESS, rc);
|
||||
|
||||
if ((remain_size = json_doc_size - strlen(json_doc)) <= 1) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_JSON_BUFFER_TOO_SHORT);
|
||||
}
|
||||
|
||||
for (j = 0; j < event->event_payload_count; ++j) {
|
||||
dev_property = &event->event_payload[i];
|
||||
if (!dev_property || !dev_property->key) {
|
||||
QCLOUD_LOG_E("%dth/%d null event property data", i, event->event_payload_count);
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_INVAL);
|
||||
}
|
||||
|
||||
rc = event_json_node_add(json_doc, remain_size, dev_property->key, dev_property->data, dev_property->type);
|
||||
QCLOUD_FUNC_EXIT_RC_IF_NOT(rc, QCLOUD_ERR_SUCCESS, rc);
|
||||
}
|
||||
|
||||
if ((remain_size = json_doc_size - strlen(json_doc)) <= 1) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_JSON_BUFFER_TOO_SHORT);
|
||||
}
|
||||
|
||||
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc)-1, remain_size, "}}," );
|
||||
rc = shadow_json_snprintf_rc2errno(rc_snprintf, remain_size);
|
||||
QCLOUD_FUNC_EXIT_RC_IF_NOT(rc, QCLOUD_ERR_SUCCESS, rc);
|
||||
}
|
||||
|
||||
if ((remain_size = json_doc_size - strlen(json_doc)) <= 1) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_JSON_BUFFER_TOO_SHORT);
|
||||
}
|
||||
|
||||
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc) - 1, remain_size, "]");
|
||||
QCLOUD_FUNC_EXIT_RC(shadow_json_snprintf_rc2errno(rc_snprintf, remain_size));
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ qcloud_err_t event_single_json_construct(char *json_doc,
|
||||
size_t json_doc_size,
|
||||
size_t remain_size,
|
||||
int event_count,
|
||||
qcloud_event_t *events[])
|
||||
{
|
||||
int i = 0;
|
||||
qcloud_err_t rc;
|
||||
int rc_snprintf;
|
||||
qcloud_event_t *event;
|
||||
shadow_dev_property_t *dev_property;
|
||||
|
||||
event = events[0];
|
||||
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size,
|
||||
"\"eventId\":\"%s\", \"type\":\"%s\", \"timestamp\":%u000, \"params\":{",\
|
||||
event->event_name, event->type, event->timestamp);
|
||||
|
||||
rc = shadow_json_snprintf_rc2errno(rc_snprintf, remain_size);
|
||||
QCLOUD_FUNC_EXIT_RC_IF_NOT(rc, QCLOUD_ERR_SUCCESS, rc);
|
||||
|
||||
if ((remain_size = json_doc_size - strlen(json_doc)) <= 1) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_JSON_BUFFER_TOO_SHORT);
|
||||
}
|
||||
|
||||
for (i = 0; i < event->event_payload_count; ++i) {
|
||||
dev_property = &event->event_payload[i];
|
||||
if (!dev_property || !dev_property->key) {
|
||||
QCLOUD_LOG_E("%dth/%d null event property data", i, event->event_payload_count);
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_INVAL);
|
||||
}
|
||||
|
||||
rc = event_json_node_add(json_doc, remain_size, dev_property->key, dev_property->data, dev_property->type);
|
||||
QCLOUD_FUNC_EXIT_RC_IF_NOT(rc, QCLOUD_ERR_SUCCESS, rc);
|
||||
}
|
||||
|
||||
if ((remain_size = json_doc_size - strlen(json_doc)) <= 1) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_JSON_BUFFER_TOO_SHORT);
|
||||
}
|
||||
|
||||
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc) - 1, remain_size, "}" );
|
||||
QCLOUD_FUNC_EXIT_RC(shadow_json_snprintf_rc2errno(rc_snprintf, remain_size));
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ qcloud_err_t event_json_construct(qcloud_event_client_t *client,
|
||||
char *json_doc,
|
||||
size_t json_doc_size,
|
||||
int event_count,
|
||||
qcloud_event_t *events[],
|
||||
event_reply_handler_fn_t handler,
|
||||
uint32_t reply_timeout_ms)
|
||||
{
|
||||
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(json_doc, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(events, QCLOUD_ERR_INVAL);
|
||||
|
||||
qcloud_err_t rc;
|
||||
int rc_snprintf = 0;
|
||||
size_t remain_size = 0;
|
||||
|
||||
rc = event_json_init(client, json_doc, json_doc_size, event_count, handler, reply_timeout_ms);
|
||||
if (rc != QCLOUD_ERR_SUCCESS) {
|
||||
QCLOUD_LOG_E("event json init failed: %d", rc);
|
||||
QCLOUD_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
if ((remain_size = json_doc_size - strlen(json_doc)) <= 1) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_JSON_BUFFER_TOO_SHORT);
|
||||
}
|
||||
|
||||
if (event_count > 1) { //多个事件
|
||||
event_multi_json_construct(json_doc, json_doc_size, remain_size, event_count, events);
|
||||
} else { //单个事件
|
||||
event_single_json_construct(json_doc, json_doc_size, remain_size, event_count, events);
|
||||
}
|
||||
|
||||
// finish json
|
||||
if ((remain_size = json_doc_size - strlen(json_doc)) < 1) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_JSON_BUFFER_TOO_SHORT);
|
||||
}
|
||||
|
||||
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size, "}");
|
||||
QCLOUD_FUNC_EXIT_RC(shadow_json_snprintf_rc2errno(rc_snprintf, remain_size));
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ qcloud_err_t event_publish(qcloud_event_client_t *client, char *json_doc)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
mqtt_publish_opt_t publish_opt;
|
||||
qcloud_shadow_client_t *shadow_client = NULL;
|
||||
|
||||
shadow_client = client->shadow_client;
|
||||
|
||||
memset(&publish_opt, 0, sizeof(mqtt_publish_opt_t));
|
||||
publish_opt.qos = MQTT_QOS0;
|
||||
publish_opt.payload = json_doc;
|
||||
publish_opt.payload_len = strlen(json_doc);
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(qcloud_mqtt_client_publish(&shadow_client->mqtt_client, client->up_topic, &publish_opt));
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ qcloud_err_t event_subscribe(qcloud_event_client_t *client)
|
||||
{
|
||||
mqtt_subscribe_opt_t subscribe_opt;
|
||||
qcloud_shadow_client_t *shadow_client = NULL;
|
||||
|
||||
shadow_client = client->shadow_client;
|
||||
|
||||
subscribe_opt.qos = MQTT_QOS0;
|
||||
subscribe_opt.private_data = (void *)client;
|
||||
subscribe_opt.message_handler = event_reply_handler;
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(qcloud_mqtt_client_subscribe(&shadow_client->mqtt_client, client->down_topic, &subscribe_opt));
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ qcloud_err_t event_unsubscribe(qcloud_event_client_t *client)
|
||||
{
|
||||
qcloud_shadow_client_t *shadow_client = NULL;
|
||||
|
||||
shadow_client = client->shadow_client;
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(qcloud_mqtt_client_unsubscribe(&shadow_client->mqtt_client, client->down_topic));
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ qcloud_err_t event_client_construct(qcloud_event_client_t *client,
|
||||
qcloud_shadow_client_t *shadow_client,
|
||||
qcloud_device_t *device)
|
||||
{
|
||||
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(shadow_client, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(device, QCLOUD_ERR_INVAL);
|
||||
|
||||
int size;
|
||||
|
||||
size = osal_snprintf(client->down_topic, QCLOUD_MQTT_TOPIC_SIZE_MAX, "$thing/down/event/%s/%s", device->product_id, device->device_name);
|
||||
if (size < 0 || size > QCLOUD_MQTT_TOPIC_SIZE_MAX - 1) {
|
||||
QCLOUD_LOG_E("topic size overflow, %d", size);
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
|
||||
}
|
||||
|
||||
size = osal_snprintf(client->up_topic, QCLOUD_MQTT_TOPIC_SIZE_MAX, "$thing/up/event/%s/%s", device->product_id, device->device_name);
|
||||
if (size < 0 || size > QCLOUD_MQTT_TOPIC_SIZE_MAX - 1) {
|
||||
QCLOUD_LOG_E("topic size overflow, %d", size);
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
|
||||
}
|
||||
|
||||
if ((client->reply_list_lock = osal_mutex_create()) == NULL) {
|
||||
QCLOUD_LOG_E("write buf lock failed.");
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
|
||||
}
|
||||
|
||||
qcloud_list_init(&client->reply_list);
|
||||
|
||||
client->shadow_client = shadow_client;
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ void event_incoming_msg_handler(void *client, void *context, mqtt_event_t *event)
|
||||
{
|
||||
uint16_t packet_id;
|
||||
qcloud_event_client_t *event_client = NULL;
|
||||
|
||||
event_client = (qcloud_event_client_t *)context;
|
||||
|
||||
switch (event->type) {
|
||||
case MQTT_EVENT_SUBCRIBE_SUCCESS:
|
||||
packet_id = *(uint16_t *)event->message;
|
||||
QCLOUD_LOG_D("subscribe success, packet id=%u", (uint32_t)packet_id);
|
||||
event_client->sync_state = QCLOUD_EVENT_SYNC_STATE_SUCCESS;
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_SUBCRIBE_TIMEOUT:
|
||||
packet_id = *(uint16_t *)event->message;
|
||||
QCLOUD_LOG_D("subscribe wait ack timeout, packet id=%u", (uint32_t)packet_id);
|
||||
event_client->sync_state = QCLOUD_EVENT_SYNC_STATE_TIMEOUT;
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_SUBCRIBE_NACK:
|
||||
packet_id = *(uint16_t *)event->message;
|
||||
QCLOUD_LOG_D("subscribe nack, packet id=%u", (uint32_t)packet_id);
|
||||
event_client->sync_state = QCLOUD_EVENT_SYNC_STATE_NACK;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
__QCLOUD_API__ qcloud_err_t qcloud_event_client_create(qcloud_event_client_t *client,
|
||||
qcloud_shadow_client_t *shadow_client,
|
||||
qcloud_device_t *device)
|
||||
{
|
||||
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(shadow_client, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(device, QCLOUD_ERR_INVAL);
|
||||
|
||||
qcloud_err_t rc;
|
||||
void *shadow_context = NULL;
|
||||
mqtt_event_handler_fn_t shadow_handler = NULL;
|
||||
|
||||
if (!qcloud_shadow_client_is_connected(shadow_client)) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
|
||||
}
|
||||
|
||||
rc = event_client_construct(client, shadow_client, device);
|
||||
QCLOUD_FUNC_EXIT_RC_IF_NOT(rc, QCLOUD_ERR_SUCCESS, rc);
|
||||
|
||||
rc = event_subscribe(client);
|
||||
QCLOUD_FUNC_EXIT_RC_IF_NOT(rc, QCLOUD_ERR_SUCCESS, rc);
|
||||
|
||||
shadow_context = shadow_client->event_handler.context;
|
||||
shadow_handler = shadow_client->event_handler.handler;
|
||||
|
||||
shadow_client->event_handler.context = (void *)client;;
|
||||
shadow_client->event_handler.handler = event_incoming_msg_handler;
|
||||
|
||||
client->sync_state = QCLOUD_EVENT_SYNC_STATE_PENDACK;
|
||||
|
||||
while (client->sync_state == QCLOUD_EVENT_SYNC_STATE_PENDACK) {
|
||||
qcloud_shadow_client_yield(client->shadow_client, 100);
|
||||
}
|
||||
|
||||
shadow_client->event_handler.context = shadow_context;
|
||||
shadow_client->event_handler.handler = shadow_handler;
|
||||
|
||||
if (client->sync_state != QCLOUD_SHADOW_SYNC_STATE_SUCCESS) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
|
||||
}
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
|
||||
}
|
||||
|
||||
__QCLOUD_API__ qcloud_err_t qcloud_event_client_destroy(qcloud_event_client_t *client)
|
||||
{
|
||||
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
|
||||
event_unsubscribe(client);
|
||||
|
||||
event_reply_list_destroy(client);
|
||||
|
||||
if (client->reply_list_lock) {
|
||||
osal_mutex_destroy(client->reply_list_lock);
|
||||
}
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS)
|
||||
}
|
||||
|
||||
__QCLOUD_API__ qcloud_err_t qcloud_event_client_post(qcloud_event_client_t *client,
|
||||
char *json_doc,
|
||||
size_t json_doc_size,
|
||||
int event_count,
|
||||
qcloud_event_t *events[],
|
||||
event_reply_handler_fn_t handler)
|
||||
{
|
||||
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(json_doc, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(events, QCLOUD_ERR_INVAL);
|
||||
|
||||
qcloud_err_t rc;
|
||||
|
||||
rc = event_json_construct(client, json_doc, json_doc_size, event_count, events, handler, 5000);
|
||||
if (rc != QCLOUD_ERR_SUCCESS) {
|
||||
QCLOUD_LOG_E("construct event json fail, %d", rc);
|
||||
QCLOUD_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
rc = event_publish(client, json_doc);
|
||||
if (rc != QCLOUD_ERR_SUCCESS) {
|
||||
QCLOUD_LOG_E("publish event to cloud fail, %d",rc);
|
||||
}
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
__QCLOUD_API__ qcloud_err_t qcloud_event_client_post_raw(qcloud_event_client_t *client,
|
||||
char *json_doc,
|
||||
size_t json_doc_size,
|
||||
char *event_msg,
|
||||
event_reply_handler_fn_t handler)
|
||||
{
|
||||
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(json_doc, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(event_msg, QCLOUD_ERR_INVAL);
|
||||
|
||||
qcloud_err_t rc;
|
||||
int rc_snprintf;
|
||||
size_t remain_size = 0;
|
||||
|
||||
rc = event_json_init(client, json_doc, json_doc_size, 2, handler, 5000);
|
||||
if (rc != QCLOUD_ERR_SUCCESS) {
|
||||
QCLOUD_LOG_E("event json init failed: %d", rc);
|
||||
QCLOUD_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
if ((remain_size = json_doc_size - strlen(json_doc)) <= 1) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_JSON_BUFFER_TOO_SHORT);
|
||||
}
|
||||
|
||||
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size, "\"events\":[%s]}", event_msg);
|
||||
rc = shadow_json_snprintf_rc2errno(rc_snprintf, remain_size);
|
||||
QCLOUD_FUNC_EXIT_RC_IF_NOT(rc, QCLOUD_ERR_SUCCESS, rc);
|
||||
|
||||
QCLOUD_LOG_D("json doc: %s", json_doc);
|
||||
|
||||
rc = event_publish(client, json_doc);
|
||||
if (rc != QCLOUD_ERR_SUCCESS) {
|
||||
QCLOUD_LOG_E("publish event raw fail, %d",rc);
|
||||
}
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright (C) 2016 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 "qcloud.h"
|
||||
|
||||
#define PRIi32 "i"
|
||||
#define PRIi16 "i"
|
||||
#define PRIi8 "i"
|
||||
#define PRIu32 "u"
|
||||
#define PRIu16 "u"
|
||||
#define PRIu8 "u"
|
||||
#define SCNi8 "hhi"
|
||||
#define SCNu8 "hhu"
|
||||
#define SCNi16 "hi"
|
||||
#define SCNu16 "hu"
|
||||
#define SCNi32 "i"
|
||||
#define SCNu32 "u"
|
||||
|
||||
__QCLOUD_INTERNAL__ qcloud_err_t event_json_node_add(char *json_doc, size_t json_doc_size, const char *key, void *data, json_data_type_t type)
|
||||
{
|
||||
qcloud_err_t rc;
|
||||
int32_t rc_snprintf = 0;
|
||||
size_t remain_size = 0;
|
||||
|
||||
if ((remain_size = json_doc_size - strlen(json_doc)) <= 1) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_JSON_BUFFER_TOO_SHORT);
|
||||
}
|
||||
|
||||
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size, "\"%s\":", key);
|
||||
rc = shadow_json_snprintf_rc2errno(rc_snprintf, remain_size);
|
||||
QCLOUD_FUNC_EXIT_RC_IF_NOT(rc, QCLOUD_ERR_SUCCESS, rc);
|
||||
|
||||
if ((remain_size = json_doc_size - strlen(json_doc)) <= 1) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_JSON_BUFFER_TOO_SHORT);
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size, "null,");
|
||||
QCLOUD_FUNC_EXIT_RC(shadow_json_snprintf_rc2errno(rc_snprintf, remain_size));
|
||||
}
|
||||
|
||||
|
||||
switch (type) {
|
||||
case JSON_DATA_TYPE_INT32:
|
||||
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size, "%"
|
||||
PRIi32
|
||||
",", *(int32_t *)(data));
|
||||
break;
|
||||
|
||||
case JSON_DATA_TYPE_INT16:
|
||||
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size, "%"
|
||||
PRIi16
|
||||
",", *(int16_t *)(data));
|
||||
break;
|
||||
|
||||
case JSON_DATA_TYPE_INT8:
|
||||
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size, "%"
|
||||
PRIi8
|
||||
",", *(int8_t *)(data));
|
||||
break;
|
||||
|
||||
case JSON_DATA_TYPE_UINT32:
|
||||
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size, "%"
|
||||
PRIu32
|
||||
",", *(uint32_t *)(data));
|
||||
break;
|
||||
|
||||
case JSON_DATA_TYPE_UINT16:
|
||||
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size, "%"
|
||||
PRIu16
|
||||
",", *(uint16_t *)(data));
|
||||
break;
|
||||
|
||||
case JSON_DATA_TYPE_UINT8:
|
||||
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size, "%"
|
||||
PRIu8
|
||||
",", *(uint8_t *)(data));
|
||||
break;
|
||||
|
||||
case JSON_DATA_TYPE_DOUBLE:
|
||||
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size, "%f,", *(double *)(data));
|
||||
break;
|
||||
|
||||
case JSON_DATA_TYPE_FLOAT:
|
||||
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size, "%f,", *(float *)(data));
|
||||
break;
|
||||
|
||||
case JSON_DATA_TYPE_BOOL:
|
||||
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size, "%u,",
|
||||
*(bool *) (data) ? 1 : 0);
|
||||
break;
|
||||
|
||||
case JSON_DATA_TYPE_STRING:
|
||||
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size, "\"%s\",", (char *)(data));
|
||||
break;
|
||||
|
||||
case JSON_DATA_TYPE_OBJECT:
|
||||
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size, "%s,", (char *)(data));
|
||||
break;
|
||||
}
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(shadow_json_snprintf_rc2errno(rc_snprintf, remain_size));
|
||||
}
|
||||
|
||||
__QCLOUD_INTERNAL__ int event_json_return_code_parse(char *json_doc, int32_t *return_code)
|
||||
{
|
||||
int rc = QCLOUD_TRUE;
|
||||
char *return_code_str;
|
||||
|
||||
return_code_str = LITE_json_value_of(EVENT_REPLY_FIELD_CODE, json_doc);
|
||||
if (!return_code_str) {
|
||||
return QCLOUD_FALSE;
|
||||
}
|
||||
|
||||
if (sscanf(return_code_str, "%" SCNi32, return_code) != 1) {
|
||||
QCLOUD_LOG_E("parse code failed, rc: %d", QCLOUD_ERR_JSON_PARSE);
|
||||
rc = QCLOUD_FALSE;
|
||||
}
|
||||
|
||||
osal_free(return_code_str);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
__QCLOUD_INTERNAL__ int event_json_status_parse(char *json_doc, char **status)
|
||||
{
|
||||
*status = LITE_json_value_of(EVENT_REPLY_FIELD_STATUS, json_doc);
|
||||
return *status != NULL;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@@ -0,0 +1,352 @@
|
||||
#include "qcloud.h"
|
||||
|
||||
__QCLOUD_STATIC__ qcloud_err_t mqtt_client_network_host_construct(qcloud_network_t *network, qcloud_device_t *device)
|
||||
{
|
||||
int server_len;
|
||||
char mqtt_server[QCLOUD_SERVER_DOMAIN_MAX];
|
||||
|
||||
memset(network->host, 0, sizeof(network->host));
|
||||
server_len = osal_snprintf(mqtt_server, sizeof(mqtt_server), "%s.%s", device->product_id, qcloud_mqtt_server);
|
||||
if (server_len < 0 || server_len > QCLOUD_SERVER_DOMAIN_MAX - 1) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
|
||||
}
|
||||
memcpy(network->host, mqtt_server, sizeof(network->host));
|
||||
|
||||
network->port = qcloud_mqtt_port;
|
||||
|
||||
return QCLOUD_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ qcloud_err_t mqtt_client_network_init(qcloud_network_t *network, qcloud_device_t *device)
|
||||
{
|
||||
#if (QCLOUD_CFG_TLS_EN > 0u)
|
||||
QCLOUD_FUNC_EXIT_RC_IF_NOT(qcloud_tls_init(&network->tls_opt, device), QCLOUD_ERR_SUCCESS, QCLOUD_ERR_FAILURE);
|
||||
QCLOUD_FUNC_EXIT_RC_IF_NOT(qcloud_network_tls_init(network), QCLOUD_ERR_SUCCESS, QCLOUD_ERR_FAILURE);
|
||||
#else
|
||||
QCLOUD_FUNC_EXIT_RC_IF_NOT(qcloud_network_tcp_init(network), QCLOUD_ERR_SUCCESS, QCLOUD_ERR_FAILURE);
|
||||
#endif
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC_IF_NOT(mqtt_client_network_host_construct(network, device), QCLOUD_ERR_SUCCESS, QCLOUD_ERR_FAILURE);
|
||||
|
||||
return QCLOUD_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ uint16_t mqtt_client_random_packet_id_generate(void)
|
||||
{
|
||||
#define PACKET_ID_MAX (65535)
|
||||
|
||||
srand((unsigned)osal_timer_current_sec());
|
||||
return rand() % (PACKET_ID_MAX + 1) + 1;
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ qcloud_err_t mqtt_client_construct(qcloud_mqtt_client_t *client,
|
||||
mqtt_event_handler_fn_t handler,
|
||||
void *handler_context,
|
||||
qcloud_auto_connect_state_t auto_connect_state)
|
||||
{
|
||||
client->event_handler.handler = handler;
|
||||
client->event_handler.context = handler_context;
|
||||
client->auto_connect_state = auto_connect_state;
|
||||
|
||||
client->command_timeout = QCLOUD_MQTT_COMMAND_TIMEOUT;
|
||||
client->connection_state = QCLOUD_MQTT_CONNECTION_STATE_DISCONNECTED;
|
||||
|
||||
// packet id 取随机数 1- 65536
|
||||
client->packet_id = mqtt_client_random_packet_id_generate();
|
||||
client->ping_outstanding = 0;
|
||||
client->is_manually_disconnected = QCLOUD_FALSE;
|
||||
client->network_disconnect_counter = 0;
|
||||
|
||||
if ((client->global_lock = osal_mutex_create()) == NULL) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
|
||||
}
|
||||
|
||||
if ((client->tx_lock = osal_mutex_create()) == NULL) {
|
||||
QCLOUD_LOG_E("write buf lock failed.");
|
||||
goto errout;
|
||||
}
|
||||
|
||||
if ((client->ack_pend_list_lock = osal_mutex_create()) == NULL) {
|
||||
QCLOUD_LOG_E("ack list lock failed.");
|
||||
goto errout;
|
||||
}
|
||||
|
||||
if ((client->msg_handler_list_lock = osal_mutex_create()) == NULL) {
|
||||
QCLOUD_LOG_E("handler list lock failed.");
|
||||
goto errout;
|
||||
}
|
||||
|
||||
qcloud_list_init(&client->ack_pend_list);
|
||||
|
||||
qcloud_list_init(&client->msg_handler_list);
|
||||
|
||||
// ping定时器以及重连延迟定时器相关初始化
|
||||
osal_timer_init(&client->ping_timer);
|
||||
osal_timer_init(&client->reconnect_timer);
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
|
||||
|
||||
errout:
|
||||
if (client->global_lock) {
|
||||
osal_mutex_destroy(client->global_lock);
|
||||
client->global_lock = NULL;
|
||||
}
|
||||
|
||||
if (client->tx_lock) {
|
||||
osal_mutex_destroy(client->tx_lock);
|
||||
client->tx_lock = NULL;
|
||||
}
|
||||
|
||||
if (client->ack_pend_list_lock) {
|
||||
osal_mutex_destroy(client->ack_pend_list_lock);
|
||||
client->ack_pend_list_lock = NULL;
|
||||
}
|
||||
|
||||
if (client->msg_handler_list_lock) {
|
||||
osal_mutex_destroy(client->msg_handler_list_lock);
|
||||
client->msg_handler_list_lock = NULL;
|
||||
}
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE)
|
||||
}
|
||||
|
||||
#if (QCLOUD_CFG_TLS_EN == 0u) && (QCLOUD_CFG_AUTH_MODE == QCLOUD_AUTH_MODE_KEY)
|
||||
__QCLOUD_STATIC__ qcloud_err_t mqtt_client_key_decode(uint8_t *decoded_buf, size_t decoded_buf_size, const char *key, size_t *decoded_key_len)
|
||||
{
|
||||
QCLOUD_POINTER_SANITY_CHECK(decoded_buf, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(key, QCLOUD_ERR_INVAL);
|
||||
|
||||
int key_len = 0;
|
||||
|
||||
memset(decoded_buf, 0, decoded_buf_size);
|
||||
|
||||
key_len = strlen(key);
|
||||
if (qcloud_utils_base64_decode(decoded_buf, decoded_buf_size, decoded_key_len,
|
||||
(unsigned char *)key, key_len) != 0) {
|
||||
QCLOUD_LOG_E("psk decode failed!");
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_INVAL);
|
||||
}
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ qcloud_err_t mqtt_client_password_encode(mqtt_connect_opt_t *connect_opt, uint8_t *key, size_t key_len)
|
||||
{
|
||||
char digest[41];
|
||||
int password_len = 0;
|
||||
|
||||
memset(digest, 0, sizeof(digest));
|
||||
utils_hmac_sha1(connect_opt->username, strlen(connect_opt->username), digest, (const char *)key, key_len);
|
||||
|
||||
password_len = osal_snprintf(connect_opt->password, sizeof(connect_opt->password), "%s;hmacsha1", digest);
|
||||
if (password_len < 0 || password_len >= QCLOUD_DEVICE_PASSWORD_MAX) {
|
||||
QCLOUD_LOG_E("password encode failed");
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_DEV_INFO);
|
||||
}
|
||||
|
||||
connect_opt->password_len = password_len;
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
__QCLOUD_STATIC__ qcloud_err_t mqtt_client_id_generate(mqtt_connect_opt_t *connect_opt, qcloud_device_t *device)
|
||||
{
|
||||
int client_id_len = 0;
|
||||
|
||||
memset(connect_opt->client_id, 0, sizeof(connect_opt->client_id));
|
||||
|
||||
client_id_len = osal_snprintf(connect_opt->client_id, sizeof(connect_opt->client_id), "%s%s", device->product_id, device->device_name);
|
||||
if (client_id_len < 0 || client_id_len >= QCLOUD_MQTT_DEVICE_CLIENT_ID_MAX) {
|
||||
QCLOUD_LOG_E("client id generate failed");
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_DEV_INFO);
|
||||
}
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ qcloud_err_t mqtt_client_username_generate(mqtt_connect_opt_t *connect_opt)
|
||||
{
|
||||
uint32_t now;
|
||||
int username_len = 0;
|
||||
char connection_id[QCLOUD_MQTT_CONNECT_ID_MAX + 1];
|
||||
|
||||
now = osal_timer_current_sec() + QCLOUD_MQTT_ACCESS_EXPIRE_TIMEOUT_MAX / 1000;
|
||||
|
||||
mqtt_glue_connect_id_generate(connection_id);
|
||||
|
||||
memset(connect_opt->username, 0, sizeof(connect_opt->username));
|
||||
|
||||
username_len = osal_snprintf(connect_opt->username, sizeof(connect_opt->username), "%s;%s;%s;%ld",
|
||||
connect_opt->client_id, QCLOUD_APPID, connection_id, now);
|
||||
if (username_len < 0 || username_len >= QCLOUD_DEVICE_USERNAME_MAX) {
|
||||
QCLOUD_LOG_E("username generate failed");
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_DEV_INFO);
|
||||
}
|
||||
|
||||
connect_opt->username_len = username_len;
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
|
||||
}
|
||||
|
||||
__QCLOUD_INTERNAL__ void mqtt_client_connection_state_set(qcloud_mqtt_client_t *client, qcloud_mqtt_con_status_t state)
|
||||
{
|
||||
osal_mutex_lock(client->global_lock);
|
||||
client->connection_state = state;
|
||||
osal_mutex_unlock(client->global_lock);
|
||||
}
|
||||
|
||||
__QCLOUD_API__ qcloud_err_t qcloud_mqtt_connect_opt_create(mqtt_connect_opt_t *connect_opt,
|
||||
qcloud_device_t *device,
|
||||
mqtt_version_t mqtt_version,
|
||||
uint16_t keep_alive_interval,
|
||||
mqtt_clean_session_state_t clean_session)
|
||||
{
|
||||
QCLOUD_POINTER_SANITY_CHECK(connect_opt, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(device, QCLOUD_ERR_INVAL);
|
||||
|
||||
#if (QCLOUD_CFG_TLS_EN == 0u)
|
||||
uint8_t decoded_key[QCLOUD_PSK_MAX];
|
||||
size_t decoded_key_len;
|
||||
#endif
|
||||
|
||||
memset(connect_opt, 0, sizeof(mqtt_connect_opt_t));
|
||||
|
||||
// 1. client id
|
||||
QCLOUD_FUNC_EXIT_RC_IF_NOT(mqtt_client_id_generate(connect_opt, device), QCLOUD_ERR_SUCCESS, QCLOUD_ERR_FAILURE);
|
||||
|
||||
// 2. username
|
||||
QCLOUD_FUNC_EXIT_RC_IF_NOT(mqtt_client_username_generate(connect_opt), QCLOUD_ERR_SUCCESS, QCLOUD_ERR_FAILURE);
|
||||
|
||||
#if (QCLOUD_CFG_TLS_EN == 0u) && (QCLOUD_CFG_AUTH_MODE == QCLOUD_AUTH_MODE_KEY)
|
||||
// 3. key(temporary)
|
||||
QCLOUD_FUNC_EXIT_RC_IF_NOT(mqtt_client_key_decode(decoded_key, sizeof(decoded_key), device->key, &decoded_key_len), QCLOUD_ERR_SUCCESS, QCLOUD_ERR_FAILURE);
|
||||
|
||||
// 4. password
|
||||
QCLOUD_FUNC_EXIT_RC_IF_NOT(mqtt_client_password_encode(connect_opt, decoded_key, decoded_key_len), QCLOUD_ERR_SUCCESS, QCLOUD_ERR_FAILURE);
|
||||
#endif
|
||||
|
||||
connect_opt->mqtt_version = mqtt_version;
|
||||
// keep alive interval is in second, no long than 11.5 minutes(11.5 * 60 seconds)
|
||||
connect_opt->keep_alive_interval = QCLOUD_MIN(keep_alive_interval, 11.5 * 60);
|
||||
connect_opt->clean_session = clean_session;
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
|
||||
}
|
||||
|
||||
__QCLOUD_API__ qcloud_err_t qcloud_mqtt_client_create(qcloud_mqtt_client_t *client,
|
||||
qcloud_device_t *device,
|
||||
mqtt_event_handler_fn_t handler,
|
||||
void *handler_context,
|
||||
qcloud_auto_connect_state_t auto_connect_state)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(device, QCLOUD_ERR_INVAL);
|
||||
|
||||
memset(client, 0, sizeof(qcloud_mqtt_client_t));
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC_IF_NOT(mqtt_client_network_init(&client->network, device), QCLOUD_ERR_SUCCESS, QCLOUD_ERR_FAILURE);
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC_IF_NOT(mqtt_client_construct(client, handler, handler_context, auto_connect_state), QCLOUD_ERR_SUCCESS, QCLOUD_ERR_FAILURE);
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
|
||||
}
|
||||
|
||||
__QCLOUD_API__ void qcloud_mqtt_client_destroy(qcloud_mqtt_client_t *client)
|
||||
{
|
||||
#if (QCLOUD_CFG_DUPLICATED_MSG_REMOVE_EN > 0u)
|
||||
mqtt_glue_packet_id_cache_reset();
|
||||
#endif
|
||||
|
||||
if (qcloud_mqtt_client_is_connected(client)) {
|
||||
qcloud_mqtt_client_disconnect(client);
|
||||
}
|
||||
|
||||
mqtt_glue_ack_list_destroy(client);
|
||||
|
||||
osal_mutex_destroy(client->ack_pend_list_lock);
|
||||
osal_mutex_destroy(client->global_lock);
|
||||
osal_mutex_destroy(client->tx_lock);
|
||||
}
|
||||
|
||||
__QCLOUD_API__ qcloud_err_t qcloud_mqtt_client_publish(qcloud_mqtt_client_t *client, char *topic, mqtt_publish_opt_t *publish_opt)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(topic, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(publish_opt, QCLOUD_ERR_INVAL);
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(qcloud_mqtt_publish(client, topic, publish_opt));
|
||||
}
|
||||
|
||||
__QCLOUD_API__ qcloud_err_t qcloud_mqtt_client_subscribe(qcloud_mqtt_client_t *client, const char *topic_filter, mqtt_subscribe_opt_t *subscribe_opt)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(topic_filter, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(subscribe_opt, QCLOUD_ERR_INVAL);
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(qcloud_mqtt_subscribe(client, topic_filter, subscribe_opt));
|
||||
}
|
||||
|
||||
__QCLOUD_API__ qcloud_err_t qcloud_mqtt_client_unsubscribe(qcloud_mqtt_client_t *client, const char *topic_filter)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(topic_filter, QCLOUD_ERR_INVAL);
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(qcloud_mqtt_unsubscribe(client, topic_filter));
|
||||
}
|
||||
|
||||
__QCLOUD_API__ qcloud_err_t qcloud_mqtt_client_yield(qcloud_mqtt_client_t *client, mqtt_connect_opt_t *connect_opt, uint32_t timeout_ms)
|
||||
{
|
||||
QCLOUD_FUNC_EXIT_RC(qcloud_mqtt_yield(client, connect_opt, timeout_ms));
|
||||
}
|
||||
|
||||
__QCLOUD_API__ qcloud_err_t qcloud_mqtt_client_disconnect(qcloud_mqtt_client_t *client)
|
||||
{
|
||||
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(qcloud_mqtt_disconnect(client));
|
||||
}
|
||||
|
||||
__QCLOUD_API__ qcloud_err_t qcloud_mqtt_client_connect(qcloud_mqtt_client_t *client, mqtt_connect_opt_t *connect_opt)
|
||||
{
|
||||
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(connect_opt, QCLOUD_ERR_INVAL);
|
||||
|
||||
qcloud_err_t rc;
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC_IF(client->connection_state, QCLOUD_MQTT_CONNECTION_STATE_CONNECTED, QCLOUD_ERR_MQTT_ALREADY_CONNECTED);
|
||||
|
||||
rc = qcloud_mqtt_connect(client, connect_opt);
|
||||
if (rc != QCLOUD_ERR_SUCCESS) {
|
||||
QCLOUD_LOG_E("mqtt connect failed: %d", rc);
|
||||
} else {
|
||||
QCLOUD_LOG_I("mqtt connect success");
|
||||
}
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
|
||||
}
|
||||
|
||||
__QCLOUD_API__ int qcloud_mqtt_client_is_connected(qcloud_mqtt_client_t *client)
|
||||
{
|
||||
int is_connected = 0;
|
||||
|
||||
if (!client) {
|
||||
return QCLOUD_FALSE;
|
||||
}
|
||||
|
||||
osal_mutex_lock(client->global_lock);
|
||||
is_connected = client->connection_state == QCLOUD_MQTT_CONNECTION_STATE_CONNECTED;
|
||||
osal_mutex_unlock(client->global_lock);
|
||||
|
||||
return is_connected ? QCLOUD_TRUE : QCLOUD_FALSE;
|
||||
}
|
||||
|
@@ -0,0 +1,962 @@
|
||||
/*******************************************************************************
|
||||
* 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
|
||||
* Sergio R. Caprile - non-blocking packet read functions for stream transport
|
||||
*******************************************************************************/
|
||||
|
||||
#include "qcloud.h"
|
||||
|
||||
__QCLOUD_STATIC__ int mqtt_common_packet_length_get(int rem_len)
|
||||
{
|
||||
rem_len += 1; /* header byte */
|
||||
|
||||
/* now remaining_length field */
|
||||
if (rem_len < 128) {
|
||||
rem_len += 1;
|
||||
} else if (rem_len < 16384) {
|
||||
rem_len += 2;
|
||||
} else if (rem_len < 2097151) {
|
||||
rem_len += 3;
|
||||
} else {
|
||||
rem_len += 4;
|
||||
}
|
||||
|
||||
return rem_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
__QCLOUD_STATIC__ int mqtt_common_serialize_connect_packet_length(mqtt_connect_opt_t *connect_opt)
|
||||
{
|
||||
int len = 0;
|
||||
|
||||
/* variable depending on MQTT or MQIsdp */
|
||||
if (connect_opt->mqtt_version == 3) {
|
||||
len = 12;
|
||||
} else if (connect_opt->mqtt_version == 4) {
|
||||
len = 10;
|
||||
}
|
||||
|
||||
len += strlen(connect_opt->client_id) + 2;
|
||||
|
||||
if (connect_opt->username_len) {
|
||||
len += connect_opt->username_len + 2;
|
||||
}
|
||||
|
||||
if (connect_opt->password_len) {
|
||||
len += connect_opt->password_len + 2;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
__QCLOUD_STATIC__ int mqtt_common_serialize_publish_packet_length(int qos, char *topic, size_t payload_len)
|
||||
{
|
||||
int len = 0;
|
||||
|
||||
len += 2 + strlen(topic) + payload_len;
|
||||
if (qos > MQTT_QOS0) {
|
||||
len += 2; /* packet id */
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
__QCLOUD_STATIC__ int mqtt_common_serialize_subscribe_packet_length(uint32_t count, char *topic_filters[])
|
||||
{
|
||||
int i;
|
||||
int len = 2; /* packet id */
|
||||
|
||||
for (i = 0; i < count; ++i) {
|
||||
len += 2 + strlen(topic_filters[i]) + 1; /* length + topic + req_qos */
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
__QCLOUD_STATIC__ int mqtt_common_serialize_unsubscribe_packet_length(uint32_t count, char *topic_filters[])
|
||||
{
|
||||
int i = 0;
|
||||
int len = 2; /* packet id */
|
||||
|
||||
for (i = 0; i < count; ++i) {
|
||||
len += 2 + strlen(topic_filters[i]); /* length + topic*/
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes the message length according to the MQTT algorithm
|
||||
* @param getcharfn pointer to function to read the next character from the data source
|
||||
* @param value the decoded length returned
|
||||
* @return the number of bytes read from the socket
|
||||
*/
|
||||
__QCLOUD_STATIC__ qcloud_err_t mqtt_common_packet_do_decode_from_buf(uint32_t (*getcharfn)(uint8_t *, uint32_t), uint32_t *value, uint32_t *read_bytes_len)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
uint8_t c;
|
||||
uint32_t multiplier = 1;
|
||||
uint32_t len = 0, get_len;
|
||||
|
||||
*value = 0;
|
||||
|
||||
do {
|
||||
#define MAX_NO_OF_REMAINING_LENGTH_BYTES 4
|
||||
|
||||
if (++len > MAX_NO_OF_REMAINING_LENGTH_BYTES) {
|
||||
/* bad data */
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_PACKET_READ);
|
||||
}
|
||||
|
||||
get_len = 0;
|
||||
get_len = (*getcharfn)(&c, 1);
|
||||
if (1 != get_len) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
|
||||
}
|
||||
*value += (c & 127) * multiplier;
|
||||
multiplier *= 128;
|
||||
} while ((c & 128) != 0);
|
||||
|
||||
*read_bytes_len = len;
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
|
||||
}
|
||||
|
||||
static uint8_t *bufptr;
|
||||
__QCLOUD_STATIC__ uint32_t __bufchar(uint8_t *c, uint32_t count)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < count; ++i) {
|
||||
*c = *bufptr++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the message length according to the MQTT algorithm
|
||||
* @param buf the buffer into which the encoded data is written
|
||||
* @param length the length to be encoded
|
||||
* @return the number of bytes written to buffer
|
||||
*/
|
||||
__QCLOUD_INTERNAL__ int mqtt_common_packet_encode(uint8_t *buf, int length)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
int rc = 0;
|
||||
uint8_t encode_byte;
|
||||
|
||||
do {
|
||||
encode_byte = (uint8_t)(length % 128);
|
||||
length /= 128;
|
||||
/* if there are more digits to encode, set the top bit of this digit */
|
||||
if (length > 0) {
|
||||
encode_byte |= 0x80;
|
||||
}
|
||||
buf[rc++] = encode_byte;
|
||||
} while (length > 0);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ qcloud_err_t mqtt_common_packet_decode_from_buf(uint8_t *buf, uint32_t *value, uint32_t *read_bytes_len)
|
||||
{
|
||||
bufptr = buf;
|
||||
return mqtt_common_packet_do_decode_from_buf(__bufchar, value, read_bytes_len);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates uint16 packet id from two bytes read from the input buffer
|
||||
* @param pptr pointer to the input buffer - incremented by the number of bytes used & returned
|
||||
* @return the value calculated
|
||||
*/
|
||||
__QCLOUD_STATIC__ uint16_t mqtt_common_packet_read_dbyte(uint8_t **pptr)
|
||||
{
|
||||
uint8_t *ptr = *pptr;
|
||||
uint8_t firstByte = (uint8_t) (*ptr);
|
||||
uint8_t secondByte = (uint8_t) (*(ptr + 1));
|
||||
uint16_t len = (uint16_t) (secondByte + (256 * firstByte));
|
||||
*pptr += 2;
|
||||
return len;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads one character from the input buffer.
|
||||
* @param pptr pointer to the input buffer - incremented by the number of bytes used & returned
|
||||
* @return the character read
|
||||
*/
|
||||
__QCLOUD_STATIC__ uint8_t mqtt_common_packet_read_byte(uint8_t **pptr)
|
||||
{
|
||||
uint8_t c = **pptr;
|
||||
(*pptr)++;
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
__QCLOUD_STATIC__ qcloud_err_t mqtt_common_packet_read_string(char **string, uint16_t *string_len, uint8_t **pptr, uint8_t *enddata)
|
||||
{
|
||||
/* the first two bytes are the length of the string */
|
||||
if (enddata - (*pptr) <= 1) { /* enough length to read the integer? */
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
|
||||
}
|
||||
|
||||
*string_len = mqtt_common_packet_read_dbyte(pptr); /* increments pptr to point past length */
|
||||
|
||||
if (*string_len > QCLOUD_MQTT_CLIENT_RX_BUF_LEN){
|
||||
QCLOUD_LOG_E("string length overflow!");
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
|
||||
}
|
||||
|
||||
if (&(*pptr)[*string_len] > enddata) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
|
||||
}
|
||||
|
||||
*string = (char *)*pptr;
|
||||
*pptr += *string_len;
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes one character to an output buffer.
|
||||
* @param pptr pointer to the output buffer - incremented by the number of bytes used & returned
|
||||
* @param c the character to write
|
||||
*/
|
||||
__QCLOUD_STATIC__ void mqtt_common_packet_write_byte(uint8_t **pptr, uint8_t c)
|
||||
{
|
||||
**pptr = c;
|
||||
(*pptr)++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes an integer as 2 bytes to an output buffer.
|
||||
* @param pptr pointer to the output buffer - incremented by the number of bytes used & returned
|
||||
* @param anInt the integer to write
|
||||
*/
|
||||
__QCLOUD_STATIC__ void mqtt_common_packet_write_dbyte(uint8_t **pptr, uint16_t dbyte)
|
||||
{
|
||||
**pptr = (uint8_t)(dbyte / 256);
|
||||
(*pptr)++;
|
||||
**pptr = (uint8_t)(dbyte % 256);
|
||||
(*pptr)++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a "UTF" string to an output buffer. Converts C string to length-delimited.
|
||||
* @param pptr pointer to the output buffer - incremented by the number of bytes used & returned
|
||||
* @param string the C string to write
|
||||
*/
|
||||
__QCLOUD_STATIC__ void mqtt_common_packet_write_string(uint8_t **pptr, const char *string)
|
||||
{
|
||||
size_t len = strlen(string);
|
||||
mqtt_common_packet_write_dbyte(pptr, (uint16_t)len);
|
||||
memcpy(*pptr, string, len);
|
||||
*pptr += len;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
__QCLOUD_STATIC__ qcloud_err_t mqtt_common_serialize_ack_packet(uint8_t *buf,
|
||||
size_t buf_len,
|
||||
mqtt_packet_t packet_type,
|
||||
uint8_t dup,
|
||||
uint16_t packet_id,
|
||||
uint32_t *serialized_len)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
QCLOUD_POINTER_SANITY_CHECK(buf, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(serialized_len, QCLOUD_ERR_INVAL);
|
||||
|
||||
mqtt_header_t header = {0};
|
||||
uint8_t *ptr = buf;
|
||||
|
||||
/* Minimum byte length required by ACK headers is
|
||||
* 2 for fixed and 2 for variable part */
|
||||
if (buf_len < 4) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_BUF_TOO_SHORT);
|
||||
}
|
||||
|
||||
header.bits.type = packet_type;
|
||||
header.bits.dup = dup;
|
||||
header.bits.qos = (packet_type == MQTT_PACKET_TYPE_PUBREL) ? 1 : 0;
|
||||
|
||||
mqtt_common_packet_write_byte(&ptr, header.byte); /* write header */
|
||||
|
||||
ptr += mqtt_common_packet_encode(ptr, 2); /* write remaining length */
|
||||
mqtt_common_packet_write_dbyte(&ptr, packet_id);
|
||||
|
||||
*serialized_len = ptr - buf;
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializes the supplied (wire) buffer into an ack
|
||||
* @param packet_type returned integer - the MQTT packet type
|
||||
* @param dup returned integer - the MQTT dup flag
|
||||
* @param packet_id returned integer - the MQTT packet identifier
|
||||
* @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, 0 is failure
|
||||
*/
|
||||
__QCLOUD_INTERNAL__ qcloud_err_t mqtt_common_deserialize_ack_packet(uint8_t *packet_type,
|
||||
uint8_t *dup,
|
||||
uint16_t *packet_id,
|
||||
uint8_t *buf,
|
||||
size_t buf_len)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
QCLOUD_POINTER_SANITY_CHECK(packet_type, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(dup, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(packet_id, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(buf, QCLOUD_ERR_INVAL);
|
||||
|
||||
qcloud_err_t rc;
|
||||
mqtt_header_t header = {0};
|
||||
uint8_t ack_code;
|
||||
uint8_t *curdata = buf, *enddata = NULL;
|
||||
uint32_t decoded_len = 0, read_bytes_len = 0;
|
||||
|
||||
/* PUBACK fixed header size is two bytes, variable header is 2 bytes, MQTT v3.1.1 Specification 3.4.1 */
|
||||
if (buf_len < 4) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_BUF_TOO_SHORT);
|
||||
}
|
||||
|
||||
header.byte = mqtt_common_packet_read_byte(&curdata);
|
||||
*dup = header.bits.dup;
|
||||
*packet_type = header.bits.type;
|
||||
|
||||
/* read remaining length */
|
||||
rc = mqtt_common_packet_decode_from_buf(curdata, &decoded_len, &read_bytes_len);
|
||||
if (QCLOUD_ERR_SUCCESS != rc) {
|
||||
QCLOUD_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
curdata += read_bytes_len;
|
||||
enddata = curdata + decoded_len;
|
||||
|
||||
if (enddata - curdata < 2) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
|
||||
}
|
||||
|
||||
*packet_id = mqtt_common_packet_read_dbyte(&curdata);
|
||||
|
||||
// 返回错误码处理
|
||||
if (enddata - curdata >= 1) {
|
||||
ack_code = mqtt_common_packet_read_byte(&curdata);
|
||||
if (ack_code != 0) {
|
||||
QCLOUD_LOG_E("deserialize ack packet failure! 0x%02x", ack_code);
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializes the supplied (wire) buffer into suback data
|
||||
* @param packet_id returned integer - the MQTT packet identifier
|
||||
* @param max_count - the maximum number of members allowed in the grantedQoSs array
|
||||
* @param count returned integer - number of members in the grantedQoSs array
|
||||
* @param grantedQoSs returned array of integers - the granted qualities of service
|
||||
* @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, 0 is failure
|
||||
*/
|
||||
__QCLOUD_INTERNAL__ qcloud_err_t mqtt_common_deserialize_suback_packet(uint16_t *packet_id,
|
||||
uint32_t max_count,
|
||||
uint32_t *count,
|
||||
int granted_qoss[],
|
||||
uint8_t *buf,
|
||||
size_t buf_len)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
QCLOUD_POINTER_SANITY_CHECK(packet_id, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(count, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(granted_qoss, QCLOUD_ERR_INVAL);
|
||||
|
||||
qcloud_err_t rc;
|
||||
mqtt_header_t header = {0};
|
||||
uint8_t *curdata = buf, *enddata = NULL;
|
||||
uint32_t decoded_len = 0, read_bytes_len = 0;
|
||||
|
||||
// SUBACK头部大小为4字节, 负载部分至少为1字节QOS返回码
|
||||
if (buf_len < 5) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_BUF_TOO_SHORT);
|
||||
}
|
||||
|
||||
// 读取报文固定头部的第一个字节
|
||||
header.byte = mqtt_common_packet_read_byte(&curdata);
|
||||
if (header.bits.type != MQTT_PACKET_TYPE_SUBACK) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
|
||||
}
|
||||
|
||||
// 读取报文固定头部的剩余长度
|
||||
rc = mqtt_common_packet_decode_from_buf(curdata, &decoded_len, &read_bytes_len);
|
||||
if (rc != QCLOUD_ERR_SUCCESS) {
|
||||
QCLOUD_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
curdata += read_bytes_len;
|
||||
enddata = curdata + decoded_len;
|
||||
if (enddata - curdata < 2) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
|
||||
}
|
||||
|
||||
// 读取报文可变头部的报文标识符
|
||||
*packet_id = mqtt_common_packet_read_dbyte(&curdata);
|
||||
|
||||
// 读取报文的负载部分
|
||||
*count = 0;
|
||||
while (curdata < enddata) {
|
||||
if (*count > max_count) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
|
||||
}
|
||||
granted_qoss[(*count)++] = mqtt_common_packet_read_byte(&curdata);
|
||||
}
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializes the supplied (wire) buffer into unsuback data
|
||||
* @param packet_id returned integer - the MQTT packet identifier
|
||||
* @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 int indicating function execution status
|
||||
*/
|
||||
__QCLOUD_INTERNAL__ qcloud_err_t mqtt_common_deserialize_unsuback_packet(uint16_t *packet_id,
|
||||
uint8_t *buf,
|
||||
size_t buf_len)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
QCLOUD_POINTER_SANITY_CHECK(buf, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(packet_id, QCLOUD_ERR_INVAL);
|
||||
|
||||
qcloud_err_t rc;
|
||||
uint8_t type = 0, dup = 0;
|
||||
|
||||
rc = mqtt_common_deserialize_ack_packet(&type, &dup, packet_id, buf, buf_len);
|
||||
if (rc != QCLOUD_ERR_SUCCESS || type != MQTT_PACKET_TYPE_UNSUBACK) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
|
||||
}
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
__QCLOUD_INTERNAL__ qcloud_err_t mqtt_common_serialize_connect_packet(uint8_t *buf,
|
||||
size_t buf_len,
|
||||
mqtt_connect_opt_t *connect_opt,
|
||||
uint32_t *serialized_len)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
QCLOUD_POINTER_SANITY_CHECK(buf, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(connect_opt, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(serialized_len, QCLOUD_ERR_INVAL);
|
||||
|
||||
int packet_len = 0;
|
||||
|
||||
uint8_t *ptr = buf;
|
||||
mqtt_header_t header = {0};
|
||||
mqtt_connect_flag_t flags = {0};
|
||||
|
||||
packet_len = mqtt_common_serialize_connect_packet_length(connect_opt);
|
||||
if (mqtt_common_packet_length_get(packet_len) > buf_len) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_BUF_TOO_SHORT);
|
||||
}
|
||||
|
||||
header.byte = 0;
|
||||
header.bits.type = MQTT_PACKET_TYPE_CONNECT;
|
||||
|
||||
// 报文固定头部第一个字节
|
||||
mqtt_common_packet_write_byte(&ptr, header.byte); /* write header */
|
||||
|
||||
// 报文固定头部剩余长度字段
|
||||
ptr += mqtt_common_packet_encode(ptr, packet_len); /* write remaining length */
|
||||
|
||||
// 报文可变头部协议名 + 协议版本号
|
||||
if (connect_opt->mqtt_version == 4) {
|
||||
mqtt_common_packet_write_string(&ptr, "MQTT");
|
||||
mqtt_common_packet_write_byte(&ptr, (uint8_t)4);
|
||||
} else {
|
||||
mqtt_common_packet_write_string(&ptr, "MQIsdp");
|
||||
mqtt_common_packet_write_byte(&ptr, (uint8_t)3);
|
||||
}
|
||||
|
||||
// 报文可变头部连接标识位
|
||||
flags.all = 0;
|
||||
flags.bits.cleansession = connect_opt->clean_session;
|
||||
|
||||
if (connect_opt->username_len) {
|
||||
flags.bits.username = 1;
|
||||
}
|
||||
|
||||
if (connect_opt->password_len) {
|
||||
flags.bits.password = 1;
|
||||
}
|
||||
|
||||
mqtt_common_packet_write_byte(&ptr, flags.all);
|
||||
|
||||
// 报文可变头部心跳周期/保持连接, 一个以秒为单位的时间间隔, 表示为一个16位的字
|
||||
mqtt_common_packet_write_dbyte(&ptr, connect_opt->keep_alive_interval);
|
||||
|
||||
// 有效负载部分: 客户端标识符
|
||||
mqtt_common_packet_write_string(&ptr, connect_opt->client_id);
|
||||
|
||||
// 用户名
|
||||
if (flags.bits.username) {
|
||||
mqtt_common_packet_write_string(&ptr, connect_opt->username);
|
||||
}
|
||||
|
||||
if (flags.bits.password) {
|
||||
mqtt_common_packet_write_string(&ptr, connect_opt->password);
|
||||
}
|
||||
|
||||
*serialized_len = ptr - buf;
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
__QCLOUD_INTERNAL__ qcloud_err_t mqtt_common_deserialize_connack_packet(uint8_t *session_present,
|
||||
uint8_t *connack_rc,
|
||||
uint8_t *buf,
|
||||
size_t buf_len)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
QCLOUD_POINTER_SANITY_CHECK(session_present, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(connack_rc, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(buf, QCLOUD_ERR_INVAL);
|
||||
|
||||
qcloud_err_t rc;
|
||||
mqtt_header_t header = {0};
|
||||
mqtt_connack_flags_t flags = {0};
|
||||
uint8_t *curdata = buf, *enddata = NULL;
|
||||
uint32_t decoded_len = 0, read_bytes_len = 0;
|
||||
|
||||
// CONNACK 头部大小是固定的2字节长度, 可变头部也是两个字节的长度, 无有效负载
|
||||
if (buf_len < 4) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_BUF_TOO_SHORT);
|
||||
}
|
||||
|
||||
// 读取固定头部第一个字节
|
||||
header.byte = mqtt_common_packet_read_byte(&curdata);
|
||||
if (header.bits.type != MQTT_PACKET_TYPE_CONNACK) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
|
||||
}
|
||||
|
||||
// 读取固定头部剩余长度字段
|
||||
QCLOUD_FUNC_EXIT_RC_IF_NOT(rc = mqtt_common_packet_decode_from_buf(curdata, &decoded_len, &read_bytes_len), QCLOUD_ERR_SUCCESS, rc);
|
||||
|
||||
curdata += read_bytes_len;
|
||||
enddata = curdata + decoded_len;
|
||||
if (enddata - curdata != 2) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
|
||||
}
|
||||
|
||||
// 读取可变头部-连接确认标志 参考MQTT协议说明文档3.2.2.1小结
|
||||
flags.all = mqtt_common_packet_read_byte(&curdata);
|
||||
*session_present = flags.bits.sessionpresent;
|
||||
|
||||
// 读取可变头部-连接返回码 参考MQTT协议说明文档3.2.2.3小结
|
||||
*connack_rc = mqtt_common_packet_read_byte(&curdata);
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes a 0-length packet into the supplied buffer, ready for writing to a socket
|
||||
* @param buf the buffer into which the packet will be serialized
|
||||
* @param buf_len the length in bytes of the supplied buffer, to avoid overruns
|
||||
* @param packettype the message type
|
||||
* @param serialized length
|
||||
* @return int indicating function execution status
|
||||
*/
|
||||
__QCLOUD_INTERNAL__ qcloud_err_t mqtt_common_serialize_zero_payload_packet(uint8_t *buf,
|
||||
size_t buf_len,
|
||||
mqtt_packet_t packet_type,
|
||||
uint32_t *serialized_len)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
QCLOUD_POINTER_SANITY_CHECK(buf, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(serialized_len, QCLOUD_ERR_INVAL);
|
||||
|
||||
uint8_t *ptr = buf;
|
||||
mqtt_header_t header = {0};
|
||||
|
||||
/* Buffer should have at least 2 bytes for the header */
|
||||
if (buf_len < 2) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_BUF_TOO_SHORT);
|
||||
}
|
||||
|
||||
header.byte = 0;
|
||||
header.bits.type = packet_type;
|
||||
|
||||
/* write header */
|
||||
mqtt_common_packet_write_byte(&ptr, header.byte);
|
||||
|
||||
/* write remaining length */
|
||||
ptr += mqtt_common_packet_encode(ptr, 0);
|
||||
*serialized_len = ptr - buf;
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_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
|
||||
*/
|
||||
__QCLOUD_INTERNAL__ qcloud_err_t mqtt_common_deserialize_publish_packet(uint8_t *dup, int *qos,
|
||||
uint8_t *retained, uint16_t *packet_id,
|
||||
char **topic, uint16_t *topic_len,
|
||||
uint8_t **payload, size_t *payload_len,
|
||||
uint8_t *buf, size_t buf_len)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
QCLOUD_POINTER_SANITY_CHECK(dup, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(qos, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(retained, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(packet_id, QCLOUD_ERR_INVAL);
|
||||
|
||||
qcloud_err_t rc;
|
||||
uint32_t decoded_len = 0, read_bytes_len = 0;
|
||||
mqtt_header_t header = {0};
|
||||
uint8_t *curdata = buf, *enddata = NULL;
|
||||
|
||||
/* 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 (buf_len < 4) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_BUF_TOO_SHORT);
|
||||
}
|
||||
|
||||
header.byte = mqtt_common_packet_read_byte(&curdata);
|
||||
if (header.bits.type != MQTT_PACKET_TYPE_PUBLISH) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
|
||||
}
|
||||
|
||||
*dup = header.bits.dup;
|
||||
*qos = header.bits.qos;
|
||||
*retained = header.bits.retain;
|
||||
|
||||
/* read remaining length */
|
||||
rc = mqtt_common_packet_decode_from_buf(curdata, &decoded_len, &read_bytes_len); /* read remaining length */
|
||||
if (QCLOUD_ERR_SUCCESS != rc) {
|
||||
QCLOUD_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
curdata += read_bytes_len;
|
||||
enddata = curdata + decoded_len;
|
||||
|
||||
/* do we have enough data to read the protocol version byte? */
|
||||
if (mqtt_common_packet_read_string(topic, topic_len, &curdata, enddata) != QCLOUD_ERR_SUCCESS ||
|
||||
enddata - curdata < 0) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
|
||||
}
|
||||
|
||||
if (*qos > MQTT_QOS0) {
|
||||
*packet_id = mqtt_common_packet_read_dbyte(&curdata);
|
||||
}
|
||||
|
||||
*payload_len = enddata - curdata;
|
||||
*payload = curdata;
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes a puback packet into the supplied buffer.
|
||||
* @param buf the buffer into which the packet will be serialized
|
||||
* @param buflen the length in bytes of the supplied buffer
|
||||
* @param packetid integer - the MQTT packet identifier
|
||||
* @return serialized length, or error if 0
|
||||
*/
|
||||
__QCLOUD_INTERNAL__ qcloud_err_t mqtt_common_serialize_puback_packet(uint8_t *buf,
|
||||
size_t buf_len,
|
||||
uint16_t packet_id,
|
||||
uint32_t *serialized_len)
|
||||
{
|
||||
return mqtt_common_serialize_ack_packet(buf, buf_len, MQTT_PACKET_TYPE_PUBACK, 0, packet_id, serialized_len);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes a pubrel packet into the supplied buffer.
|
||||
* @param buf the buffer into which the packet will be serialized
|
||||
* @param buflen the length in bytes of the supplied buffer
|
||||
* @param dup integer - the MQTT dup flag
|
||||
* @param packetid integer - the MQTT packet identifier
|
||||
* @return serialized length, or error if 0
|
||||
*/
|
||||
__QCLOUD_INTERNAL__ qcloud_err_t mqtt_common_serialize_pubrel_packet(uint8_t *buf,
|
||||
size_t buf_len,
|
||||
uint8_t dup,
|
||||
uint16_t packet_id,
|
||||
uint32_t *serialized_len)
|
||||
{
|
||||
return mqtt_common_serialize_ack_packet(buf, buf_len, MQTT_PACKET_TYPE_PUBREL, dup, packet_id, serialized_len);
|
||||
}
|
||||
|
||||
__QCLOUD_INTERNAL__ qcloud_err_t mqtt_common_serialize_pubrec_packet(uint8_t *buf,
|
||||
size_t buf_len,
|
||||
uint16_t packet_id,
|
||||
uint32_t *serialized_len)
|
||||
{
|
||||
return mqtt_common_serialize_ack_packet(buf, buf_len, MQTT_PACKET_TYPE_PUBREC, 0, packet_id, serialized_len);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
__QCLOUD_INTERNAL__ qcloud_err_t mqtt_common_serialize_publish_packet(uint8_t *buf,
|
||||
size_t buf_len,
|
||||
uint8_t dup,
|
||||
int qos,
|
||||
uint8_t retained,
|
||||
uint16_t packet_id,
|
||||
char *topic,
|
||||
uint8_t *payload,
|
||||
size_t payload_len,
|
||||
uint32_t *serialized_len)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
QCLOUD_POINTER_SANITY_CHECK(buf, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(serialized_len, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(payload, QCLOUD_ERR_INVAL);
|
||||
|
||||
uint8_t *ptr = buf;
|
||||
mqtt_header_t header = {0};
|
||||
int rem_len = 0;
|
||||
|
||||
rem_len = mqtt_common_serialize_publish_packet_length(qos, topic, payload_len);
|
||||
if (mqtt_common_packet_length_get(rem_len) > buf_len) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_BUF_TOO_SHORT);
|
||||
}
|
||||
|
||||
header.bits.type = MQTT_PACKET_TYPE_PUBLISH;
|
||||
header.bits.dup = dup;
|
||||
header.bits.qos = qos;
|
||||
header.bits.retain = retained;
|
||||
|
||||
mqtt_common_packet_write_byte(&ptr, header.byte); /* write header */
|
||||
|
||||
ptr += mqtt_common_packet_encode(ptr, rem_len); /* write remaining length */;
|
||||
|
||||
mqtt_common_packet_write_string(&ptr, topic); /* Variable Header: Topic Name */
|
||||
|
||||
if (qos > MQTT_QOS0) {
|
||||
mqtt_common_packet_write_dbyte(&ptr, packet_id); /* Variable Header: Topic Name */
|
||||
}
|
||||
|
||||
memcpy(ptr, payload, payload_len);
|
||||
ptr += payload_len;
|
||||
|
||||
*serialized_len = ptr - buf;
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
__QCLOUD_INTERNAL__ qcloud_err_t mqtt_common_serialize_subscribe_packet(uint8_t *buf,
|
||||
size_t buf_len,
|
||||
uint8_t dup,
|
||||
uint16_t packet_id,
|
||||
uint32_t count,
|
||||
char *topic_filters[],
|
||||
int requested_qoss[],
|
||||
uint32_t *serialized_len)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
QCLOUD_POINTER_SANITY_CHECK(buf, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(serialized_len, QCLOUD_ERR_INVAL);
|
||||
|
||||
int i = 0;
|
||||
uint8_t *ptr = buf;
|
||||
mqtt_header_t header = {0};
|
||||
uint32_t rem_len = 0;
|
||||
|
||||
// SUBSCRIBE报文的剩余长度 = 报文标识符(2 byte) + count * (长度字段(2 byte) + topicLen + qos(1 byte))
|
||||
rem_len = mqtt_common_serialize_subscribe_packet_length(count, topic_filters);
|
||||
if (mqtt_common_packet_length_get(rem_len) > buf_len) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_BUF_TOO_SHORT);
|
||||
}
|
||||
|
||||
// 初始化报文头部
|
||||
header.byte = 0;
|
||||
header.bits.type = MQTT_PACKET_TYPE_SUBSCRIBE;
|
||||
header.bits.dup = dup;
|
||||
header.bits.qos = MQTT_QOS1;
|
||||
// 写报文固定头部第一个字节
|
||||
mqtt_common_packet_write_byte(&ptr, header.byte);
|
||||
|
||||
// 写报文固定头部剩余长度字段
|
||||
ptr += mqtt_common_packet_encode(ptr, rem_len);
|
||||
|
||||
// 写可变头部: 报文标识符
|
||||
mqtt_common_packet_write_dbyte(&ptr, packet_id);
|
||||
|
||||
// 写报文的负载部分数据
|
||||
for (i = 0; i < count; ++i) {
|
||||
mqtt_common_packet_write_string(&ptr, topic_filters[i]);
|
||||
mqtt_common_packet_write_byte(&ptr, (uint8_t)requested_qoss[i]);
|
||||
}
|
||||
|
||||
*serialized_len = ptr - buf;
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
__QCLOUD_INTERNAL__ qcloud_err_t mqtt_common_serialize_unsubscribe_packet(uint8_t *buf, size_t buf_len,
|
||||
uint8_t dup, uint16_t packet_id,
|
||||
uint32_t count, char *topic_filters[],
|
||||
uint32_t *serialized_len)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
QCLOUD_POINTER_SANITY_CHECK(buf, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(serialized_len, QCLOUD_ERR_INVAL);
|
||||
|
||||
int i = 0;
|
||||
uint8_t *ptr = buf;
|
||||
mqtt_header_t header = {0};
|
||||
uint32_t rem_len = 0;
|
||||
|
||||
rem_len = mqtt_common_serialize_unsubscribe_packet_length(count, topic_filters);
|
||||
if (mqtt_common_packet_length_get(rem_len) > buf_len) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_BUF_TOO_SHORT);
|
||||
}
|
||||
|
||||
header.byte = 0;
|
||||
header.bits.type = MQTT_PACKET_TYPE_UNSUBSCRIBE;
|
||||
header.bits.dup = dup;
|
||||
header.bits.qos = MQTT_QOS1;
|
||||
mqtt_common_packet_write_byte(&ptr, header.byte); /* write header */
|
||||
|
||||
ptr += mqtt_common_packet_encode(ptr, rem_len); /* write remaining length */
|
||||
|
||||
mqtt_common_packet_write_dbyte(&ptr, packet_id);
|
||||
|
||||
for (i = 0; i < count; ++i) {
|
||||
mqtt_common_packet_write_string(&ptr, topic_filters[i]);
|
||||
}
|
||||
|
||||
*serialized_len = ptr - buf;
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
|
||||
}
|
||||
|
@@ -0,0 +1,153 @@
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "qcloud.h"
|
||||
|
||||
__QCLOUD_STATIC__ qcloud_err_t mqtt_connack2errno(uint8_t connack_rc)
|
||||
{
|
||||
switch (connack_rc) {
|
||||
case MQTT_CONNACK_CONNECTION_ACCEPTED:
|
||||
return QCLOUD_ERR_MQTT_CONNACK_CONNECTION_ACCEPTED;
|
||||
|
||||
case MQTT_CONANCK_UNACCEPTABLE_PROTOCOL_VERSION_ERROR:
|
||||
return QCLOUD_ERR_MQTT_CONANCK_UNACCEPTABLE_PROTOCOL_VERSION;
|
||||
|
||||
case MQTT_CONNACK_IDENTIFIER_REJECTED_ERROR:
|
||||
return QCLOUD_ERR_MQTT_CONNACK_IDENTIFIER_REJECTED;
|
||||
|
||||
case MQTT_CONNACK_SERVER_UNAVAILABLE_ERROR:
|
||||
return QCLOUD_ERR_MQTT_CONNACK_SERVER_UNAVAILABLE;
|
||||
|
||||
case MQTT_CONNACK_BAD_USERDATA_ERROR:
|
||||
return QCLOUD_ERR_MQTT_CONNACK_BAD_USERDATA;
|
||||
|
||||
case MQTT_CONNACK_NOT_AUTHORIZED_ERROR:
|
||||
return QCLOUD_ERR_MQTT_CONNACK_NOT_AUTHORIZED;
|
||||
|
||||
default:
|
||||
return QCLOUD_ERR_MQTT_CONNACK_UNKNOWN;
|
||||
}
|
||||
}
|
||||
__QCLOUD_API__ qcloud_err_t qcloud_mqtt_connect(qcloud_mqtt_client_t *client, mqtt_connect_opt_t *connect_opt)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(connect_opt, QCLOUD_ERR_INVAL);
|
||||
|
||||
osal_timer_t timer;
|
||||
uint32_t serialized_len = 0;
|
||||
uint8_t connack_rc = 0, session_present = 0;
|
||||
qcloud_err_t rc = QCLOUD_ERR_FAILURE;
|
||||
|
||||
osal_timer_init(&timer);
|
||||
osal_timer_countdown_ms(&timer, client->command_timeout);
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC_IF_NOT(rc = client->network.connect(&(client->network)), QCLOUD_ERR_SUCCESS, rc);
|
||||
|
||||
osal_mutex_lock(client->tx_lock);
|
||||
|
||||
// 序列化CONNECT报文
|
||||
rc = mqtt_common_serialize_connect_packet(client->tx_buffer, sizeof(client->tx_buffer), connect_opt, &serialized_len);
|
||||
if (rc != QCLOUD_ERR_SUCCESS || serialized_len == 0) {
|
||||
osal_mutex_unlock(client->tx_lock);
|
||||
goto errout;
|
||||
}
|
||||
|
||||
// 发送CONNECT报文
|
||||
rc = mqtt_glue_packet_send(client, serialized_len, &timer);
|
||||
if (QCLOUD_ERR_SUCCESS != rc) {
|
||||
osal_mutex_unlock(client->tx_lock);
|
||||
goto errout;
|
||||
}
|
||||
|
||||
osal_mutex_unlock(client->tx_lock);
|
||||
|
||||
// 阻塞等待CONNACK的报文,
|
||||
rc = mqtt_glue_spin4ack(client, &timer, (uint8_t)MQTT_PACKET_TYPE_CONNACK);
|
||||
if (QCLOUD_ERR_SUCCESS != rc) {
|
||||
goto errout;
|
||||
}
|
||||
|
||||
// 反序列化CONNACK包, 检查返回码
|
||||
rc = mqtt_common_deserialize_connack_packet(&session_present, &connack_rc, client->rx_buffer, sizeof(client->rx_buffer));
|
||||
if (QCLOUD_ERR_SUCCESS != rc) {
|
||||
goto errout;
|
||||
}
|
||||
|
||||
rc = mqtt_connack2errno(connack_rc);
|
||||
if (rc != QCLOUD_ERR_MQTT_CONNACK_CONNECTION_ACCEPTED) {
|
||||
goto errout;
|
||||
}
|
||||
|
||||
mqtt_client_connection_state_set(client, QCLOUD_MQTT_CONNECTION_STATE_CONNECTED);
|
||||
|
||||
client->keep_alive_interval = connect_opt->keep_alive_interval;
|
||||
|
||||
osal_mutex_lock(client->global_lock);
|
||||
client->is_manually_disconnected = QCLOUD_FALSE;
|
||||
client->ping_outstanding = 0;
|
||||
osal_timer_countdown(&client->ping_timer, client->keep_alive_interval);
|
||||
osal_mutex_unlock(client->global_lock);
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
|
||||
|
||||
errout:
|
||||
client->network.disconnect(&client->network);
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
__QCLOUD_API__ qcloud_err_t qcloud_mqtt_disconnect(qcloud_mqtt_client_t *client)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
|
||||
qcloud_err_t rc;
|
||||
osal_timer_t timer;
|
||||
uint32_t serialized_len = 0;
|
||||
|
||||
if (!qcloud_mqtt_client_is_connected(client)) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_NO_CONN);
|
||||
}
|
||||
|
||||
mqtt_glue_msg_handler_list_destroy(client);
|
||||
|
||||
// 1. 组disconnect包
|
||||
osal_mutex_lock(client->tx_lock);
|
||||
|
||||
rc = mqtt_common_serialize_zero_payload_packet(client->tx_buffer, sizeof(client->tx_buffer), MQTT_PACKET_TYPE_DISCONNECT, &serialized_len);
|
||||
if (rc != QCLOUD_ERR_SUCCESS) {
|
||||
osal_mutex_unlock(client->tx_lock);
|
||||
QCLOUD_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
osal_timer_init(&timer);
|
||||
osal_timer_countdown_ms(&timer, client->command_timeout);
|
||||
|
||||
// 2. 发送disconnect包
|
||||
if (serialized_len > 0) {
|
||||
rc = mqtt_glue_packet_send(client, serialized_len, &timer);
|
||||
if (rc != QCLOUD_ERR_SUCCESS) {
|
||||
osal_mutex_unlock(client->tx_lock);
|
||||
QCLOUD_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
}
|
||||
osal_mutex_unlock(client->tx_lock);
|
||||
|
||||
// 3. 断开底层TCP连接, 并修改相关标识位
|
||||
client->network.disconnect(&(client->network));
|
||||
mqtt_client_connection_state_set(client, QCLOUD_MQTT_CONNECTION_STATE_DISCONNECTED);
|
||||
client->is_manually_disconnected = QCLOUD_TRUE;
|
||||
|
||||
QCLOUD_LOG_I("mqtt disconnect!");
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,65 @@
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "qcloud.h"
|
||||
|
||||
__QCLOUD_API__ qcloud_err_t qcloud_mqtt_publish(qcloud_mqtt_client_t *client, char *topic, mqtt_publish_opt_t *publish_opt)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(publish_opt, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_STRING_PTR_SANITY_CHECK(topic, QCLOUD_ERR_INVAL);
|
||||
|
||||
qcloud_err_t rc;
|
||||
osal_timer_t timer;
|
||||
uint32_t len = 0;
|
||||
size_t topic_len = 0;
|
||||
|
||||
topic_len = strlen(topic);
|
||||
if (topic_len > QCLOUD_MQTT_TOPIC_SIZE_MAX) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_MAX_TOPIC_LENGTH);
|
||||
}
|
||||
|
||||
if (publish_opt->qos == MQTT_QOS2) {
|
||||
QCLOUD_LOG_E("QoS2 isn't supported yet");
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_QOS_NOT_SUPPORT);
|
||||
}
|
||||
|
||||
if (!qcloud_mqtt_client_is_connected(client)) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_NO_CONN);
|
||||
}
|
||||
|
||||
osal_timer_init(&timer);
|
||||
osal_timer_countdown_ms(&timer, client->command_timeout);
|
||||
|
||||
osal_mutex_lock(client->tx_lock);
|
||||
|
||||
if (publish_opt->qos == MQTT_QOS1) {
|
||||
publish_opt->id = mqtt_glue_packet_id_generate(client);
|
||||
}
|
||||
QCLOUD_LOG_D("publish topic seq=%d|name=%s|payload=%s", publish_opt->id, topic, (char *)publish_opt->payload);
|
||||
|
||||
rc = mqtt_common_serialize_publish_packet(client->tx_buffer, sizeof(client->tx_buffer),
|
||||
0, publish_opt->qos, publish_opt->retained, publish_opt->id,
|
||||
topic, (uint8_t *) publish_opt->payload, publish_opt->payload_len, &len);
|
||||
if (QCLOUD_ERR_SUCCESS != rc) {
|
||||
osal_mutex_unlock(client->tx_lock);
|
||||
QCLOUD_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
rc = mqtt_glue_packet_send(client, len, &timer);
|
||||
if (rc == QCLOUD_ERR_SUCCESS && publish_opt->qos > MQTT_QOS0) {
|
||||
mqtt_glue_ack_list_record(client, QCLOUD_MQTT_ACK_TYPE_PUBACK, NULL, publish_opt->id, len);
|
||||
}
|
||||
|
||||
osal_mutex_unlock(client->tx_lock);
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@@ -0,0 +1,89 @@
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "qcloud.h"
|
||||
|
||||
__QCLOUD_API__ qcloud_err_t qcloud_mqtt_subscribe(qcloud_mqtt_client_t *client, const char *topic_filter, mqtt_subscribe_opt_t *subscribe_opt)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(subscribe_opt, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_STRING_PTR_SANITY_CHECK(topic_filter, QCLOUD_ERR_INVAL);
|
||||
|
||||
qcloud_err_t rc;
|
||||
osal_timer_t timer;
|
||||
size_t topic_len;
|
||||
uint32_t len = 0;
|
||||
uint16_t packet_id = 0;
|
||||
char *topic_filter_mutable = NULL;
|
||||
qcloud_mqtt_msg_handler_t *msg_handler = NULL;
|
||||
|
||||
topic_len = strlen(topic_filter);
|
||||
if (topic_len > QCLOUD_MQTT_TOPIC_SIZE_MAX) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_MAX_TOPIC_LENGTH);
|
||||
}
|
||||
|
||||
if (subscribe_opt->qos == MQTT_QOS2) {
|
||||
QCLOUD_LOG_E("QoS2 not supported yet");
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_QOS_NOT_SUPPORT);
|
||||
}
|
||||
|
||||
if (!qcloud_mqtt_client_is_connected(client)) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_NO_CONN)
|
||||
}
|
||||
|
||||
/* topic filter should be valid in the whole sub life */
|
||||
topic_filter_mutable = mqtt_glue_string_const2mutable(topic_filter, topic_len);
|
||||
if (!topic_filter_mutable) {
|
||||
QCLOUD_LOG_E("malloc failed");
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
|
||||
}
|
||||
|
||||
osal_timer_init(&timer);
|
||||
osal_timer_countdown_ms(&timer, client->command_timeout);
|
||||
|
||||
osal_mutex_lock(client->tx_lock);
|
||||
// 序列化SUBSCRIBE报文
|
||||
packet_id = mqtt_glue_packet_id_generate(client);
|
||||
QCLOUD_LOG_D("topic name=%s|packet id=%d|private data=%s", topic_filter_mutable, packet_id, (char *)subscribe_opt->private_data);
|
||||
|
||||
rc = mqtt_common_serialize_subscribe_packet(client->tx_buffer, sizeof(client->tx_buffer),
|
||||
0, packet_id, 1, &topic_filter_mutable,
|
||||
(int *)&subscribe_opt->qos, &len);
|
||||
if (QCLOUD_ERR_SUCCESS != rc) {
|
||||
osal_mutex_unlock(client->tx_lock);
|
||||
mqtt_glue_string_mutable_free(topic_filter_mutable);
|
||||
QCLOUD_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
// 发送SUBSCRIBE报文
|
||||
rc = mqtt_glue_packet_send(client, len, &timer);
|
||||
if (QCLOUD_ERR_SUCCESS != rc) {
|
||||
osal_mutex_unlock(client->tx_lock);
|
||||
mqtt_glue_string_mutable_free(topic_filter_mutable);
|
||||
QCLOUD_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
msg_handler = mqtt_glue_msg_handler_create(topic_filter_mutable, subscribe_opt);
|
||||
if (!msg_handler) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
|
||||
}
|
||||
|
||||
rc = mqtt_glue_ack_list_record(client, QCLOUD_MQTT_ACK_TYPE_SUBACK, msg_handler, packet_id, len);
|
||||
if (QCLOUD_ERR_SUCCESS != rc) {
|
||||
osal_mutex_unlock(client->tx_lock);
|
||||
mqtt_glue_msg_handler_destory(msg_handler);
|
||||
QCLOUD_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
osal_mutex_unlock(client->tx_lock);
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@@ -0,0 +1,83 @@
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "qcloud.h"
|
||||
|
||||
__QCLOUD_API__ qcloud_err_t qcloud_mqtt_unsubscribe(qcloud_mqtt_client_t *client, const char *topic_filter)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_STRING_PTR_SANITY_CHECK(topic_filter, QCLOUD_ERR_INVAL);
|
||||
|
||||
qcloud_err_t rc;
|
||||
osal_timer_t timer;
|
||||
size_t topic_len = 0;
|
||||
uint32_t len = 0;
|
||||
uint16_t packet_id = 0;
|
||||
int is_subscribed = QCLOUD_FALSE;
|
||||
char *topic_filter_mutable = NULL;
|
||||
|
||||
topic_len = strlen(topic_filter);
|
||||
if (topic_len > QCLOUD_MQTT_TOPIC_SIZE_MAX) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_MAX_TOPIC_LENGTH);
|
||||
}
|
||||
|
||||
mqtt_glue_msg_handler_uninstall(client, topic_filter, &is_subscribed);
|
||||
|
||||
if (!is_subscribed) {
|
||||
QCLOUD_LOG_E("not subscribed: %s", topic_filter);
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_UNSUB_FAIL);
|
||||
}
|
||||
|
||||
if (!qcloud_mqtt_client_is_connected(client)) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_NO_CONN);
|
||||
}
|
||||
|
||||
/* topic filter should be valid in the whole sub life */
|
||||
topic_filter_mutable = mqtt_glue_string_const2mutable(topic_filter, topic_len);
|
||||
if (!topic_filter_mutable) {
|
||||
QCLOUD_LOG_E("malloc failed");
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
|
||||
}
|
||||
|
||||
osal_timer_init(&timer);
|
||||
osal_timer_countdown_ms(&timer, client->command_timeout);
|
||||
|
||||
osal_mutex_lock(client->tx_lock);
|
||||
|
||||
packet_id = mqtt_glue_packet_id_generate(client);
|
||||
rc = mqtt_common_serialize_unsubscribe_packet(client->tx_buffer, sizeof(client->tx_buffer),
|
||||
0, packet_id, 1, &topic_filter_mutable, &len);
|
||||
|
||||
mqtt_glue_string_mutable_free(topic_filter_mutable);
|
||||
|
||||
if (QCLOUD_ERR_SUCCESS != rc) {
|
||||
osal_mutex_unlock(client->tx_lock);
|
||||
QCLOUD_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
/* send the unsubscribe packet */
|
||||
rc = mqtt_glue_packet_send(client, len, &timer);
|
||||
if (QCLOUD_ERR_SUCCESS != rc) {
|
||||
osal_mutex_unlock(client->tx_lock);
|
||||
QCLOUD_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
rc = mqtt_glue_ack_list_record(client, QCLOUD_MQTT_ACK_TYPE_UNSUBACK, NULL, packet_id, len);
|
||||
if (QCLOUD_ERR_SUCCESS != rc) {
|
||||
QCLOUD_LOG_E("push publish into to pubInfolist failed: %d", rc);
|
||||
osal_mutex_unlock(client->tx_lock);
|
||||
QCLOUD_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
osal_mutex_unlock(client->tx_lock);
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@@ -0,0 +1,286 @@
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "qcloud.h"
|
||||
|
||||
__QCLOUD_STATIC__ qcloud_err_t mqtt_yield_resubscribe(qcloud_mqtt_client_t *client)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
|
||||
qcloud_err_t rc;
|
||||
char *topic = NULL;
|
||||
mqtt_subscribe_opt_t subscribe_opt;
|
||||
qcloud_list_t *curr, *next;
|
||||
qcloud_mqtt_msg_handler_t *msg_handler;
|
||||
|
||||
if (!qcloud_mqtt_client_is_connected(client)) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_NO_CONN);
|
||||
}
|
||||
|
||||
if (qcloud_list_empty(&client->msg_handler_list)) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
|
||||
}
|
||||
|
||||
osal_mutex_lock(client->msg_handler_list_lock);
|
||||
|
||||
QCLOUD_LIST_FOR_EACH_SAFE(curr, next, &client->msg_handler_list) {
|
||||
msg_handler = QCLOUD_LIST_ENTRY(curr, qcloud_mqtt_msg_handler_t, list);
|
||||
|
||||
subscribe_opt.message_handler = msg_handler->handler;
|
||||
subscribe_opt.qos = msg_handler->qos;
|
||||
subscribe_opt.private_data = msg_handler->private_data;
|
||||
|
||||
rc = qcloud_mqtt_subscribe(client, msg_handler->topic_filter_mutable, &subscribe_opt);
|
||||
if (rc != QCLOUD_ERR_SUCCESS) {
|
||||
osal_mutex_unlock(client->msg_handler_list_lock);
|
||||
QCLOUD_LOG_E("resubscribe failed %d, topic: %s", rc, topic);
|
||||
QCLOUD_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
}
|
||||
|
||||
osal_mutex_unlock(client->msg_handler_list_lock);
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ qcloud_err_t mqtt_yield_do_reconnect(qcloud_mqtt_client_t *client, mqtt_connect_opt_t *connect_opt)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(connect_opt, QCLOUD_ERR_INVAL);
|
||||
|
||||
qcloud_err_t rc;
|
||||
|
||||
QCLOUD_LOG_I("attempt to reconnect...");
|
||||
|
||||
if (qcloud_mqtt_client_is_connected(client)) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_ALREADY_CONNECTED);
|
||||
}
|
||||
|
||||
rc = qcloud_mqtt_connect(client, connect_opt);
|
||||
|
||||
if (!qcloud_mqtt_client_is_connected(client)) {
|
||||
QCLOUD_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
rc = mqtt_yield_resubscribe(client);
|
||||
if (rc != QCLOUD_ERR_SUCCESS) {
|
||||
QCLOUD_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_RECONNECTED);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 处理非手动断开连接的情况
|
||||
*
|
||||
* @param client
|
||||
* @return
|
||||
*/
|
||||
__QCLOUD_STATIC__ qcloud_err_t mqtt_yield_try_disconnect(qcloud_mqtt_client_t *client)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
qcloud_err_t rc;
|
||||
|
||||
if (!qcloud_mqtt_client_is_connected(client)) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_NO_CONN);
|
||||
}
|
||||
|
||||
rc = qcloud_mqtt_disconnect(client);
|
||||
// 若断开连接失败, 强制断开底层TLS层连接
|
||||
if (rc != QCLOUD_ERR_SUCCESS) {
|
||||
client->network.disconnect(&(client->network));
|
||||
mqtt_client_connection_state_set(client, QCLOUD_MQTT_CONNECTION_STATE_DISCONNECTED);
|
||||
}
|
||||
|
||||
mqtt_glue_callback_involve(client, MQTT_EVENT_DISCONNECT, NULL);
|
||||
|
||||
// 非手动断开连接
|
||||
client->is_manually_disconnected = QCLOUD_FALSE;
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_NO_CONN);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 处理自动重连的相关逻辑
|
||||
*
|
||||
* @param client
|
||||
* @return
|
||||
*/
|
||||
__QCLOUD_STATIC__ qcloud_err_t mqtt_yield_try_reconnect(qcloud_mqtt_client_t *client, mqtt_connect_opt_t *connect_opt)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
qcloud_err_t rc = QCLOUD_ERR_MQTT_RECONNECTED;
|
||||
|
||||
// 自动重连等待时间还未过期, 还未到重连的时候, 返回正在进行重连
|
||||
if (!osal_timer_is_expired(&client->reconnect_timer)) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_ATTEMPTING_RECONNECT);
|
||||
}
|
||||
|
||||
rc = mqtt_yield_do_reconnect(client, connect_opt);
|
||||
if (rc == QCLOUD_ERR_MQTT_RECONNECTED) {
|
||||
QCLOUD_LOG_E("reconnect success");
|
||||
mqtt_glue_callback_involve(client, MQTT_EVENT_RECONNECT, NULL);
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(rc);
|
||||
} else {
|
||||
QCLOUD_LOG_E("attempt reconnect failed %d", rc);
|
||||
rc = QCLOUD_ERR_MQTT_ATTEMPTING_RECONNECT;
|
||||
}
|
||||
|
||||
client->reconnect_try_duration *= 2;
|
||||
|
||||
if (client->reconnect_try_duration > QCLOUD_MQTT_RECONNECT_TRY_THRESHOLD) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_RECONNECT_TIMEOUT);
|
||||
}
|
||||
osal_timer_countdown_ms(&client->reconnect_timer, client->reconnect_try_duration);
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 处理与服务器维持心跳的相关逻辑
|
||||
*
|
||||
* @param client
|
||||
* @return
|
||||
*/
|
||||
__QCLOUD_STATIC__ qcloud_err_t mqtt_yield_keep_alive(qcloud_mqtt_client_t *client, mqtt_connect_opt_t *connect_opt)
|
||||
{
|
||||
#define MQTT_PING_RETRY_TIMES 2
|
||||
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
int try = 0;
|
||||
qcloud_err_t rc;
|
||||
osal_timer_t timer;
|
||||
uint32_t serialized_len = 0;
|
||||
|
||||
if (!connect_opt->keep_alive_interval) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
|
||||
}
|
||||
|
||||
if (!osal_timer_is_expired(&client->ping_timer)) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
|
||||
}
|
||||
|
||||
if (client->ping_outstanding >= MQTT_PING_RETRY_TIMES) {
|
||||
// reaching here means we haven't received any MQTT packet for a long time(keep_alive_interval)
|
||||
QCLOUD_LOG_E("fail to recv MQTT msg.");
|
||||
QCLOUD_FUNC_EXIT_RC(mqtt_yield_try_disconnect(client));
|
||||
}
|
||||
|
||||
/* there is no ping outstanding - send one */
|
||||
osal_mutex_lock(client->tx_lock);
|
||||
rc = mqtt_common_serialize_zero_payload_packet(client->tx_buffer, sizeof(client->tx_buffer), MQTT_PACKET_TYPE_PINGREQ, &serialized_len);
|
||||
if (QCLOUD_ERR_SUCCESS != rc) {
|
||||
osal_mutex_unlock(client->tx_lock);
|
||||
QCLOUD_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
/* send the ping packet */
|
||||
osal_timer_init(&timer);
|
||||
do {
|
||||
osal_timer_countdown_ms(&timer, client->command_timeout);
|
||||
rc = mqtt_glue_packet_send(client, serialized_len, &timer);
|
||||
} while (QCLOUD_ERR_SUCCESS != rc && (try++ < 3));
|
||||
|
||||
if (QCLOUD_ERR_SUCCESS != rc) {
|
||||
osal_mutex_unlock(client->tx_lock);
|
||||
// if send a PING fails, propably the connection is not OK and we decide to disconnect and begin reconnection attempts
|
||||
QCLOUD_LOG_E("fail to send PING request.");
|
||||
rc = mqtt_yield_try_disconnect(client);
|
||||
QCLOUD_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
osal_mutex_unlock(client->tx_lock);
|
||||
|
||||
osal_mutex_lock(client->global_lock);
|
||||
++client->ping_outstanding;
|
||||
|
||||
/* start a timer to wait for PINGRESP from server */
|
||||
osal_timer_countdown(&client->ping_timer, QCLOUD_MIN(5, connect_opt->keep_alive_interval / 2));
|
||||
osal_mutex_unlock(client->global_lock);
|
||||
|
||||
QCLOUD_LOG_D("PING request %u sent.", client->ping_outstanding);
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
|
||||
}
|
||||
|
||||
__QCLOUD_API__ qcloud_err_t qcloud_mqtt_yield(qcloud_mqtt_client_t *client, mqtt_connect_opt_t *connect_opt, uint32_t timeout_ms)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_NUMBERIC_SANITY_CHECK(timeout_ms, QCLOUD_ERR_INVAL);
|
||||
|
||||
qcloud_err_t rc = QCLOUD_ERR_SUCCESS;
|
||||
osal_timer_t timer;
|
||||
uint8_t packet_type;
|
||||
|
||||
// 1. 检查连接是否已经手动断开
|
||||
if (!qcloud_mqtt_client_is_connected(client)) {
|
||||
if (client->is_manually_disconnected) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_MANUALLY_DISCONNECTED);
|
||||
} else if (client->auto_connect_state != QCLOUD_AUTO_CONN_STATE_ENABLED) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_NO_CONN);
|
||||
}
|
||||
}
|
||||
|
||||
osal_timer_init(&timer);
|
||||
osal_timer_countdown_ms(&timer, timeout_ms);
|
||||
|
||||
// 3. 循环读取消息以及心跳包管理
|
||||
while (!osal_timer_is_expired(&timer)) {
|
||||
if (!qcloud_mqtt_client_is_connected(client)) {
|
||||
if (client->reconnect_try_duration > QCLOUD_MQTT_RECONNECT_TRY_THRESHOLD) {
|
||||
rc = QCLOUD_ERR_MQTT_RECONNECT_TIMEOUT;
|
||||
break;
|
||||
}
|
||||
|
||||
rc = mqtt_yield_try_reconnect(client, connect_opt);
|
||||
continue;
|
||||
}
|
||||
|
||||
rc = mqtt_glue_spin(client, &timer, &packet_type);
|
||||
|
||||
if (rc == QCLOUD_ERR_SUCCESS) {
|
||||
/* check list of ACK pend list to remove node that is timeout */
|
||||
mqtt_glue_ack_list_scan(client);
|
||||
|
||||
rc = mqtt_yield_keep_alive(client, connect_opt);
|
||||
} 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) {
|
||||
QCLOUD_LOG_E("network failed, MQTT disconnect %d", rc);
|
||||
rc = mqtt_yield_try_disconnect(client);
|
||||
}
|
||||
|
||||
if (rc == QCLOUD_ERR_MQTT_NO_CONN) {
|
||||
++client->network_disconnect_counter;
|
||||
|
||||
if (client->auto_connect_state != QCLOUD_AUTO_CONN_STATE_ENABLED) {
|
||||
break;
|
||||
}
|
||||
|
||||
client->reconnect_try_duration = QCLOUD_MQTT_RECONNECT_TRY_INITIAL;
|
||||
osal_timer_countdown_ms(&client->reconnect_timer, client->reconnect_try_duration);
|
||||
|
||||
// 如果超时时间到了,则会直接返回
|
||||
rc = QCLOUD_ERR_MQTT_ATTEMPTING_RECONNECT;
|
||||
} else if (rc != QCLOUD_ERR_SUCCESS) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@@ -0,0 +1,398 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright (C) 2016 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 "qcloud.h"
|
||||
|
||||
#include "mbedtls/net_sockets.h"
|
||||
#include "mbedtls/debug.h"
|
||||
#include "mbedtls/ssl.h"
|
||||
#include "mbedtls/entropy.h"
|
||||
#include "mbedtls/ctr_drbg.h"
|
||||
#include "mbedtls/error.h"
|
||||
#include "mbedtls/certs.h"
|
||||
#include "mbedtls/timing.h"
|
||||
#include "mbedtls/ssl_cookie.h"
|
||||
#include "mbedtls/debug.h"
|
||||
|
||||
#define DEBUG_LEVEL 0
|
||||
|
||||
void mbedtls_dtls_net_init(mbedtls_net_context *ctx);
|
||||
int mbedtls_dtls_net_connect(mbedtls_net_context *ctx, const char *host, const char *port, int proto);
|
||||
void mbedtls_dtls_net_usleep(unsigned long usec);
|
||||
int mbedtls_dtls_net_recv(void *ctx, unsigned char *buf, size_t len);
|
||||
int mbedtls_dtls_net_recv_timeout(void *ctx, unsigned char *buf, size_t len, uint32_t timeout);
|
||||
int mbedtls_dtls_net_send(void *ctx, const unsigned char *buf, size_t len);
|
||||
void mbedtls_dtls_net_free(mbedtls_net_context *ctx);
|
||||
|
||||
#if (QCLOUD_CFG_AUTH_MODE == QCLOUD_AUTH_MODE_KEY)
|
||||
static const int ciphersuites[] = { MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA, MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA, 0 };
|
||||
#endif
|
||||
|
||||
typedef struct dtls_paramter_st {
|
||||
mbedtls_net_context socket_fd; // socket文件描述符
|
||||
mbedtls_entropy_context entropy; // 保存熵配置
|
||||
mbedtls_ctr_drbg_context ctr_drbg; // 随机数生成器
|
||||
mbedtls_ssl_context ssl; // 保存SSL基本数据
|
||||
mbedtls_ssl_config ssl_conf; // TSL/TLS配置信息
|
||||
|
||||
#if defined(MBEDTLS_X509_CRT_PARSE_C)
|
||||
mbedtls_x509_crt ca_cert; // ca证书信息
|
||||
mbedtls_x509_crt client_cert; // 客户端证书信息
|
||||
#endif
|
||||
mbedtls_pk_context private_key; // 客户端私钥信息
|
||||
|
||||
mbedtls_timing_delay_context timer;
|
||||
mbedtls_ssl_cookie_ctx cookie_ctx;
|
||||
} dtls_param_t;
|
||||
|
||||
/**
|
||||
* @brief 释放mbedtls开辟的内存
|
||||
*/
|
||||
static void mbedtls_dtls_param_free(dtls_param_t *dtls_param)
|
||||
{
|
||||
mbedtls_dtls_net_free(&dtls_param->socket_fd);
|
||||
#if defined(MBEDTLS_X509_CRT_PARSE_C)
|
||||
mbedtls_x509_crt_free(&dtls_param->client_cert);
|
||||
mbedtls_x509_crt_free(&dtls_param->ca_cert);
|
||||
mbedtls_pk_free(&dtls_param->private_key);
|
||||
#endif
|
||||
mbedtls_ssl_free(&dtls_param->ssl);
|
||||
mbedtls_ssl_config_free(&dtls_param->ssl_conf);
|
||||
mbedtls_ctr_drbg_free(&dtls_param->ctr_drbg);
|
||||
mbedtls_entropy_free(&dtls_param->entropy);
|
||||
#if 0
|
||||
mbedtls_ssl_cookie_free(&(dtls_param->cookie_ctx));
|
||||
#endif
|
||||
|
||||
osal_free(dtls_param);
|
||||
}
|
||||
|
||||
static void mbedtls_debug(void *ctx, int level, const char *file, int line, const char *str)
|
||||
{
|
||||
QCLOUD_LOG_I("[mbedTLS]:[%s]:[%d]: %s\r\n", file, line, str);
|
||||
}
|
||||
|
||||
static int mbedtls_client_init(dtls_param_t *dtls_param, qcloud_tls_opt_t *tls_opt)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
#if defined(MBEDTLS_DEBUG_C)
|
||||
mbedtls_debug_set_threshold(DEBUG_LEVEL);
|
||||
#endif
|
||||
|
||||
mbedtls_dtls_net_init(&dtls_param->socket_fd);
|
||||
mbedtls_ssl_init(&dtls_param->ssl);
|
||||
mbedtls_ssl_config_init(&dtls_param->ssl_conf);
|
||||
#if defined(MBEDTLS_X509_CRT_PARSE_C)
|
||||
mbedtls_x509_crt_init(&dtls_param->ca_cert);
|
||||
mbedtls_x509_crt_init(&dtls_param->client_cert);
|
||||
mbedtls_pk_init(&dtls_param->private_key);
|
||||
#endif
|
||||
mbedtls_ctr_drbg_init(&dtls_param->ctr_drbg);
|
||||
mbedtls_entropy_init(&dtls_param->entropy);
|
||||
|
||||
if((ret = mbedtls_ctr_drbg_seed(&dtls_param->ctr_drbg, mbedtls_entropy_func,
|
||||
&dtls_param->entropy, NULL,0)) != 0) {
|
||||
QCLOUD_LOG_E("mbedtls_ctr_drbg_seed failed returned -0x%x", -ret);
|
||||
return QCLOUD_ERR_SSL_INIT;
|
||||
}
|
||||
|
||||
mbedtls_ssl_conf_authmode(&dtls_param->ssl_conf, MBEDTLS_SSL_VERIFY_NONE );
|
||||
|
||||
#if defined(MBEDTLS_X509_CRT_PARSE_C)
|
||||
if (tls_opt->ca_cert != NULL) {
|
||||
if ((ret = mbedtls_x509_crt_parse(&dtls_param->ca_cert, (const unsigned char *)tls_opt->ca_cert,
|
||||
(tls_opt->ca_cert_len + 1)))) {
|
||||
QCLOUD_LOG_E("parse ca crt failed returned -0x%04x", -ret);
|
||||
return QCLOUD_ERR_SSL_CERT;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if (QCLOUD_CFG_AUTH_MODE == QCLOUD_AUTH_MODE_CERT)
|
||||
if (tls_opt->cert_path != NULL && tls_opt->priv_key_path != NULL) {
|
||||
#if defined(MBEDTLS_X509_CRT_PARSE_C)
|
||||
if ((ret = mbedtls_x509_crt_parse_file(&dtls_param->client_cert, tls_opt->cert_path)) != 0) {
|
||||
QCLOUD_LOG_E("load client cert file failed returned -0x%x", ret);
|
||||
return QCLOUD_ERR_SSL_CERT;
|
||||
}
|
||||
#endif
|
||||
|
||||
if ((ret = mbedtls_pk_parse_keyfile(&dtls_param->private_key, tls_opt->priv_key_path, "")) != 0) {
|
||||
QCLOUD_LOG_E("load client key file failed returned -0x%x", ret);
|
||||
return QCLOUD_ERR_SSL_CERT;
|
||||
}
|
||||
|
||||
#if defined(MBEDTLS_X509_CRT_PARSE_C)
|
||||
if (0 == ret) {
|
||||
mbedtls_ssl_conf_ca_chain(&dtls_param->ssl_conf, &dtls_param->ca_cert, NULL);
|
||||
if ((ret = mbedtls_ssl_conf_own_cert(&dtls_param->ssl_conf, &dtls_param->client_cert, &dtls_param->private_key)) != 0) {
|
||||
QCLOUD_LOG_E("mbedtls_ssl_conf_own_cert failed returned -0x%x", -ret);
|
||||
return QCLOUD_ERR_SSL_CERT;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
QCLOUD_LOG_D("cert_file/key_file is empty!|cert_file=%s|key_file=%s", tls_opt->cert_path, tls_opt->priv_key_path);
|
||||
}
|
||||
|
||||
#else
|
||||
if (tls_opt->psk != NULL && tls_opt->psk_id !=NULL) {
|
||||
const char *psk_id = tls_opt->psk_id;
|
||||
ret = mbedtls_ssl_conf_psk(&dtls_param->ssl_conf, (unsigned char *)tls_opt->psk, tls_opt->psk_len,
|
||||
(const unsigned char *)psk_id, strlen(psk_id));
|
||||
} else {
|
||||
QCLOUD_LOG_D("psk/pskid is empty!|psk=%s|psd_id=%s", tls_opt->psk, tls_opt->psk_id);
|
||||
}
|
||||
|
||||
if (0 != ret) {
|
||||
QCLOUD_LOG_E("mbedtls_ssl_conf_psk fail: -0x%x", -ret);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 建立UDP连接
|
||||
*
|
||||
* @param socket_fd Socket描述符
|
||||
* @param host 服务器主机名
|
||||
* @param port 服务器端口地址
|
||||
* @return 返回QCLOUD_ERR_SUCCESS, 表示成功
|
||||
*/
|
||||
int mbedtls_udp_connect(mbedtls_net_context *socket_fd, const char *host, int port)
|
||||
{
|
||||
int ret = 0;
|
||||
char port_str[6];
|
||||
|
||||
osal_snprintf(port_str, 6, "%d", port);
|
||||
if ((ret = mbedtls_dtls_net_connect(socket_fd, host, port_str, MBEDTLS_NET_PROTO_UDP)) != 0) {
|
||||
QCLOUD_LOG_E("mbedtls_dtls_net_connect host:%s port:%s returned -0x%04x", host, port_str, -ret);
|
||||
switch (ret) {
|
||||
case MBEDTLS_ERR_NET_SOCKET_FAILED:
|
||||
return QCLOUD_ERR_TCP_SOCKET_FAILED;
|
||||
case MBEDTLS_ERR_NET_UNKNOWN_HOST:
|
||||
return QCLOUD_ERR_TCP_UNKNOWN_HOST;
|
||||
default:
|
||||
return QCLOUD_ERR_TCP_CONNECT;
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
if ((ret = mbedtls_net_set_block(socket_fd)) != 0) {
|
||||
QCLOUD_LOG_E("set block faliled returned -0x%04x", -ret);
|
||||
return QCLOUD_ERR_TCP_CONNECT;
|
||||
}
|
||||
#endif
|
||||
|
||||
return QCLOUD_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
__QCLOUD_OSAL__ void *osal_dtls_connect(qcloud_tls_opt_t *tls_opt, const char *host, int port)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
int ret = QCLOUD_ERR_SUCCESS;
|
||||
dtls_param_t *dtls_param = NULL;
|
||||
|
||||
if (!tls_opt) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dtls_param = (dtls_param_t *)osal_malloc(sizeof(dtls_param_t));
|
||||
if (!dtls_param) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((ret = mbedtls_client_init(dtls_param, tls_opt)) != QCLOUD_ERR_SUCCESS) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
QCLOUD_LOG_D(" Connecting to /%s/%d...", host, port);
|
||||
if ((ret = mbedtls_udp_connect(&dtls_param->socket_fd, host, port)) != QCLOUD_ERR_SUCCESS) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
QCLOUD_LOG_D(" Setting up the SSL/TLS structure...");
|
||||
if ((ret = mbedtls_ssl_config_defaults(&dtls_param->ssl_conf, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_DATAGRAM,
|
||||
MBEDTLS_SSL_PRESET_DEFAULT)) != 0) {
|
||||
QCLOUD_LOG_E("mbedtls_ssl_config_defaults result 0x%04x", ret);
|
||||
goto error;
|
||||
}
|
||||
|
||||
mbedtls_ssl_conf_authmode(&dtls_param->ssl_conf, MBEDTLS_SSL_VERIFY_REQUIRED);
|
||||
|
||||
mbedtls_ssl_conf_rng(&dtls_param->ssl_conf, mbedtls_ctr_drbg_random, &dtls_param->ctr_drbg);
|
||||
mbedtls_ssl_conf_dbg(&dtls_param->ssl_conf, mbedtls_debug, NULL);
|
||||
|
||||
#if 0
|
||||
if ((ret = mbedtls_ssl_cookie_setup(&pDataParams->cookie_ctx, mbedtls_ctr_drbg_random, &pDataParams->ctr_drbg)) != 0) {
|
||||
QCLOUD_LOG_E("mbedtls_ssl_cookie_setup result 0x%04x", ret);
|
||||
goto error;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(MBEDTLS_SSL_DTLS_HELLO_VERIFY) && defined(MBEDTLS_SSL_SRV_C)
|
||||
mbedtls_ssl_conf_dtls_cookies(&dtls_param->ssl_conf, mbedtls_ssl_cookie_write, mbedtls_ssl_cookie_check, &dtls_param->cookie_ctx);
|
||||
#endif
|
||||
|
||||
#if (QCLOUD_CFG_AUTH_MODE == QCLOUD_AUTH_MODE_KEY)
|
||||
mbedtls_ssl_conf_ciphersuites(&dtls_param->ssl_conf, ciphersuites);
|
||||
#endif
|
||||
|
||||
#ifdef MBEDTLS_SSL_PROTO_DTLS
|
||||
if (dtls_param->ssl_conf.transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) {
|
||||
mbedtls_ssl_conf_min_version(&dtls_param->ssl_conf, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3);
|
||||
|
||||
mbedtls_ssl_conf_max_version(&dtls_param->ssl_conf, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3);
|
||||
|
||||
mbedtls_ssl_conf_handshake_timeout(&dtls_param->ssl_conf, (MBEDTLS_SSL_DTLS_TIMEOUT_DFL_MIN * 2),
|
||||
(MBEDTLS_SSL_DTLS_TIMEOUT_DFL_MIN * 2 * 4));
|
||||
}
|
||||
#endif
|
||||
|
||||
if ((ret = mbedtls_ssl_setup(&dtls_param->ssl, &dtls_param->ssl_conf)) != 0) {
|
||||
QCLOUD_LOG_E("mbedtls_ssl_setup failed returned -0x%x", -ret);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (dtls_param->ssl_conf.transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) {
|
||||
mbedtls_ssl_set_timer_cb(&dtls_param->ssl, (void *)&dtls_param->timer, mbedtls_timing_set_delay,
|
||||
mbedtls_timing_get_delay);
|
||||
}
|
||||
|
||||
#if defined(MBEDTLS_X509_CRT_PARSE_C)
|
||||
if ((ret = mbedtls_ssl_set_hostname(&dtls_param->ssl, host)) != 0) {
|
||||
QCLOUD_LOG_E("mbedtls_ssl_set_hostname failed returned -0x%x", -ret);
|
||||
goto error;
|
||||
}
|
||||
#endif
|
||||
|
||||
mbedtls_ssl_set_bio(&dtls_param->ssl, (void *)&dtls_param->socket_fd, mbedtls_dtls_net_send, mbedtls_dtls_net_recv,
|
||||
mbedtls_dtls_net_recv_timeout);
|
||||
|
||||
QCLOUD_LOG_D("Performing the SSL/TLS handshake...");
|
||||
while ((ret = mbedtls_ssl_handshake(&dtls_param->ssl)) != 0) {
|
||||
if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
|
||||
QCLOUD_LOG_E("mbedtls_ssl_handshake failed returned -0x%x", -ret);
|
||||
#if defined(MBEDTLS_X509_CRT_PARSE_C)
|
||||
if (ret == MBEDTLS_ERR_X509_CERT_VERIFY_FAILED) {
|
||||
QCLOUD_LOG_E("Unable to verify the server's certificate");
|
||||
}
|
||||
#endif
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if ((ret = mbedtls_ssl_get_verify_result(&dtls_param->ssl)) != 0) {
|
||||
QCLOUD_LOG_E("mbedtls_ssl_get_verify_result failed returned -0x%x", -ret);
|
||||
goto error;
|
||||
}
|
||||
|
||||
return dtls_param;
|
||||
|
||||
error:
|
||||
mbedtls_dtls_param_free(dtls_param);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
__QCLOUD_OSAL__ void osal_dtls_disconnect(void *handle)
|
||||
{
|
||||
int rc = 0;
|
||||
dtls_param_t *dtls_param = NULL;
|
||||
|
||||
if (!handle) {
|
||||
return;
|
||||
}
|
||||
|
||||
dtls_param = (dtls_param_t *)handle;
|
||||
|
||||
do {
|
||||
rc = mbedtls_ssl_close_notify(&dtls_param->ssl);
|
||||
} while (rc == MBEDTLS_ERR_SSL_WANT_READ || rc == MBEDTLS_ERR_SSL_WANT_WRITE);
|
||||
|
||||
mbedtls_dtls_param_free(dtls_param);
|
||||
}
|
||||
|
||||
__QCLOUD_OSAL__ qcloud_err_t osal_dtls_write(void *handle, const void *buf, size_t len, size_t *write_len)
|
||||
{
|
||||
QCLOUD_POINTER_SANITY_CHECK(handle, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(buf, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(write_len, QCLOUD_ERR_INVAL);
|
||||
|
||||
int rc;
|
||||
dtls_param_t *data_params = NULL;
|
||||
|
||||
data_params = (dtls_param_t *)handle;
|
||||
|
||||
rc = mbedtls_ssl_write(&data_params->ssl, buf, len);
|
||||
if (rc < 0) {
|
||||
QCLOUD_LOG_E("ssl_write failed %d", rc);
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SSL_WRITE);
|
||||
}
|
||||
|
||||
*write_len = rc;
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
|
||||
}
|
||||
|
||||
__QCLOUD_OSAL__ qcloud_err_t osal_dtls_read(void *handle, void *buf, size_t len, uint32_t timeout, size_t *read_len)
|
||||
{
|
||||
QCLOUD_POINTER_SANITY_CHECK(handle, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(buf, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(read_len, QCLOUD_ERR_INVAL);
|
||||
|
||||
int rc = 0;
|
||||
dtls_param_t *data_params = NULL;
|
||||
|
||||
data_params = (dtls_param_t *)handle;
|
||||
|
||||
mbedtls_ssl_conf_read_timeout(&data_params->ssl_conf, timeout);
|
||||
|
||||
do {
|
||||
rc = mbedtls_ssl_read(&data_params->ssl, buf, len);
|
||||
} while (rc == MBEDTLS_ERR_SSL_WANT_READ || rc == MBEDTLS_ERR_SSL_WANT_WRITE);
|
||||
|
||||
if (rc > 0) {
|
||||
*read_len = rc;
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
|
||||
}
|
||||
|
||||
switch (rc) {
|
||||
case MBEDTLS_ERR_SSL_TIMEOUT:
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SSL_READ_TIMEOUT);
|
||||
|
||||
case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY:
|
||||
QCLOUD_LOG_E("connection closed");
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_DTLS_PEER_CLOSE_NOTIFY);
|
||||
|
||||
default:
|
||||
QCLOUD_LOG_E("ssl_read returned -0x%x", -rc);
|
||||
break;
|
||||
}
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright (C) 2016 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 <stdarg.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "tos.h"
|
||||
#include "qcloud.h"
|
||||
|
||||
__QCLOUD_OSAL__ void osal_printf(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
vprintf(fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
__QCLOUD_OSAL__ int osal_snprintf(char *str, const int len, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
int rc;
|
||||
|
||||
va_start(args, fmt);
|
||||
rc = vsnprintf(str, len, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
__QCLOUD_OSAL__ int osal_vsnprintf(char *str, const int len, const char *format, va_list ap)
|
||||
{
|
||||
return vsnprintf(str, len, format, ap);
|
||||
}
|
||||
|
||||
__QCLOUD_OSAL__ void *osal_mutex_create(void)
|
||||
{
|
||||
k_mutex_t *mutex;
|
||||
|
||||
mutex = (k_mutex_t *)osal_malloc(sizeof(k_mutex_t));
|
||||
tos_mutex_create(mutex);
|
||||
|
||||
return (void *)mutex;
|
||||
}
|
||||
|
||||
__QCLOUD_OSAL__ void osal_mutex_destroy(void *mutex)
|
||||
{
|
||||
k_err_t ret;
|
||||
|
||||
if (K_ERR_NONE != (ret = tos_mutex_destroy((k_mutex_t *)mutex))) {
|
||||
osal_printf("osal_mutex_destroy err, err:%d\n\r", ret);
|
||||
} else {
|
||||
osal_free((void *)mutex);
|
||||
}
|
||||
}
|
||||
|
||||
__QCLOUD_OSAL__ void osal_mutex_lock(void *mutex)
|
||||
{
|
||||
k_err_t ret;
|
||||
|
||||
if (K_ERR_NONE != (ret = tos_mutex_pend((k_mutex_t *)mutex))) {
|
||||
osal_printf("osal_mutex_lock err, err:%d\n\r", ret);
|
||||
}
|
||||
}
|
||||
|
||||
__QCLOUD_OSAL__ int osal_mutex_trylock(void *mutex)
|
||||
{
|
||||
k_err_t ret;
|
||||
|
||||
if (K_ERR_NONE != (ret = tos_mutex_pend_timed((k_mutex_t *)mutex, 0))) {
|
||||
osal_printf("osal_mutex_lock err, err:%d\n\r", ret);
|
||||
return (int)ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
__QCLOUD_OSAL__ void osal_mutex_unlock(void *mutex)
|
||||
{
|
||||
k_err_t ret;
|
||||
|
||||
if (K_ERR_NONE != (ret = tos_mutex_post((k_mutex_t *)mutex))) {
|
||||
osal_printf("osal_mutex_unlock err, err:%d\n\r", ret);
|
||||
}
|
||||
}
|
||||
|
||||
__QCLOUD_OSAL__ void *osal_malloc(uint32_t size)
|
||||
{
|
||||
return tos_mmheap_alloc(size);
|
||||
}
|
||||
|
||||
__QCLOUD_OSAL__ void osal_free(void *ptr)
|
||||
{
|
||||
tos_mmheap_free(ptr);
|
||||
}
|
||||
|
||||
__QCLOUD_OSAL__ void osal_sleep_ms(uint32_t ms)
|
||||
{
|
||||
(void)tos_sleep_hmsm(0,0,0, ms);
|
||||
}
|
||||
|
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright (C) 2016 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 <stdio.h>
|
||||
#include <string.h>
|
||||
#include "tos.h"
|
||||
#include "sal_module_wrapper.h"
|
||||
#include "qcloud.h"
|
||||
|
||||
#define PORT_BUFF_LEN 16
|
||||
|
||||
__QCLOUD_OSAL__ int osal_tcp_connect(const char *host, uint16_t port)
|
||||
{
|
||||
int fd;
|
||||
char port_str[PORT_BUFF_LEN];
|
||||
|
||||
memset(port_str, 0, PORT_BUFF_LEN);
|
||||
snprintf(port_str, PORT_BUFF_LEN, "%u", port);
|
||||
QCLOUD_LOG_I("osal_tcp_connect entry, host=%s port=%d(%s)", host , port, port_str);
|
||||
|
||||
fd = tos_sal_module_connect(host, port_str, TOS_SAL_PROTO_TCP);
|
||||
if (fd < 0) {
|
||||
QCLOUD_LOG_I("net connect fail\n\r");
|
||||
if (QCLOUD_ERR_SUCCESS == tos_sal_module_init()) {
|
||||
QCLOUD_LOG_I("net reinit success\n\r");
|
||||
fd = tos_sal_module_connect(host, port_str, TOS_SAL_PROTO_TCP);
|
||||
if (fd < 0) {
|
||||
QCLOUD_LOG_I("net connect fail\n\r");
|
||||
return NULL;
|
||||
} else {
|
||||
QCLOUD_LOG_I("net connect success, fd=%d\n\r", fd);
|
||||
}
|
||||
} else {
|
||||
QCLOUD_LOG_I("net reinit fail\n\r");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
__QCLOUD_OSAL__ int osal_tcp_disconnect(int fd)
|
||||
{
|
||||
(void)tos_sal_module_close(fd);
|
||||
return QCLOUD_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
__QCLOUD_OSAL__ qcloud_err_t osal_tcp_write(int fd, const void *buf, size_t len, uint32_t timeout, size_t *write_len)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = tos_sal_module_send(fd, buf, len);
|
||||
|
||||
if (ret < 0) {
|
||||
return QCLOUD_ERR_TCP_WRITE_FAIL;
|
||||
}
|
||||
|
||||
(*(int *)write_len) = ret;
|
||||
return QCLOUD_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
__QCLOUD_OSAL__ qcloud_err_t osal_tcp_read(int fd, void *buf, size_t len, uint32_t timeout, size_t *read_len)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = tos_sal_module_recv_timeout(fd, buf, len, timeout);
|
||||
|
||||
if (ret < 0) {
|
||||
return QCLOUD_ERR_TCP_READ_FAIL;
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
return QCLOUD_ERR_TCP_NOTHING_TO_READ;
|
||||
}
|
||||
|
||||
(*(int *)read_len) = ret;
|
||||
return QCLOUD_ERR_SUCCESS;
|
||||
}
|
||||
|
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright (C) 2016 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 <stdio.h>
|
||||
#include <string.h>
|
||||
#include "tos.h"
|
||||
#include "qcloud.h"
|
||||
|
||||
#include "lwip/api.h"
|
||||
#include "lwip/sockets.h"
|
||||
#include "lwip/err.h"
|
||||
#include "lwip/sys.h"
|
||||
#include "lwip/netdb.h"
|
||||
|
||||
__QCLOUD_OSAL__ int osal_tcp_connect(const char *host, uint16_t port)
|
||||
{
|
||||
int ret;
|
||||
int fd = 0;
|
||||
char port_str[6];
|
||||
struct addrinfo hints, *addr_list, *cur;
|
||||
|
||||
osal_snprintf(port_str, 6, "%d", port);
|
||||
|
||||
memset(&hints, 0x00, sizeof(hints));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_protocol = IPPROTO_TCP;
|
||||
|
||||
ret = getaddrinfo(host, port_str, &hints, &addr_list);
|
||||
if (ret) {
|
||||
QCLOUD_LOG_E("getaddrinfo(%s:%s) error: %s", host, port_str, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (cur = addr_list; cur != NULL; cur = cur->ai_next) {
|
||||
fd = socket(cur->ai_family, cur->ai_socktype, cur->ai_protocol);
|
||||
if (fd < 0) {
|
||||
ret = -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (connect(fd, cur->ai_addr, cur->ai_addrlen) == 0) {
|
||||
ret = fd;
|
||||
break;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
if (ret == -1) {
|
||||
QCLOUD_LOG_E("fail to connect: %s:%s", host, port_str);
|
||||
}
|
||||
|
||||
freeaddrinfo(addr_list);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
__QCLOUD_OSAL__ int osal_tcp_disconnect(int fd)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* Shutdown both send and receive operations. */
|
||||
rc = shutdown(fd, 2);
|
||||
if (rc != 0) {
|
||||
QCLOUD_LOG_E("shutdown error: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = close(fd);
|
||||
if (rc != 0) {
|
||||
QCLOUD_LOG_E("closesocket error: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
__QCLOUD_OSAL__ qcloud_err_t osal_tcp_write(int fd, const void *buf, size_t len, uint32_t timeout, size_t *write_len)
|
||||
{
|
||||
int ret;
|
||||
qcloud_err_t rc = QCLOUD_ERR_SUCCESS;
|
||||
uint32_t len_sent = 0;
|
||||
fd_set sets;
|
||||
uint32_t time_remain;
|
||||
osal_timer_t timer;
|
||||
struct timeval tv;
|
||||
|
||||
osal_timer_init(&timer);
|
||||
osal_timer_countdown_ms(&timer, timeout);
|
||||
|
||||
do {
|
||||
if (osal_timer_is_expired(&timer)) {
|
||||
rc = QCLOUD_ERR_TCP_WRITE_TIMEOUT;
|
||||
break;
|
||||
}
|
||||
|
||||
time_remain = osal_timer_remain(&timer);
|
||||
|
||||
tv.tv_sec = time_remain / 1000;
|
||||
tv.tv_usec = (time_remain % 1000) * 1000;
|
||||
|
||||
FD_ZERO(&sets);
|
||||
FD_SET(fd, &sets);
|
||||
|
||||
ret = select(fd + 1, NULL, &sets, NULL, &tv);
|
||||
if (ret > 0) {
|
||||
if (FD_ISSET(fd, &sets) == 0) {
|
||||
QCLOUD_LOG_E("Should NOT arrive");
|
||||
/* If timeout in next loop, it will not sent any data */
|
||||
rc = QCLOUD_ERR_TCP_NOTHING_TO_READ;
|
||||
continue;
|
||||
}
|
||||
} else if (ret == 0) {
|
||||
rc = QCLOUD_ERR_TCP_WRITE_TIMEOUT;
|
||||
QCLOUD_LOG_E("select-write timeout %d", fd);
|
||||
break;
|
||||
} else {
|
||||
if (errno == EINTR) {
|
||||
QCLOUD_LOG_E("EINTR be caught");
|
||||
continue;
|
||||
}
|
||||
|
||||
rc = QCLOUD_ERR_TCP_WRITE_FAIL;
|
||||
QCLOUD_LOG_E("select-write fail: %s", strerror(errno));
|
||||
break;
|
||||
}
|
||||
|
||||
ret = send(fd, (uint8_t *)buf + len_sent, len - len_sent, 0);
|
||||
if (ret > 0) {
|
||||
len_sent += ret;
|
||||
} else if (ret == 0) {
|
||||
QCLOUD_LOG_E("No data be sent. Should NOT arrive");
|
||||
} else {
|
||||
if (errno == EINTR) {
|
||||
QCLOUD_LOG_E("EINTR be caught");
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = QCLOUD_ERR_TCP_WRITE_FAIL;
|
||||
QCLOUD_LOG_E("send fail: %s", strerror(errno));
|
||||
break;
|
||||
}
|
||||
} while (len_sent < len);
|
||||
|
||||
*write_len = (size_t)len_sent;
|
||||
|
||||
return len_sent > 0 ? QCLOUD_ERR_SUCCESS : rc;
|
||||
}
|
||||
|
||||
__QCLOUD_OSAL__ qcloud_err_t osal_tcp_read(int fd, void *buf, size_t len, uint32_t timeout, size_t *read_len)
|
||||
{
|
||||
int ret;
|
||||
qcloud_err_t rc = QCLOUD_ERR_SUCCESS;
|
||||
uint32_t len_recv = 0;
|
||||
uint32_t time_remain;
|
||||
fd_set sets;
|
||||
struct timeval tv;
|
||||
osal_timer_t timer;
|
||||
struct sockaddr_in peer;
|
||||
socklen_t sLen = sizeof(peer);
|
||||
int peer_port = 0;
|
||||
|
||||
osal_timer_init(&timer);
|
||||
osal_timer_countdown_ms(&timer, timeout);
|
||||
|
||||
do {
|
||||
if (osal_timer_is_expired(&timer)) {
|
||||
rc = QCLOUD_ERR_TCP_READ_TIMEOUT;
|
||||
break;
|
||||
}
|
||||
|
||||
time_remain = osal_timer_remain(&timer);
|
||||
|
||||
tv.tv_sec = time_remain / 1000;
|
||||
tv.tv_usec = (time_remain % 1000) * 1000;
|
||||
|
||||
FD_ZERO(&sets);
|
||||
FD_SET(fd, &sets);
|
||||
|
||||
ret = select(fd + 1, &sets, NULL, NULL, &tv);
|
||||
if (ret > 0) {
|
||||
ret = recv(fd, (uint8_t *)buf + len_recv, len - len_recv, 0);
|
||||
if (ret > 0) {
|
||||
len_recv += ret;
|
||||
} else if (ret == 0) {
|
||||
getpeername(fd, (struct sockaddr*)&peer, &sLen);
|
||||
peer_port = ntohs(peer.sin_port);
|
||||
QCLOUD_LOG_E("connection is closed by server: %s:%d", inet_ntoa(peer.sin_addr), peer_port);
|
||||
|
||||
rc = QCLOUD_ERR_TCP_PEER_SHUTDOWN;
|
||||
break;
|
||||
} else {
|
||||
if (EINTR == errno) {
|
||||
QCLOUD_LOG_E("EINTR be caught");
|
||||
continue;
|
||||
}
|
||||
|
||||
QCLOUD_LOG_E("recv error: %s", strerror(errno));
|
||||
rc = QCLOUD_ERR_TCP_READ_FAIL;
|
||||
break;
|
||||
}
|
||||
} else if (ret == 0) {
|
||||
rc = QCLOUD_ERR_TCP_READ_TIMEOUT;
|
||||
break;
|
||||
} else {
|
||||
QCLOUD_LOG_E("select-recv error: %s", strerror(errno));
|
||||
rc = QCLOUD_ERR_TCP_READ_FAIL;
|
||||
break;
|
||||
}
|
||||
} while ((len_recv < len));
|
||||
|
||||
*read_len = (size_t)len_recv;
|
||||
|
||||
if (rc == QCLOUD_ERR_TCP_READ_TIMEOUT && len_recv == 0) {
|
||||
rc = QCLOUD_ERR_TCP_NOTHING_TO_READ;
|
||||
}
|
||||
|
||||
return (len == len_recv) ? QCLOUD_ERR_SUCCESS : rc;
|
||||
}
|
||||
|
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright (C) 2016 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 <tos.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "tos.h"
|
||||
#include "qcloud.h"
|
||||
|
||||
static char now_time_str[20] = {0};
|
||||
|
||||
__QCLOUD_OSAL__ uint32_t osal_uptime_ms(void)
|
||||
{
|
||||
#if (TOS_CFG_CPU_TICK_PER_SECOND == 1000)
|
||||
return (uint32_t)tos_systick_get();
|
||||
#else
|
||||
k_tick_t tick = 0u;
|
||||
|
||||
tick = tos_systick_get() * 1000;
|
||||
return (uint32_t)((tick + TOS_CFG_CPU_TICK_PER_SECOND - 1) / TOS_CFG_CPU_TICK_PER_SECOND);
|
||||
#endif
|
||||
}
|
||||
|
||||
__QCLOUD_OSAL__ long osal_timer_current_sec(void)
|
||||
{
|
||||
return osal_uptime_ms() / 1000 + 50 * 365 * 24 * 3600; // 100 years
|
||||
}
|
||||
|
||||
__QCLOUD_OSAL__ char *osal_timer_current(void)
|
||||
{
|
||||
long time_sec;
|
||||
|
||||
time_sec = osal_timer_current_sec();
|
||||
memset(now_time_str, 0, 20);
|
||||
snprintf(now_time_str, 20, "%d",time_sec);
|
||||
|
||||
return now_time_str;
|
||||
}
|
||||
|
||||
__QCLOUD_OSAL__ int osal_timer_is_expired(osal_timer_t *timer)
|
||||
{
|
||||
return osal_uptime_ms() > timer->end_time ? 1 : 0;
|
||||
}
|
||||
|
||||
__QCLOUD_OSAL__ void osal_timer_countdown_ms(osal_timer_t *timer, uint32_t timeout)
|
||||
{
|
||||
timer->end_time = osal_uptime_ms();
|
||||
timer->end_time += timeout;
|
||||
}
|
||||
|
||||
__QCLOUD_OSAL__ void osal_timer_countdown(osal_timer_t *timer, uint32_t timeout)
|
||||
{
|
||||
timer->end_time = osal_uptime_ms();
|
||||
timer->end_time += timeout * 1000;
|
||||
}
|
||||
|
||||
__QCLOUD_OSAL__ uint32_t osal_timer_remain(osal_timer_t *timer)
|
||||
{
|
||||
uint32_t now;
|
||||
|
||||
now = osal_uptime_ms();
|
||||
if (timer->end_time <= now) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return timer->end_time - now;
|
||||
}
|
||||
|
||||
__QCLOUD_OSAL__ void osal_timer_init(osal_timer_t *timer)
|
||||
{
|
||||
timer->end_time = 0;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@@ -0,0 +1,438 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright (C) 2016 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 <stdint.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "tos.h"
|
||||
#include "qcloud.h"
|
||||
|
||||
#include "mbedtls/ssl.h"
|
||||
#include "mbedtls/entropy.h"
|
||||
#include "mbedtls/net_sockets.h"
|
||||
#include "mbedtls/ctr_drbg.h"
|
||||
#include "mbedtls/error.h"
|
||||
#include "mbedtls/debug.h"
|
||||
|
||||
#define DEBUG_LEVEL 0
|
||||
|
||||
#if (QCLOUD_CFG_AUTH_MODE == QCLOUD_AUTH_MODE_KEY)
|
||||
static const int ciphersuites[] = {MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA, MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA, 0};
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief 用于保存SSL连接相关数据结构
|
||||
*/
|
||||
typedef struct tls_paramter_st {
|
||||
mbedtls_net_context socket_fd; // socket文件描述符
|
||||
mbedtls_entropy_context entropy; // 保存熵配置
|
||||
mbedtls_ctr_drbg_context ctr_drbg; // 随机数生成器
|
||||
mbedtls_ssl_context ssl; // 保存SSL基本数据
|
||||
mbedtls_ssl_config ssl_conf; // TSL/TLS配置信息
|
||||
#if defined(MBEDTLS_X509_CRT_PARSE_C)
|
||||
mbedtls_x509_crt ca_cert; // ca证书信息
|
||||
mbedtls_x509_crt client_cert; // 客户端证书信息
|
||||
#endif
|
||||
mbedtls_pk_context private_key; // 客户端私钥信息
|
||||
} tls_param_t;
|
||||
|
||||
/**
|
||||
* @brief 释放mbedtls开辟的内存
|
||||
*/
|
||||
static void mbedtls_tls_param_free(tls_param_t *tls_paramter)
|
||||
{
|
||||
mbedtls_net_free(&tls_paramter->socket_fd);
|
||||
#if defined(MBEDTLS_X509_CRT_PARSE_C)
|
||||
mbedtls_x509_crt_free(&tls_paramter->client_cert);
|
||||
mbedtls_x509_crt_free(&tls_paramter->ca_cert);
|
||||
mbedtls_pk_free(&tls_paramter->private_key);
|
||||
#endif
|
||||
mbedtls_ssl_free(&tls_paramter->ssl);
|
||||
mbedtls_ssl_config_free(&tls_paramter->ssl_conf);
|
||||
mbedtls_ctr_drbg_free(&tls_paramter->ctr_drbg);
|
||||
mbedtls_entropy_free(&tls_paramter->entropy);
|
||||
|
||||
osal_free(tls_paramter);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief mbedtls库初始化
|
||||
*
|
||||
* 1. 执行mbedtls库相关初始化函数
|
||||
* 2. 随机数生成器
|
||||
* 3. 加载CA证书, 客户端证书及私钥文件/设置psk
|
||||
*
|
||||
* @param pDataParams TLS连接相关数据结构
|
||||
* @param pConnectParams TLS证书密钥相关
|
||||
* @return 返回QCLOUD_ERR_SUCCESS, 表示成功
|
||||
*/
|
||||
static void mbedtls_debug(void *ctx, int level, const char *file, int line, const char *str)
|
||||
{
|
||||
QCLOUD_LOG_I("[mbedTLS]:[%s]:[%d]: %s\r\n", file, line, str);
|
||||
}
|
||||
|
||||
static int mbedtls_client_init(tls_param_t *tls_param, qcloud_tls_opt_t *tls_opt)
|
||||
{
|
||||
int ret = QCLOUD_ERR_SUCCESS;
|
||||
|
||||
#if defined(MBEDTLS_DEBUG_C)
|
||||
mbedtls_debug_set_threshold(DEBUG_LEVEL);
|
||||
#endif
|
||||
|
||||
mbedtls_net_init(&tls_param->socket_fd);
|
||||
mbedtls_ssl_init(&tls_param->ssl);
|
||||
mbedtls_ssl_config_init(&tls_param->ssl_conf);
|
||||
mbedtls_ctr_drbg_init(&tls_param->ctr_drbg);
|
||||
#if defined(MBEDTLS_X509_CRT_PARSE_C)
|
||||
mbedtls_x509_crt_init(&tls_param->ca_cert);
|
||||
mbedtls_x509_crt_init(&tls_param->client_cert);
|
||||
mbedtls_pk_init(&tls_param->private_key);
|
||||
#endif
|
||||
|
||||
mbedtls_entropy_init(&tls_param->entropy);
|
||||
|
||||
mbedtls_ssl_conf_dbg(&tls_param->ssl_conf, mbedtls_debug, NULL);
|
||||
|
||||
// 随机数, 增加custom参数, 目前为NULL
|
||||
if ((ret = mbedtls_ctr_drbg_seed(&tls_param->ctr_drbg, mbedtls_entropy_func,
|
||||
&tls_param->entropy, NULL, 0)) != 0) {
|
||||
QCLOUD_LOG_E("mbedtls_ctr_drbg_seed failed returned -0x%04x", -ret);
|
||||
return QCLOUD_ERR_SSL_INIT;
|
||||
}
|
||||
|
||||
#if defined(MBEDTLS_X509_CRT_PARSE_C)
|
||||
if (tls_opt->ca_cert != NULL) {
|
||||
if ((ret = mbedtls_x509_crt_parse(&tls_param->ca_cert, (const unsigned char *)tls_opt->ca_cert,
|
||||
(tls_opt->ca_cert_len + 1)))) {
|
||||
QCLOUD_LOG_E("parse ca crt failed returned -0x%04x", -ret);
|
||||
return QCLOUD_ERR_SSL_CERT;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if (QCLOUD_CFG_AUTH_MODE == QCLOUD_AUTH_MODE_CERT)
|
||||
if (tls_opt->cert_path != NULL && tls_opt->priv_key_path != NULL) {
|
||||
#if defined(MBEDTLS_X509_CRT_PARSE_C)
|
||||
if ((ret = mbedtls_x509_crt_parse_file(&tls_param->client_cert, tls_opt->cert_path)) != 0) {
|
||||
QCLOUD_LOG_E("load client cert file failed returned 0x%x", ret<0?-ret:ret);
|
||||
return QCLOUD_ERR_SSL_CERT;
|
||||
}
|
||||
#endif
|
||||
|
||||
if ((ret = mbedtls_pk_parse_keyfile(&tls_param->private_key, tls_opt->priv_key_path, "")) != 0) {
|
||||
QCLOUD_LOG_E("load client key file failed returned 0x%x", ret < 0 ? -ret : ret);
|
||||
return QCLOUD_ERR_SSL_CERT;
|
||||
}
|
||||
} else {
|
||||
QCLOUD_LOG_E("cert_file/key_file is empty!|cert_file=%s|key_file=%s", tls_opt->cert_path, tls_opt->priv_key_path);
|
||||
}
|
||||
#else
|
||||
if (tls_opt->psk != NULL && tls_opt->psk_id != NULL) {
|
||||
const char *psk_id = tls_opt->psk_id;
|
||||
ret = mbedtls_ssl_conf_psk(&tls_param->ssl_conf, (unsigned char *)tls_opt->psk, tls_opt->psk_len,
|
||||
(const unsigned char *)psk_id, strlen(psk_id));
|
||||
} else {
|
||||
QCLOUD_LOG_D("psk/pskid is empty!|psk=%s|psd_id=%s", tls_opt->psk, tls_opt->psk_id);
|
||||
}
|
||||
|
||||
if (0 != ret) {
|
||||
QCLOUD_LOG_E("mbedtls_ssl_conf_psk fail: -0x%x", -ret);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
return QCLOUD_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief 建立TCP连接
|
||||
*
|
||||
* @param socket_fd Socket描述符
|
||||
* @param host 服务器主机名
|
||||
* @param port 服务器端口地址
|
||||
* @return 返回QCLOUD_ERR_SUCCESS, 表示成功
|
||||
*/
|
||||
int mbedtls_tcp_connect(mbedtls_net_context *socket_fd, const char *host, int port)
|
||||
{
|
||||
int ret = 0;
|
||||
char port_str[6];
|
||||
|
||||
osal_snprintf(port_str, 6, "%d", port);
|
||||
|
||||
if ((ret = mbedtls_net_connect(socket_fd, host, port_str, MBEDTLS_NET_PROTO_TCP)) != 0) {
|
||||
QCLOUD_LOG_E("tcp connect failed returned -0x%04x", -ret);
|
||||
|
||||
switch (ret) {
|
||||
case MBEDTLS_ERR_NET_SOCKET_FAILED:
|
||||
return QCLOUD_ERR_TCP_SOCKET_FAILED;
|
||||
case MBEDTLS_ERR_NET_UNKNOWN_HOST:
|
||||
return QCLOUD_ERR_TCP_UNKNOWN_HOST;
|
||||
default:
|
||||
return QCLOUD_ERR_TCP_CONNECT;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#if 0
|
||||
if ((ret = mbedtls_net_set_block(socket_fd)) != 0) {
|
||||
QCLOUD_LOG_E("set block faliled returned -0x%04x", -ret);
|
||||
return QCLOUD_ERR_TCP_CONNECT;
|
||||
}
|
||||
#endif
|
||||
|
||||
return QCLOUD_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 在该函数中可对服务端证书进行自定义的校验
|
||||
*
|
||||
* 这种行为发生在握手过程中, 一般是校验连接服务器的主机名与服务器证书中的CN或SAN的域名信息是否一致
|
||||
* 不过, mbedtls库已经实现该功能, 可以参考函数 `mbedtls_x509_crt_verify_with_profile`
|
||||
*
|
||||
* @param hostname 连接服务器的主机名
|
||||
* @param crt x509格式的证书
|
||||
* @param depth
|
||||
* @param flags
|
||||
* @return
|
||||
*/
|
||||
|
||||
#if defined(MBEDTLS_X509_CRT_PARSE_C)
|
||||
static int qcloud_server_certificate_verify(void *hostname, mbedtls_x509_crt *crt, int depth, uint32_t *flags)
|
||||
{
|
||||
return *flags;
|
||||
}
|
||||
#endif
|
||||
|
||||
__QCLOUD_OSAL__ void *osal_tls_connect(qcloud_tls_opt_t *tls_opt, const char *host, int port)
|
||||
{
|
||||
int ret = 0;
|
||||
tls_param_t *tls_param = NULL;
|
||||
|
||||
if (!tls_opt) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tls_param = (tls_param_t *)osal_malloc(sizeof(tls_param_t));
|
||||
if (!tls_param) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((ret = mbedtls_client_init(tls_param, tls_opt)) != QCLOUD_ERR_SUCCESS) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
QCLOUD_LOG_D(" Connecting to /%s/%d...", host, port);
|
||||
if ((ret = mbedtls_tcp_connect(&tls_param->socket_fd, host, port)) != QCLOUD_ERR_SUCCESS) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
QCLOUD_LOG_D(" Setting up the SSL/TLS structure...");
|
||||
if ((ret = mbedtls_ssl_config_defaults(&tls_param->ssl_conf, MBEDTLS_SSL_IS_CLIENT,
|
||||
MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT)) != 0) {
|
||||
QCLOUD_LOG_E("mbedtls_ssl_config_defaults failed returned -0x%04x", -ret);
|
||||
goto error;
|
||||
}
|
||||
|
||||
#if defined(MBEDTLS_X509_CRT_PARSE_C)
|
||||
|
||||
mbedtls_ssl_conf_verify(&tls_param->ssl_conf, qcloud_server_certificate_verify, (void *)host);
|
||||
|
||||
mbedtls_ssl_conf_authmode(&tls_param->ssl_conf, MBEDTLS_SSL_VERIFY_REQUIRED);
|
||||
#endif
|
||||
mbedtls_ssl_conf_rng(&tls_param->ssl_conf, mbedtls_ctr_drbg_random, &tls_param->ctr_drbg);
|
||||
|
||||
|
||||
#if defined(MBEDTLS_X509_CRT_PARSE_C)
|
||||
mbedtls_ssl_conf_ca_chain(&tls_param->ssl_conf, &tls_param->ca_cert, NULL);
|
||||
if ((ret = mbedtls_ssl_conf_own_cert(&tls_param->ssl_conf,
|
||||
&tls_param->client_cert, &tls_param->private_key)) != 0) {
|
||||
QCLOUD_LOG_E("mbedtls_ssl_conf_own_cert failed returned 0x%04x", -ret);
|
||||
goto error;
|
||||
}
|
||||
#endif
|
||||
|
||||
mbedtls_ssl_conf_read_timeout(&tls_param->ssl_conf, tls_opt->timeout);
|
||||
if ((ret = mbedtls_ssl_setup(&tls_param->ssl, &tls_param->ssl_conf)) != 0) {
|
||||
QCLOUD_LOG_E("mbedtls_ssl_setup failed returned 0x%04x", -ret);
|
||||
goto error;
|
||||
}
|
||||
|
||||
#if (QCLOUD_CFG_AUTH_MODE == QCLOUD_AUTH_MODE_KEY)
|
||||
// 选择加密套件代码,以后不通加密方式合并端口的时候可以用到
|
||||
if(tls_opt->psk != NULL) {
|
||||
mbedtls_ssl_conf_ciphersuites(&tls_param->ssl_conf, ciphersuites);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(MBEDTLS_X509_CRT_PARSE_C)
|
||||
// Set the hostname to check against the received server certificate and sni
|
||||
if ((ret = mbedtls_ssl_set_hostname(&tls_param->ssl, host)) != 0) {
|
||||
QCLOUD_LOG_E("mbedtls_ssl_set_hostname failed returned 0x%04x", -ret);
|
||||
goto error;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
mbedtls_ssl_set_bio(&tls_param->ssl, &tls_param->socket_fd, mbedtls_net_send, mbedtls_net_recv,
|
||||
mbedtls_net_recv_timeout);
|
||||
|
||||
QCLOUD_LOG_D("Performing the SSL/TLS handshake...");
|
||||
while ((ret = mbedtls_ssl_handshake(&tls_param->ssl)) != 0) {
|
||||
if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
|
||||
QCLOUD_LOG_E("mbedtls_ssl_handshake failed returned 0x%04x", -ret);
|
||||
|
||||
#if defined(MBEDTLS_X509_CRT_PARSE_C)
|
||||
if (ret == MBEDTLS_ERR_X509_CERT_VERIFY_FAILED) {
|
||||
QCLOUD_LOG_E("Unable to verify the server's certificate");
|
||||
}
|
||||
#endif
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if ((ret = mbedtls_ssl_get_verify_result(&tls_param->ssl)) != 0) {
|
||||
QCLOUD_LOG_E("mbedtls_ssl_get_verify_result failed returned 0x%04x", -ret);
|
||||
goto error;
|
||||
}
|
||||
|
||||
mbedtls_ssl_conf_read_timeout(&tls_param->ssl_conf, 100);
|
||||
|
||||
return tls_param;
|
||||
|
||||
error:
|
||||
mbedtls_tls_param_free(tls_param);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
__QCLOUD_OSAL__ void osal_tls_disconnect(void *handle)
|
||||
{
|
||||
int ret = 0;
|
||||
tls_param_t *tls_param;
|
||||
|
||||
if (!handle) {
|
||||
QCLOUD_LOG_D("handle is NULL");
|
||||
return;
|
||||
}
|
||||
|
||||
tls_param = (tls_param_t *)handle;
|
||||
|
||||
do {
|
||||
ret = mbedtls_ssl_close_notify(&tls_param->ssl);
|
||||
} while (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE);
|
||||
|
||||
mbedtls_tls_param_free(tls_param);
|
||||
}
|
||||
|
||||
__QCLOUD_OSAL__ qcloud_err_t osal_tls_write(void *handle, const void *buf, size_t len, uint32_t timeout, size_t *write_len)
|
||||
{
|
||||
QCLOUD_POINTER_SANITY_CHECK(handle, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(buf, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(write_len, QCLOUD_ERR_INVAL);
|
||||
|
||||
osal_timer_t timer;
|
||||
tls_param_t *tls_param;
|
||||
int error = QCLOUD_FALSE;
|
||||
int write_rc = 0;
|
||||
size_t written_so_far;
|
||||
|
||||
tls_param = (tls_param_t *)handle;
|
||||
|
||||
osal_timer_init(&timer);
|
||||
osal_timer_countdown_ms(&timer, timeout);
|
||||
|
||||
for (written_so_far = 0; written_so_far < len && !osal_timer_is_expired(&timer); written_so_far += write_rc) {
|
||||
while (!osal_timer_is_expired(&timer) &&
|
||||
(write_rc = mbedtls_ssl_write(&tls_param->ssl, (uint8_t *)buf + written_so_far, len - written_so_far)) <= 0) {
|
||||
if (write_rc != MBEDTLS_ERR_SSL_WANT_READ && write_rc != MBEDTLS_ERR_SSL_WANT_WRITE) {
|
||||
QCLOUD_LOG_E("tls_write failed 0x%04x", -write_rc);
|
||||
error = QCLOUD_TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (error) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*write_len = written_so_far;
|
||||
|
||||
if (error) {
|
||||
return QCLOUD_ERR_SSL_WRITE;
|
||||
} else if (osal_timer_is_expired(&timer) && written_so_far != len) {
|
||||
return QCLOUD_ERR_SSL_WRITE_TIMEOUT;
|
||||
}
|
||||
|
||||
return QCLOUD_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
__QCLOUD_OSAL__ qcloud_err_t osal_tls_read(void *handle, void *buf, size_t len, uint32_t timeout, size_t *read_len)
|
||||
{
|
||||
//mbedtls_ssl_conf_read_timeout(&(pParams->ssl_conf), timeout_ms); TODO:每次调用这个方法会导致read阻塞, 超时也不返回
|
||||
// 这里使用非阻塞的方式, 具体的超时操作由上层做
|
||||
QCLOUD_POINTER_SANITY_CHECK(handle, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(buf, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(read_len, QCLOUD_ERR_INVAL);
|
||||
|
||||
osal_timer_t timer;
|
||||
tls_param_t *tls_param = NULL;
|
||||
|
||||
*read_len = 0;
|
||||
|
||||
osal_timer_init(&timer);
|
||||
osal_timer_countdown_ms(&timer, timeout);
|
||||
|
||||
tls_param = (tls_param_t *)handle;
|
||||
|
||||
do {
|
||||
int read_rc = 0;
|
||||
read_rc = mbedtls_ssl_read(&tls_param->ssl, (uint8_t *)buf + *read_len, len - *read_len);
|
||||
|
||||
if (read_rc > 0) {
|
||||
*read_len += read_rc;
|
||||
} else if (read_rc == 0 ||
|
||||
(read_rc != MBEDTLS_ERR_SSL_WANT_WRITE &&
|
||||
read_rc != MBEDTLS_ERR_SSL_WANT_READ &&
|
||||
read_rc != MBEDTLS_ERR_SSL_TIMEOUT)) {
|
||||
QCLOUD_LOG_E("tls_read failed: 0x%04x", -read_rc);
|
||||
return QCLOUD_ERR_SSL_READ;
|
||||
}
|
||||
|
||||
if (osal_timer_is_expired(&timer)) {
|
||||
break;
|
||||
}
|
||||
} while (*read_len < len);
|
||||
|
||||
if (len == *read_len) {
|
||||
return QCLOUD_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
if (*read_len == 0) {
|
||||
return QCLOUD_ERR_SSL_NOTHING_TO_READ;
|
||||
} else {
|
||||
return QCLOUD_ERR_SSL_READ_TIMEOUT;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright (C) 2016 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 <stdio.h>
|
||||
#include <string.h>
|
||||
#include "tos.h"
|
||||
#include "sal_module_wrapper.h"
|
||||
#include "qcloud.h"
|
||||
|
||||
#define PORT_BUFF_LEN 16
|
||||
|
||||
__QCLOUD_OSAL__ int osal_udp_connect(const char *host, uint16_t port)
|
||||
{
|
||||
int fd;
|
||||
char port_str[PORT_BUFF_LEN];
|
||||
|
||||
memset(port_str, 0, PORT_BUFF_LEN);
|
||||
snprintf(port_str, PORT_BUFF_LEN, "%u", port);
|
||||
QCLOUD_LOG_I("osal_udp_connect entry, host=%s port=%d(%s)", host , port, port_str);
|
||||
|
||||
fd = tos_sal_module_connect("111.230.127.136", "5684", TOS_SAL_PROTO_UDP);
|
||||
if (fd < 0) {
|
||||
QCLOUD_LOG_I("net connect fail\n\r");
|
||||
if (QCLOUD_ERR_SUCCESS == tos_sal_module_init()) { /* <20><><EFBFBD>³<EFBFBD>ʼ<EFBFBD><CABC>ģ<EFBFBD><C4A3> */
|
||||
QCLOUD_LOG_I("net reinit success\n\r");
|
||||
fd = tos_sal_module_connect(host, port_str, TOS_SAL_PROTO_UDP);
|
||||
if (fd < 0) {
|
||||
QCLOUD_LOG_I("net connect fail\n\r");
|
||||
return NULL;
|
||||
} else {
|
||||
QCLOUD_LOG_I("net connect success, fd=%d\n\r", fd);
|
||||
}
|
||||
} else {
|
||||
QCLOUD_LOG_I("net reinit fail\n\r");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
__QCLOUD_OSAL__ void osal_udp_disconnect(int sockfd)
|
||||
{
|
||||
(void)tos_sal_module_close(sockfd);
|
||||
}
|
||||
|
||||
__QCLOUD_OSAL__ qcloud_err_t osal_udp_write(int sockfd, const void *buf, size_t len, uint32_t timeout, size_t *write_len)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = tos_sal_module_sendto(sockfd, NULL, NULL, buf, len);
|
||||
|
||||
if (ret < 0) {
|
||||
return QCLOUD_ERR_TCP_WRITE_FAIL;
|
||||
}
|
||||
|
||||
(*(int *)write_len) = ret;
|
||||
return QCLOUD_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
__QCLOUD_OSAL__ qcloud_err_t osal_udp_read(int sockfd, void *buf, size_t len, uint32_t timeout, size_t *read_len)
|
||||
{
|
||||
int ret;
|
||||
|
||||
QCLOUD_LOG_I("osal_udp_read len %d timeout %d\r\n", len);
|
||||
|
||||
ret = tos_sal_module_recvfrom_timeout(sockfd, buf, len, timeout);
|
||||
|
||||
if (ret < 0) {
|
||||
return QCLOUD_ERR_TCP_READ_FAIL;
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
return QCLOUD_ERR_TCP_NOTHING_TO_READ;
|
||||
}
|
||||
|
||||
*(int *)read_len = ret;
|
||||
return QCLOUD_ERR_SUCCESS;
|
||||
}
|
||||
|
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright (C) 2016 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 <stdio.h>
|
||||
#include <string.h>
|
||||
#include "tos.h"
|
||||
#include "qcloud.h"
|
||||
|
||||
#include "lwip/api.h"
|
||||
#include "lwip/sockets.h"
|
||||
#include "lwip/err.h"
|
||||
#include "lwip/sys.h"
|
||||
#include "lwip/netdb.h"
|
||||
|
||||
__QCLOUD_OSAL__ int osal_udp_connect(const char *host, uint16_t port)
|
||||
{
|
||||
#define NETWORK_ADDR_LEN (16)
|
||||
|
||||
int ret;
|
||||
int fd = 0;
|
||||
char port_str[6] = {0};
|
||||
struct addrinfo hints, *addr_list, *cur;
|
||||
|
||||
osal_snprintf(port_str, 6, "%d", port);
|
||||
|
||||
memset((char *)&hints, 0x00, sizeof(hints));
|
||||
hints.ai_socktype = SOCK_DGRAM;
|
||||
hints.ai_family = AF_INET;
|
||||
hints.ai_protocol = IPPROTO_UDP;
|
||||
|
||||
QCLOUD_LOG_D("establish tcp connection with server(host=%s port=%s)", host, port_str);
|
||||
|
||||
if (getaddrinfo(host, port_str, &hints, &addr_list) != 0) {
|
||||
QCLOUD_LOG_E("getaddrinfo error,errno:%s", strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (cur = addr_list; cur != NULL; cur = cur->ai_next) {
|
||||
fd = socket(cur->ai_family, cur->ai_socktype, cur->ai_protocol);
|
||||
if (fd < 0) {
|
||||
ret = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (connect(fd, cur->ai_addr, cur->ai_addrlen) == 0) {
|
||||
ret = fd;
|
||||
break;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
QCLOUD_LOG_E("fail to establish udp");
|
||||
} else {
|
||||
QCLOUD_LOG_D("success to establish udp, fd=%d", ret);
|
||||
}
|
||||
|
||||
freeaddrinfo(addr_list);
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
__QCLOUD_OSAL__ void osal_udp_disconnect(int sockfd)
|
||||
{
|
||||
close(sockfd);
|
||||
}
|
||||
|
||||
__QCLOUD_OSAL__ qcloud_err_t osal_udp_write(int sockfd, const void *buf, size_t len, uint32_t timeout, size_t *write_len)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = send(sockfd, (char *)buf, (int)len, 0);
|
||||
if (ret < 0) {
|
||||
return QCLOUD_ERR_UDP_WRITE_FAIL;
|
||||
}
|
||||
|
||||
return QCLOUD_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
__QCLOUD_OSAL__ qcloud_err_t osal_udp_read(int sockfd, void *buf, size_t len, uint32_t timeout, size_t *read_len)
|
||||
{
|
||||
int ret;
|
||||
struct timeval tv;
|
||||
fd_set read_fds;
|
||||
|
||||
if (sockfd < 0) {
|
||||
return QCLOUD_ERR_TCP_READ_FAIL;
|
||||
}
|
||||
|
||||
FD_ZERO(&read_fds);
|
||||
FD_SET(sockfd, &read_fds);
|
||||
|
||||
tv.tv_sec = timeout / 1000;
|
||||
tv.tv_usec = (timeout % 1000) * 1000;
|
||||
|
||||
ret = select(sockfd + 1, &read_fds, NULL, NULL, timeout == 0 ? NULL : &tv);
|
||||
|
||||
/* Zero fds ready means we timed out */
|
||||
if (ret == 0) {
|
||||
return QCLOUD_ERR_UDP_READ_TIMEOUT; /* receive timeout */
|
||||
} else if (ret < 0) {
|
||||
if (errno == EINTR) {
|
||||
return QCLOUD_ERR_UDP_READ_FAIL; /* want read */
|
||||
}
|
||||
|
||||
return QCLOUD_ERR_UDP_READ_TIMEOUT; /* receive failed */
|
||||
}
|
||||
|
||||
ret = read(sockfd, buf, len);
|
||||
if (ret > 0) {
|
||||
return QCLOUD_ERR_SUCCESS;
|
||||
} else {
|
||||
return QCLOUD_ERR_UDP_READ_FAIL;
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,62 @@
|
||||
#include "qcloud.h"
|
||||
|
||||
#if (QCLOUD_CFG_AUTH_MODE == QCLOUD_AUTH_MODE_CERT)
|
||||
|
||||
__QCLOUD_API__ qcloud_err_t qcloud_device_create(qcloud_device_t *device,
|
||||
const char *product_id,
|
||||
const char *device_name,
|
||||
const char *cert_path,
|
||||
const char *priv_key_path)
|
||||
{
|
||||
QCLOUD_POINTER_SANITY_CHECK(device, QCLOUD_ERR_DEV_INFO);
|
||||
QCLOUD_POINTER_SANITY_CHECK(product_id, QCLOUD_ERR_DEV_INFO);
|
||||
QCLOUD_POINTER_SANITY_CHECK(device_name, QCLOUD_ERR_DEV_INFO);
|
||||
QCLOUD_POINTER_SANITY_CHECK(cert_path, QCLOUD_ERR_DEV_INFO);
|
||||
QCLOUD_POINTER_SANITY_CHECK(priv_key_path, QCLOUD_ERR_DEV_INFO);
|
||||
|
||||
if (strlen(product_id) > QCLOUD_DEVICE_PRODUCT_ID_MAX ||
|
||||
strlen(device_name) > QCLOUD_DEVICE_DEVICE_NAME_MAX ||
|
||||
strlen(cert_path) > QCLOUD_PATH_MAX ||
|
||||
strlen(priv_key_path) > QCLOUD_PATH_MAX) {
|
||||
return QCLOUD_ERR_DEV_INFO;
|
||||
}
|
||||
|
||||
device->product_id = product_id;
|
||||
device->device_name = device_name;
|
||||
device->cert_path = cert_path;
|
||||
device->priv_key_path = priv_key_path;
|
||||
|
||||
QCLOUD_LOG_I("SDK version: %s, product ID: %s, device name: %s", QCLOUD_SDK_VERSION, product_id, device_name);
|
||||
|
||||
return QCLOUD_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
__QCLOUD_API__ qcloud_err_t qcloud_device_create(qcloud_device_t *device,
|
||||
const char *product_id,
|
||||
const char *device_name,
|
||||
const char *key)
|
||||
{
|
||||
QCLOUD_POINTER_SANITY_CHECK(device, QCLOUD_ERR_DEV_INFO);
|
||||
QCLOUD_POINTER_SANITY_CHECK(product_id, QCLOUD_ERR_DEV_INFO);
|
||||
QCLOUD_POINTER_SANITY_CHECK(device_name, QCLOUD_ERR_DEV_INFO);
|
||||
QCLOUD_POINTER_SANITY_CHECK(key, QCLOUD_ERR_DEV_INFO);
|
||||
|
||||
if (strlen(product_id) > QCLOUD_DEVICE_PRODUCT_ID_MAX ||
|
||||
strlen(device_name) > QCLOUD_DEVICE_DEVICE_NAME_MAX ||
|
||||
strlen(key) > QCLOUD_DEVICE_KEY_MAX) {
|
||||
return QCLOUD_ERR_DEV_INFO;
|
||||
}
|
||||
|
||||
device->product_id = product_id;
|
||||
device->device_name = device_name;
|
||||
device->key = key;
|
||||
|
||||
QCLOUD_LOG_I("SDK version: %s, product ID: %s, device name: %s", QCLOUD_SDK_VERSION, product_id, device_name);
|
||||
|
||||
return QCLOUD_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@@ -0,0 +1,55 @@
|
||||
#include "qcloud.h"
|
||||
|
||||
static const char *level_str[] = {
|
||||
"DIS", "ERR", "WRN", "INF", "DBG"
|
||||
};
|
||||
|
||||
static qcloud_log_handler_t sg_log_message_handler= NULL;
|
||||
|
||||
__QCLOUD_STATIC__ const char *file_path2name(const char *file_path)
|
||||
{
|
||||
const char *suffix = NULL;
|
||||
|
||||
suffix = strrchr(file_path, '\\');
|
||||
if (!suffix) {
|
||||
return file_path;
|
||||
}
|
||||
|
||||
return ++suffix;
|
||||
}
|
||||
|
||||
void qcloud_log_handler_set(qcloud_log_handler_t handler)
|
||||
{
|
||||
sg_log_message_handler = handler;
|
||||
}
|
||||
|
||||
void qcloud_log_write(const char *file, const char *func, const int line, const int level, const char *fmt, ...)
|
||||
{
|
||||
/* format log content */
|
||||
const char *file_name = file_path2name(file);
|
||||
|
||||
char sg_text_buf[QCLOUD_LOG_LEN_MAX + 1];
|
||||
char *tmp_buf = sg_text_buf;
|
||||
char *o = tmp_buf;
|
||||
memset(tmp_buf, 0, sizeof(sg_text_buf));
|
||||
|
||||
o += osal_snprintf(o, sizeof(sg_text_buf), "%s|%s|%s|%s(%d): ", level_str[level], osal_timer_current(), file_name, func, line);
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
o += vsnprintf(o, QCLOUD_LOG_LEN_MAX - 2 - strlen(tmp_buf), fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
strcat(tmp_buf, "\r\n");
|
||||
|
||||
/* customer defined log print handler */
|
||||
if (sg_log_message_handler != NULL && sg_log_message_handler(tmp_buf)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* default log handler: print to console */
|
||||
osal_printf("%s", tmp_buf);
|
||||
|
||||
return;
|
||||
}
|
||||
|
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright (C) 2016 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 "qcloud.h"
|
||||
|
||||
__QCLOUD_STATIC__ int qcloud_network_tcp_is_connected(qcloud_network_t *network)
|
||||
{
|
||||
return network->fd >= 0;
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ qcloud_err_t qcloud_network_tcp_read(qcloud_network_t *network, void *buf, size_t len, uint32_t timeout, size_t *read_len)
|
||||
{
|
||||
QCLOUD_POINTER_SANITY_CHECK(network, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(buf, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(read_len, QCLOUD_ERR_INVAL);
|
||||
|
||||
return osal_tcp_read(network->fd, buf, len, timeout, read_len);
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ qcloud_err_t qcloud_network_tcp_write(qcloud_network_t *network, const void *buf, size_t len, uint32_t timeout, size_t *write_len)
|
||||
{
|
||||
QCLOUD_POINTER_SANITY_CHECK(network, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(buf, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(write_len, QCLOUD_ERR_INVAL);
|
||||
|
||||
return osal_tcp_write(network->fd, buf, len, timeout, write_len);
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ void qcloud_network_tcp_disconnect(qcloud_network_t *network)
|
||||
{
|
||||
if (!network || network->fd < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
osal_tcp_disconnect(network->fd);
|
||||
network->fd = -1;
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ qcloud_err_t qcloud_network_tcp_connect(qcloud_network_t *network)
|
||||
{
|
||||
QCLOUD_POINTER_SANITY_CHECK(network, QCLOUD_ERR_INVAL);
|
||||
|
||||
network->fd = osal_tcp_connect(network->host, network->port);
|
||||
if (network->fd >= 0) {
|
||||
return QCLOUD_ERR_SUCCESS;
|
||||
}
|
||||
return QCLOUD_ERR_FAILURE;
|
||||
}
|
||||
|
||||
__QCLOUD_API__ qcloud_err_t qcloud_network_tcp_init(qcloud_network_t *network)
|
||||
{
|
||||
QCLOUD_POINTER_SANITY_CHECK(network, QCLOUD_ERR_INVAL);
|
||||
|
||||
network->connect = qcloud_network_tcp_connect;
|
||||
network->read = qcloud_network_tcp_read;
|
||||
network->write = qcloud_network_tcp_write;
|
||||
network->disconnect = qcloud_network_tcp_disconnect;
|
||||
|
||||
network->is_connected = qcloud_network_tcp_is_connected;
|
||||
network->fd = -1;
|
||||
|
||||
return QCLOUD_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ int qcloud_network_udp_is_connected(qcloud_network_t *network)
|
||||
{
|
||||
return network->fd >= 0;
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ qcloud_err_t qcloud_network_udp_read(qcloud_network_t *network, void *buf, size_t len, uint32_t timeout, size_t *read_len)
|
||||
{
|
||||
QCLOUD_POINTER_SANITY_CHECK(network, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(buf, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(read_len, QCLOUD_ERR_INVAL);
|
||||
|
||||
return osal_udp_read(network->fd, buf, len, timeout, read_len);
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ qcloud_err_t qcloud_network_udp_write(qcloud_network_t *network, const void *buf, size_t len, uint32_t timeout, size_t *write_len)
|
||||
{
|
||||
QCLOUD_POINTER_SANITY_CHECK(network, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(buf, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(write_len, QCLOUD_ERR_INVAL);
|
||||
|
||||
return osal_udp_write(network->fd, buf, len, timeout, write_len);
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ void qcloud_network_udp_disconnect(qcloud_network_t *network)
|
||||
{
|
||||
if (!network || network->fd < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
osal_udp_disconnect(network->fd);
|
||||
network->fd = -1;
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ qcloud_err_t qcloud_network_udp_connect(qcloud_network_t *network)
|
||||
{
|
||||
QCLOUD_POINTER_SANITY_CHECK(network, QCLOUD_ERR_INVAL);
|
||||
|
||||
network->fd = osal_udp_connect(network->host, network->port);
|
||||
if (network->fd >= 0) {
|
||||
return QCLOUD_ERR_SUCCESS;
|
||||
}
|
||||
return QCLOUD_ERR_FAILURE;
|
||||
}
|
||||
|
||||
__QCLOUD_API__ qcloud_err_t qcloud_network_udp_init(qcloud_network_t *network)
|
||||
{
|
||||
QCLOUD_POINTER_SANITY_CHECK(network, QCLOUD_ERR_INVAL);
|
||||
|
||||
|
||||
network->connect = qcloud_network_udp_connect;
|
||||
network->read = qcloud_network_udp_read;
|
||||
network->write = qcloud_network_udp_write;
|
||||
network->disconnect = qcloud_network_udp_disconnect;
|
||||
|
||||
network->is_connected = qcloud_network_udp_is_connected;
|
||||
network->fd = -1;
|
||||
|
||||
return QCLOUD_ERR_SUCCESS;
|
||||
}
|
||||
|
198
components/connectivity/TencentCloud_SDK/source/src/qcloud_tls.c
Normal file
198
components/connectivity/TencentCloud_SDK/source/src/qcloud_tls.c
Normal file
@@ -0,0 +1,198 @@
|
||||
#include "qcloud.h"
|
||||
|
||||
#if (QCLOUD_CFG_TLS_EN > 0u)
|
||||
|
||||
#if (QCLOUD_CFG_AUTH_MODE == QCLOUD_AUTH_MODE_CERT)
|
||||
__QCLOUD_STATIC__ qcloud_err_t qcloud_tls_init_with_cert(qcloud_tls_opt_t *tls_opt, qcloud_device_t *device)
|
||||
{
|
||||
if (!device->cert_path || !device->priv_key_path) {
|
||||
QCLOUD_LOG_E("cert file or key file is empty!");
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_INVAL);
|
||||
}
|
||||
|
||||
QCLOUD_LOG_D("cert path: %s", device->cert_path);
|
||||
QCLOUD_LOG_D("priv key path: %s", device->priv_key_path);
|
||||
|
||||
tls_opt->cert_path = device->cert_path;
|
||||
tls_opt->priv_key_path = device->priv_key_path;
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
|
||||
}
|
||||
#else
|
||||
|
||||
__QCLOUD_STATIC__ qcloud_err_t qcloud_tls_psk_id_generate(qcloud_tls_opt_t *tls_opt, qcloud_device_t *device)
|
||||
{
|
||||
int psk_id_len = 0;
|
||||
|
||||
memset(tls_opt->psk_id, 0, sizeof(tls_opt->psk_id));
|
||||
|
||||
psk_id_len = osal_snprintf(tls_opt->psk_id, QCLOUD_MQTT_DEVICE_CLIENT_ID_MAX, "%s%s", device->product_id, device->device_name);
|
||||
if (psk_id_len < 0 || psk_id_len >= QCLOUD_MQTT_DEVICE_CLIENT_ID_MAX) {
|
||||
QCLOUD_LOG_E("psk id generate failed");
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_DEV_INFO);
|
||||
}
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ qcloud_err_t qcloud_tls_psk_generate(qcloud_tls_opt_t *tls_opt, qcloud_device_t *device)
|
||||
{
|
||||
size_t key_len = 0, decoded_psk_len;
|
||||
|
||||
if (!device->key) {
|
||||
QCLOUD_LOG_E("device key NULL!");
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_INVAL);
|
||||
}
|
||||
|
||||
memset(tls_opt->psk, 0, sizeof(tls_opt->psk));
|
||||
|
||||
key_len = strlen(device->key);
|
||||
if (qcloud_utils_base64_decode(tls_opt->psk, sizeof(tls_opt->psk), &decoded_psk_len,
|
||||
(const unsigned char *)device->key, key_len) != 0) {
|
||||
QCLOUD_LOG_E("psk decode failed!");
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_INVAL);
|
||||
}
|
||||
|
||||
tls_opt->psk_len = decoded_psk_len;
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ qcloud_err_t qcloud_tls_init_with_key(qcloud_tls_opt_t *tls_opt, qcloud_device_t *device)
|
||||
{
|
||||
QCLOUD_FUNC_EXIT_RC_IF_NOT(qcloud_tls_psk_generate(tls_opt, device), QCLOUD_ERR_SUCCESS, QCLOUD_ERR_FAILURE);
|
||||
QCLOUD_FUNC_EXIT_RC_IF_NOT(qcloud_tls_psk_id_generate(tls_opt, device), QCLOUD_ERR_SUCCESS, QCLOUD_ERR_FAILURE);
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
__QCLOUD_INTERNAL__ qcloud_err_t qcloud_tls_init(qcloud_tls_opt_t *tls_opt, qcloud_device_t *device)
|
||||
{
|
||||
#if (QCLOUD_CFG_AUTH_MODE == QCLOUD_AUTH_MODE_CERT)
|
||||
QCLOUD_FUNC_EXIT_RC_IF_NOT(qcloud_tls_init_with_cert(tls_opt, device), QCLOUD_ERR_SUCCESS, QCLOUD_ERR_FAILURE);
|
||||
#else
|
||||
QCLOUD_FUNC_EXIT_RC_IF_NOT(qcloud_tls_init_with_key(tls_opt, device), QCLOUD_ERR_SUCCESS, QCLOUD_ERR_FAILURE);
|
||||
#endif
|
||||
|
||||
tls_opt->ca_cert = qcloud_ca_cert;
|
||||
tls_opt->ca_cert_len = strlen(tls_opt->ca_cert);
|
||||
tls_opt->handle = NULL;
|
||||
tls_opt->timeout = QCLOUD_TLS_HANDSHAKE_TIMEOUT;
|
||||
|
||||
return QCLOUD_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ qcloud_err_t qcloud_tls_connect(qcloud_network_t *network)
|
||||
{
|
||||
QCLOUD_POINTER_SANITY_CHECK(network, QCLOUD_ERR_INVAL);
|
||||
|
||||
network->tls_opt.handle = osal_tls_connect(&(network->tls_opt), network->host, network->port);
|
||||
if (network->tls_opt.handle) {
|
||||
return QCLOUD_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
return QCLOUD_ERR_FAILURE;
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ void qcloud_tls_disconnect(qcloud_network_t *network)
|
||||
{
|
||||
if (!network || !network->tls_opt.handle) {
|
||||
return;
|
||||
}
|
||||
|
||||
osal_tls_disconnect(network->tls_opt.handle);
|
||||
network->tls_opt.handle = NULL;
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ qcloud_err_t qcloud_tls_read(qcloud_network_t *network, void *buf, size_t len, uint32_t timeout, size_t *read_len)
|
||||
{
|
||||
QCLOUD_POINTER_SANITY_CHECK(network, QCLOUD_ERR_INVAL);
|
||||
|
||||
return osal_tls_read(network->tls_opt.handle, buf, len, timeout, read_len);
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ qcloud_err_t qcloud_tls_write(qcloud_network_t *network, const void *buf, size_t len, uint32_t timeout, size_t *write_len)
|
||||
{
|
||||
QCLOUD_POINTER_SANITY_CHECK(network, QCLOUD_ERR_INVAL);
|
||||
|
||||
return osal_tls_write(network->tls_opt.handle, buf, len, timeout, write_len);
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ int qcloud_tls_is_connected(qcloud_network_t *network)
|
||||
{
|
||||
return network->tls_opt.handle != NULL;
|
||||
}
|
||||
|
||||
__QCLOUD_INTERNAL__ qcloud_err_t qcloud_network_tls_init(qcloud_network_t *network)
|
||||
{
|
||||
QCLOUD_POINTER_SANITY_CHECK(network, QCLOUD_ERR_INVAL);
|
||||
|
||||
network->connect = qcloud_tls_connect;
|
||||
network->read = qcloud_tls_read;
|
||||
network->write = qcloud_tls_write;
|
||||
network->disconnect = qcloud_tls_disconnect;
|
||||
|
||||
network->is_connected = qcloud_tls_is_connected;
|
||||
|
||||
return QCLOUD_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
__QCLOUD_INTERNAL__ qcloud_err_t qcloud_dtls_connect(qcloud_network_t *network)
|
||||
{
|
||||
QCLOUD_POINTER_SANITY_CHECK(network, QCLOUD_ERR_INVAL);
|
||||
|
||||
network->tls_opt.handle = osal_dtls_connect(&(network->tls_opt), network->host, network->port);
|
||||
if (network->tls_opt.handle != 0) {
|
||||
return QCLOUD_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
return QCLOUD_ERR_FAILURE;
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ void qcloud_dtls_disconnect(qcloud_network_t *network)
|
||||
{
|
||||
if (!network || !network->tls_opt.handle) {
|
||||
return;
|
||||
}
|
||||
|
||||
osal_dtls_disconnect(network->tls_opt.handle);
|
||||
network->tls_opt.handle = NULL;
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ qcloud_err_t qcloud_dtls_read(qcloud_network_t *network, void *buf, size_t len, uint32_t timeout, size_t *read_len)
|
||||
{
|
||||
QCLOUD_POINTER_SANITY_CHECK(network, QCLOUD_ERR_INVAL);
|
||||
|
||||
return osal_dtls_read(network->tls_opt.handle, buf, len, timeout, read_len);
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ qcloud_err_t qcloud_dtls_write(qcloud_network_t *network, const void *buf, size_t len, uint32_t timeout, size_t *write_len)
|
||||
{
|
||||
QCLOUD_POINTER_SANITY_CHECK(network, QCLOUD_ERR_INVAL);
|
||||
|
||||
return osal_dtls_write(network->tls_opt.handle, buf, len, write_len);
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ int qcloud_dtls_is_connected(qcloud_network_t *network)
|
||||
{
|
||||
return network->tls_opt.handle != NULL;
|
||||
}
|
||||
|
||||
__QCLOUD_INTERNAL__ qcloud_err_t qcloud_network_dtls_init(qcloud_network_t *network)
|
||||
{
|
||||
QCLOUD_POINTER_SANITY_CHECK(network, QCLOUD_ERR_INVAL);
|
||||
|
||||
network->connect = qcloud_dtls_connect;
|
||||
network->read = qcloud_dtls_read;
|
||||
network->write = qcloud_dtls_write;
|
||||
network->disconnect = qcloud_dtls_disconnect;
|
||||
|
||||
network->is_connected = qcloud_dtls_is_connected;
|
||||
|
||||
return QCLOUD_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@@ -0,0 +1,936 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright (C) 2016 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 "qcloud.h"
|
||||
|
||||
__QCLOUD_STATIC__ qcloud_err_t shadow_client_construct(qcloud_shadow_client_t *client,
|
||||
mqtt_event_handler_fn_t handler,
|
||||
shadow_type_t shadow_type,
|
||||
qcloud_device_t *device)
|
||||
{
|
||||
int topic_len = 0;
|
||||
|
||||
client->shadow_type = shadow_type;
|
||||
client->event_handler.handler = handler;
|
||||
|
||||
client->version = 0;
|
||||
client->token_num = 0;
|
||||
client->sync_state = QCLOUD_SHADOW_SYNC_STATE_NONE;
|
||||
|
||||
client->request_list_counter = 0;
|
||||
qcloud_list_init(&client->request_list);
|
||||
|
||||
qcloud_list_init(&client->property_list);
|
||||
|
||||
memset(client->request_topic_subscribe, 0, sizeof(client->request_topic_subscribe));
|
||||
|
||||
if (shadow_type == SHADOW_TYPE_TEMPLATE) {
|
||||
topic_len = osal_snprintf(client->request_topic_subscribe, QCLOUD_MQTT_TOPIC_SIZE_MAX,
|
||||
"$template/operation/result/%s/%s",
|
||||
device->product_id, device->device_name);
|
||||
} else {
|
||||
topic_len = osal_snprintf(client->request_topic_subscribe, QCLOUD_MQTT_TOPIC_SIZE_MAX,
|
||||
"$shadow/operation/result/%s/%s",
|
||||
device->product_id, device->device_name);
|
||||
}
|
||||
|
||||
if (topic_len < 0 || topic_len >= QCLOUD_MQTT_TOPIC_SIZE_MAX) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
|
||||
}
|
||||
|
||||
memset(client->request_topic_publish, 0, sizeof(client->request_topic_publish));
|
||||
|
||||
if (shadow_type == SHADOW_TYPE_TEMPLATE) {
|
||||
topic_len = osal_snprintf(client->request_topic_publish, QCLOUD_MQTT_TOPIC_SIZE_MAX,
|
||||
"$template/operation/%s/%s",
|
||||
device->product_id, device->device_name);
|
||||
} else {
|
||||
topic_len = osal_snprintf(client->request_topic_publish, QCLOUD_MQTT_TOPIC_SIZE_MAX,
|
||||
"$shadow/operation/%s/%s",
|
||||
device->product_id, device->device_name);
|
||||
}
|
||||
|
||||
if (topic_len < 0 || topic_len >= QCLOUD_MQTT_TOPIC_SIZE_MAX) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
|
||||
}
|
||||
|
||||
strncpy(client->device_product_id, device->product_id, sizeof(client->device_product_id));
|
||||
client->device_product_id[QCLOUD_DEVICE_PRODUCT_ID_MAX] = '\0';
|
||||
|
||||
client->global_lock = osal_mutex_create();
|
||||
if (!client->global_lock) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
|
||||
}
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ void shadow_incoming_msg_handler(void *client, void *context, mqtt_event_t *event)
|
||||
{
|
||||
uint16_t packet_id;
|
||||
mqtt_incoming_msg_t *mqtt_msg;
|
||||
qcloud_shadow_client_t *shadow_client = NULL;
|
||||
|
||||
shadow_client = (qcloud_shadow_client_t *)context;
|
||||
|
||||
switch (event->type) {
|
||||
case MQTT_EVENT_SUBCRIBE_SUCCESS:
|
||||
packet_id = *(uint16_t *)event->message;
|
||||
QCLOUD_LOG_D("subscribe success, packet id=%u", (uint32_t)packet_id);
|
||||
shadow_client->sync_state = QCLOUD_SHADOW_SYNC_STATE_SUCCESS;
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_SUBCRIBE_TIMEOUT:
|
||||
packet_id = *(uint16_t *)event->message;
|
||||
QCLOUD_LOG_D("subscribe wait ack timeout, packet id=%u", (uint32_t)packet_id);
|
||||
shadow_client->sync_state = QCLOUD_SHADOW_SYNC_STATE_TIMEOUT;
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_SUBCRIBE_NACK:
|
||||
packet_id = *(uint16_t *)event->message;
|
||||
QCLOUD_LOG_D("subscribe nack, packet id=%u", (uint32_t)packet_id);
|
||||
shadow_client->sync_state = QCLOUD_SHADOW_SYNC_STATE_NACK;
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_PUBLISH_RECVEIVED:
|
||||
mqtt_msg = (mqtt_incoming_msg_t *)event->message;
|
||||
QCLOUD_LOG_D("topic arrived without handler: topic=%.*s, payload=%.*s",
|
||||
mqtt_msg->topic_len,
|
||||
mqtt_msg->topic,
|
||||
mqtt_msg->payload_len,
|
||||
mqtt_msg->payload);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (shadow_client->event_handler.handler) {
|
||||
shadow_client->event_handler.handler(shadow_client, shadow_client->event_handler.context, event);
|
||||
}
|
||||
}
|
||||
|
||||
__QCLOUD_API__ qcloud_err_t qcloud_shadow_client_create(qcloud_shadow_client_t *client,
|
||||
qcloud_device_t *device,
|
||||
mqtt_event_handler_fn_t handler,
|
||||
shadow_type_t shadow_type)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(device, QCLOUD_ERR_INVAL);
|
||||
|
||||
qcloud_err_t rc;
|
||||
|
||||
memset(client, 0, sizeof(qcloud_shadow_client_t));
|
||||
|
||||
rc = qcloud_mqtt_client_create(&client->mqtt_client, device, shadow_incoming_msg_handler, (void *)client, QCLOUD_AUTO_CONN_STATE_ENABLED);
|
||||
QCLOUD_FUNC_EXIT_RC_IF_NOT(rc, QCLOUD_ERR_SUCCESS, QCLOUD_ERR_FAILURE);
|
||||
|
||||
rc = qcloud_mqtt_connect_opt_create(&client->mqtt_connect_opt, device, MQTT_VERSION_3_1_1, 240, MQTT_CLEAN_SESSION_STATE_ENABLED);
|
||||
if (rc != QCLOUD_ERR_SUCCESS) {
|
||||
goto errout;
|
||||
}
|
||||
|
||||
rc = shadow_client_construct(client, handler, shadow_type, device);
|
||||
if (rc != QCLOUD_ERR_SUCCESS) {
|
||||
goto errout;
|
||||
}
|
||||
|
||||
rc = qcloud_mqtt_client_connect(&client->mqtt_client, &client->mqtt_connect_opt);
|
||||
if (rc != QCLOUD_ERR_SUCCESS) {
|
||||
goto errout;
|
||||
}
|
||||
|
||||
rc = shadow_glue_operation_request_subscribe(client);
|
||||
if (rc != QCLOUD_ERR_SUCCESS) {
|
||||
goto errout;
|
||||
}
|
||||
|
||||
client->sync_state = QCLOUD_SHADOW_SYNC_STATE_PENDACK;
|
||||
|
||||
while (client->sync_state == QCLOUD_SHADOW_SYNC_STATE_PENDACK) {
|
||||
qcloud_shadow_client_yield(client, 100);
|
||||
}
|
||||
|
||||
if (client->sync_state != QCLOUD_SHADOW_SYNC_STATE_SUCCESS) {
|
||||
goto errout;
|
||||
}
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
|
||||
|
||||
errout:
|
||||
qcloud_mqtt_client_destroy(&client->mqtt_client);
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
|
||||
}
|
||||
|
||||
__QCLOUD_API__ qcloud_err_t qcloud_shadow_client_yield(qcloud_shadow_client_t *client, uint32_t timeout)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_NUMBERIC_SANITY_CHECK(timeout, QCLOUD_ERR_INVAL);
|
||||
|
||||
shadow_glue_request_list_scan(client);
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(qcloud_mqtt_client_yield(&client->mqtt_client, &client->mqtt_connect_opt, timeout));
|
||||
}
|
||||
|
||||
__QCLOUD_API__ qcloud_err_t qcloud_shadow_client_publish(qcloud_shadow_client_t *client, char *topic, mqtt_publish_opt_t *publish_opt)
|
||||
{
|
||||
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(topic, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(publish_opt, QCLOUD_ERR_INVAL);
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(qcloud_mqtt_publish(&client->mqtt_client, topic, publish_opt));
|
||||
}
|
||||
|
||||
__QCLOUD_API__ qcloud_err_t qcloud_shadow_client_subscribe(qcloud_shadow_client_t *client, const char *topic_filter, mqtt_subscribe_opt_t *subscribe_opt)
|
||||
{
|
||||
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(topic_filter, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(subscribe_opt, QCLOUD_ERR_INVAL);
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(qcloud_mqtt_subscribe(&client->mqtt_client, topic_filter, subscribe_opt));
|
||||
}
|
||||
|
||||
__QCLOUD_API__ qcloud_err_t qcloud_shadow_client_unsubscribe(qcloud_shadow_client_t *client, const char *topic_filter)
|
||||
|
||||
{
|
||||
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(topic_filter, QCLOUD_ERR_INVAL);
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(qcloud_mqtt_unsubscribe(&client->mqtt_client, topic_filter));
|
||||
}
|
||||
|
||||
__QCLOUD_API__ int qcloud_shadow_client_is_connected(qcloud_shadow_client_t *client)
|
||||
{
|
||||
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
|
||||
return qcloud_mqtt_client_is_connected(&client->mqtt_client);
|
||||
}
|
||||
|
||||
__QCLOUD_API__ qcloud_err_t qcloud_shadow_client_destroy(qcloud_shadow_client_t *client)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
|
||||
shadow_glue_operation_request_unsubscribe(client);
|
||||
|
||||
shadow_glue_property_list_destroy(client);
|
||||
shadow_glue_request_list_destroy(client);
|
||||
|
||||
qcloud_mqtt_client_destroy(&client->mqtt_client);
|
||||
|
||||
if (client->global_lock) {
|
||||
osal_mutex_destroy(client->global_lock);
|
||||
}
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS)
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ int shadow_device_property_is_exist(qcloud_shadow_client_t *client, shadow_dev_property_t *that_dev_property)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
qcloud_list_t *curr, *next;
|
||||
qcloud_shadow_prop_info_t *property_info = NULL;
|
||||
shadow_dev_property_t *this_dev_property = NULL;
|
||||
|
||||
if (qcloud_list_empty(&client->property_list)) {
|
||||
return QCLOUD_FALSE;
|
||||
}
|
||||
|
||||
osal_mutex_lock(client->global_lock);
|
||||
|
||||
QCLOUD_LIST_FOR_EACH_SAFE(curr, next, &client->property_list) {
|
||||
property_info = QCLOUD_LIST_ENTRY(curr, qcloud_shadow_prop_info_t, list);
|
||||
this_dev_property = property_info->dev_property;
|
||||
|
||||
if (strcmp(this_dev_property->key, that_dev_property->key) != 0 ||
|
||||
this_dev_property->type != that_dev_property->type) {
|
||||
continue;
|
||||
}
|
||||
|
||||
osal_mutex_unlock(client->global_lock);
|
||||
return QCLOUD_TRUE;
|
||||
}
|
||||
|
||||
osal_mutex_unlock(client->global_lock);
|
||||
|
||||
return QCLOUD_FALSE;
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ qcloud_err_t shadow_device_property_do_register(qcloud_shadow_client_t *client,
|
||||
shadow_dev_property_t *dev_property,
|
||||
shadow_property_delta_handler_fn_t handler)
|
||||
{
|
||||
qcloud_shadow_prop_info_t *property_info = NULL;
|
||||
|
||||
property_info = (qcloud_shadow_prop_info_t *)osal_malloc(sizeof(qcloud_shadow_prop_info_t));
|
||||
if (!property_info) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_INVAL);
|
||||
}
|
||||
|
||||
qcloud_list_init(&property_info->list);
|
||||
property_info->dev_property = dev_property;
|
||||
property_info->handler = handler;
|
||||
qcloud_list_add(&property_info->list, &client->property_list);
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
|
||||
}
|
||||
|
||||
__QCLOUD_API__ qcloud_err_t qcloud_shadow_device_property_register(qcloud_shadow_client_t *client,
|
||||
shadow_dev_property_t *dev_property,
|
||||
shadow_property_delta_handler_fn_t handler)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(dev_property, QCLOUD_ERR_INVAL);
|
||||
|
||||
if (!qcloud_mqtt_client_is_connected(&client->mqtt_client)) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_NO_CONN);
|
||||
}
|
||||
|
||||
if (shadow_device_property_is_exist(client, dev_property)) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SHADOW_PROPERTY_EXIST);
|
||||
}
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(shadow_device_property_do_register(client, dev_property, handler));
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ qcloud_err_t shadow_device_property_do_unregister(qcloud_shadow_client_t *client, shadow_dev_property_t *that_dev_property)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
qcloud_list_t *curr, *next;
|
||||
qcloud_shadow_prop_info_t *property_info = NULL;
|
||||
shadow_dev_property_t *this_dev_property = NULL;
|
||||
|
||||
if (qcloud_list_empty(&client->property_list)) {
|
||||
return QCLOUD_ERR_FAILURE;
|
||||
}
|
||||
|
||||
osal_mutex_lock(client->global_lock);
|
||||
|
||||
QCLOUD_LIST_FOR_EACH_SAFE(curr, next, &client->property_list) {
|
||||
property_info = QCLOUD_LIST_ENTRY(curr, qcloud_shadow_prop_info_t, list);
|
||||
this_dev_property = property_info->dev_property;
|
||||
|
||||
if (strcmp(this_dev_property->key, that_dev_property->key) != 0 ||
|
||||
this_dev_property->type != that_dev_property->type) {
|
||||
continue;
|
||||
}
|
||||
|
||||
qcloud_list_del(&property_info->list);
|
||||
osal_free(property_info);
|
||||
|
||||
osal_mutex_unlock(client->global_lock);
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
|
||||
}
|
||||
|
||||
osal_mutex_unlock(client->global_lock);
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
|
||||
}
|
||||
|
||||
__QCLOUD_API__ qcloud_err_t qcloud_shadow_device_property_unregister(qcloud_shadow_client_t *client, shadow_dev_property_t *dev_property)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(dev_property, QCLOUD_ERR_INVAL);
|
||||
|
||||
if (!qcloud_mqtt_client_is_connected(&client->mqtt_client)) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_NO_CONN);
|
||||
}
|
||||
|
||||
if (!shadow_device_property_is_exist(client, dev_property)) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SHADOW_NOT_PROPERTY_EXIST);
|
||||
}
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(shadow_device_property_do_unregister(client, dev_property));
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ void shadow_req_state_update_handler(void *client, qcloud_shadow_req_method_t method, qcloud_shadow_req_state_t req_state, const char *json_doc, void *context)
|
||||
{
|
||||
QCLOUD_LOG_D("request state=%d", req_state);
|
||||
|
||||
if (json_doc) {
|
||||
QCLOUD_LOG_D("json doc=%s", json_doc);
|
||||
} else {
|
||||
QCLOUD_LOG_D("json doc NULL");
|
||||
}
|
||||
|
||||
*((qcloud_shadow_req_state_t *)context) = req_state;
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ qcloud_err_t shadow_request_state2errno(qcloud_shadow_req_state_t state)
|
||||
{
|
||||
switch (state) {
|
||||
case QCLOUD_SHADOW_REQUEST_STATE_ACCEPTED:
|
||||
return QCLOUD_ERR_SUCCESS;
|
||||
|
||||
case QCLOUD_SHADOW_REQUEST_STATE_TIMEOUT:
|
||||
return QCLOUD_ERR_SHADOW_UPDATE_TIMEOUT;
|
||||
|
||||
case QCLOUD_SHADOW_REQUEST_STATE_REJECTED:
|
||||
return QCLOUD_ERR_SHADOW_UPDATE_REJECTED;
|
||||
}
|
||||
|
||||
return QCLOUD_ERR_INVAL;
|
||||
}
|
||||
|
||||
__QCLOUD_API__ qcloud_err_t qcloud_shadow_client_update_async(qcloud_shadow_client_t *client,
|
||||
char *json_doc,
|
||||
size_t json_doc_size,
|
||||
shadow_requset_handler_fn_t handler,
|
||||
void *context,
|
||||
uint32_t timeout)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(json_doc, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_NUMBERIC_SANITY_CHECK(timeout, QCLOUD_ERR_INVAL);
|
||||
|
||||
qcloud_shadow_req_opt_t request_opt;
|
||||
|
||||
if (!qcloud_mqtt_client_is_connected(&client->mqtt_client)) {
|
||||
QCLOUD_LOG_E("mqtt disconnected");
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_NO_CONN);
|
||||
}
|
||||
|
||||
// 如果没有之前没有订阅$shadow/operation/result成功,再一次订阅
|
||||
if (client->sync_state != QCLOUD_SHADOW_SYNC_STATE_SUCCESS) {
|
||||
shadow_glue_operation_request_subscribe(client);
|
||||
}
|
||||
|
||||
QCLOUD_LOG_D("update request docment: %s", json_doc);
|
||||
|
||||
request_opt.method = QCLOUD_SHADOW_REQUEST_METHOD_UPDATE;
|
||||
request_opt.handler = handler;
|
||||
request_opt.context = context;
|
||||
request_opt.timeout = timeout; // in seconds
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(shadow_glue_request_post(client, &request_opt, json_doc, json_doc_size));
|
||||
}
|
||||
|
||||
__QCLOUD_API__ qcloud_err_t qcloud_shadow_client_update_sync(qcloud_shadow_client_t *client, char *json_doc, size_t json_doc_size, uint32_t timeout)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(json_doc, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_NUMBERIC_SANITY_CHECK(timeout, QCLOUD_ERR_INVAL);
|
||||
|
||||
qcloud_err_t rc = QCLOUD_ERR_SUCCESS;
|
||||
qcloud_shadow_req_state_t req_state = QCLOUD_SHADOW_REQUEST_STATE_NONE;
|
||||
|
||||
if (!qcloud_mqtt_client_is_connected(&client->mqtt_client)) {
|
||||
QCLOUD_LOG_E("shadow disconnected");
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_NO_CONN);
|
||||
}
|
||||
|
||||
rc = qcloud_shadow_client_update_async(client, json_doc, json_doc_size, shadow_req_state_update_handler, &req_state, timeout);
|
||||
QCLOUD_FUNC_EXIT_RC_IF_NOT(rc, QCLOUD_ERR_SUCCESS, QCLOUD_ERR_FAILURE);
|
||||
|
||||
while (req_state == QCLOUD_SHADOW_REQUEST_STATE_NONE) {
|
||||
qcloud_shadow_client_yield(client, 200);
|
||||
}
|
||||
|
||||
return shadow_request_state2errno(req_state);
|
||||
}
|
||||
|
||||
__QCLOUD_API__ qcloud_err_t qcloud_shadow_client_get_async(qcloud_shadow_client_t *client,
|
||||
shadow_requset_handler_fn_t handler,
|
||||
void *context,
|
||||
uint32_t timeout)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(handler, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_NUMBERIC_SANITY_CHECK(timeout, QCLOUD_ERR_INVAL);
|
||||
|
||||
qcloud_err_t rc;
|
||||
qcloud_shadow_req_opt_t request_opt;
|
||||
char request_json_buffer[QCLOUD_SHADOW_JSON_WITH_CLIENT_TOKEN_MAX];
|
||||
|
||||
if (!qcloud_mqtt_client_is_connected(&client->mqtt_client)) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_NO_CONN);
|
||||
}
|
||||
|
||||
// 如果没有之前没有订阅$shadow/operation/result成功,再一次订阅
|
||||
if (client->sync_state != QCLOUD_SHADOW_SYNC_STATE_SUCCESS) {
|
||||
shadow_glue_operation_request_subscribe(client);
|
||||
}
|
||||
|
||||
++client->token_num;
|
||||
rc = shadow_json_empty_doc_build(request_json_buffer, client->token_num, client->device_product_id);
|
||||
QCLOUD_FUNC_EXIT_RC_IF_NOT(rc, QCLOUD_ERR_SUCCESS, rc);
|
||||
QCLOUD_LOG_D("get request document: %s", request_json_buffer);
|
||||
|
||||
request_opt.method = QCLOUD_SHADOW_REQUEST_METHOD_GET;
|
||||
request_opt.handler = handler;
|
||||
request_opt.context = context;
|
||||
request_opt.timeout = timeout; // in seconds
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(shadow_glue_request_post(client, &request_opt, request_json_buffer, sizeof(request_json_buffer)));
|
||||
}
|
||||
|
||||
__QCLOUD_API__ qcloud_err_t qcloud_shadow_client_get_sync(qcloud_shadow_client_t *client, uint32_t timeout)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_NUMBERIC_SANITY_CHECK(timeout, QCLOUD_ERR_INVAL);
|
||||
|
||||
qcloud_err_t rc = QCLOUD_ERR_SUCCESS;
|
||||
qcloud_shadow_req_state_t req_state = QCLOUD_SHADOW_REQUEST_STATE_NONE;
|
||||
|
||||
if (!qcloud_mqtt_client_is_connected(&client->mqtt_client)) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_NO_CONN);
|
||||
}
|
||||
|
||||
rc = qcloud_shadow_client_get_async(client, shadow_req_state_update_handler, &req_state, timeout);
|
||||
QCLOUD_FUNC_EXIT_RC_IF_NOT(rc, QCLOUD_ERR_SUCCESS, QCLOUD_ERR_FAILURE);
|
||||
|
||||
while (req_state == QCLOUD_SHADOW_REQUEST_STATE_NONE) {
|
||||
qcloud_shadow_client_yield(client, 200);
|
||||
}
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(shadow_request_state2errno(req_state));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 初始化一个JSON文档
|
||||
*
|
||||
* 本函数主要是为JSON文档添加state字段, 即 "{\"state\":{", 所以在生成JSON文档时, 请先调用该方法
|
||||
*
|
||||
* @param jsonBuffer 为存储JSON文档准备的字符串缓冲区
|
||||
* @param sizeOfBuffer 缓冲区大小
|
||||
* @return 返回QCLOUD_ERR_SUCCESS, 表示初始化成功
|
||||
*/
|
||||
__QCLOUD_STATIC__ qcloud_err_t shadow_jsondoc_init(qcloud_shadow_client_t *client,
|
||||
char *json_doc,
|
||||
size_t json_doc_size,
|
||||
int is_overwrite)
|
||||
{
|
||||
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(json_doc, QCLOUD_ERR_INVAL);
|
||||
|
||||
int rc = 0;
|
||||
|
||||
if (is_overwrite) {
|
||||
rc = osal_snprintf(json_doc, json_doc_size, "{\"version\":%d, \"overwriteUpdate\":true, \"state\":{", client->version);
|
||||
} else {
|
||||
rc = osal_snprintf(json_doc, json_doc_size, "{\"version\":%d, \"state\":{", client->version);
|
||||
}
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(shadow_json_snprintf_rc2errno(rc, json_doc_size));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 在JSON文档中添加结尾部分的内容, 包括clientToken字段、version字段
|
||||
*
|
||||
* @param jsonBuffer 为存储JSON文档准备的字符串缓冲区
|
||||
* @param sizeOfBuffer 缓冲区大小
|
||||
* @return 返回QCLOUD_ERR_SUCCESS, 表示成功
|
||||
*/
|
||||
__QCLOUD_STATIC__ qcloud_err_t shadow_jsondoc_finalize(qcloud_shadow_client_t *client, char *json_doc, size_t json_doc_size)
|
||||
{
|
||||
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(json_doc, QCLOUD_ERR_INVAL);
|
||||
|
||||
int rc_snprintf;
|
||||
qcloud_err_t rc;
|
||||
size_t remain_size = 0;
|
||||
|
||||
if ((remain_size = json_doc_size - strlen(json_doc)) <= 1) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_JSON_BUFFER_TOO_SHORT);
|
||||
}
|
||||
|
||||
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc) - 1, remain_size, "}, \"%s\":\"", SHADOW_FIELD_CLIENT_TOKEN);
|
||||
rc = shadow_json_snprintf_rc2errno(rc_snprintf, remain_size);
|
||||
QCLOUD_FUNC_EXIT_RC_IF_NOT(rc, QCLOUD_ERR_SUCCESS, rc);
|
||||
|
||||
if ((remain_size = json_doc_size - strlen(json_doc)) <= 1) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_JSON_BUFFER_TOO_SHORT);
|
||||
}
|
||||
|
||||
++client->token_num;
|
||||
rc = shadow_json_client_token_generate(json_doc + strlen(json_doc), remain_size, client->token_num, client->device_product_id);
|
||||
QCLOUD_FUNC_EXIT_RC_IF_NOT(rc, QCLOUD_ERR_SUCCESS, rc);
|
||||
|
||||
if ((remain_size = json_doc_size - strlen(json_doc)) <= 1) {
|
||||
return QCLOUD_ERR_JSON_BUFFER_TOO_SHORT;
|
||||
}
|
||||
|
||||
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size, "\"}");
|
||||
QCLOUD_FUNC_EXIT_RC(shadow_json_snprintf_rc2errno(rc_snprintf, remain_size));
|
||||
}
|
||||
|
||||
__QCLOUD_API__ qcloud_err_t qcloud_shadow_client_report_construct(qcloud_shadow_client_t *client,
|
||||
char *json_doc,
|
||||
size_t json_doc_size,
|
||||
int count, ...)
|
||||
{
|
||||
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(json_doc, QCLOUD_ERR_INVAL);
|
||||
|
||||
qcloud_err_t rc;
|
||||
va_list args;
|
||||
size_t remain_size = 0;
|
||||
int i = 0, rc_snprintf = 0;
|
||||
shadow_dev_property_t *dev_property;
|
||||
|
||||
rc = shadow_jsondoc_init(client, json_doc, json_doc_size, QCLOUD_FALSE);
|
||||
if (rc != QCLOUD_ERR_SUCCESS) {
|
||||
QCLOUD_LOG_E("json init failed: %d", rc);
|
||||
QCLOUD_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
if ((remain_size = json_doc_size - strlen(json_doc)) <= 1) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_JSON_BUFFER_TOO_SHORT);
|
||||
}
|
||||
|
||||
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size, "\"reported\":{");
|
||||
rc = shadow_json_snprintf_rc2errno(rc_snprintf, remain_size);
|
||||
QCLOUD_FUNC_EXIT_RC_IF_NOT(rc, QCLOUD_ERR_SUCCESS, rc);
|
||||
|
||||
va_start(args, count);
|
||||
|
||||
for (i = 0; i < count; ++i) {
|
||||
dev_property = va_arg(args, shadow_dev_property_t *);
|
||||
if (!dev_property || !dev_property->key) {
|
||||
va_end(args);
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_INVAL);
|
||||
}
|
||||
|
||||
rc = shadow_json_node_add(json_doc, remain_size, dev_property->key, dev_property->data, dev_property->type);
|
||||
if (rc != QCLOUD_ERR_SUCCESS) {
|
||||
va_end(args);
|
||||
QCLOUD_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
}
|
||||
|
||||
va_end(args);
|
||||
|
||||
if ((remain_size = json_doc_size - strlen(json_doc)) <= 1) {
|
||||
return QCLOUD_ERR_JSON_BUFFER_TOO_SHORT;
|
||||
}
|
||||
|
||||
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc) - 1, remain_size, "},");
|
||||
rc = shadow_json_snprintf_rc2errno(rc_snprintf, remain_size);
|
||||
if (rc != QCLOUD_ERR_SUCCESS) {
|
||||
QCLOUD_LOG_E("json add report failed: %d", rc);
|
||||
QCLOUD_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
rc = shadow_jsondoc_finalize(client, json_doc, json_doc_size);
|
||||
if (rc != QCLOUD_ERR_SUCCESS) {
|
||||
QCLOUD_LOG_E("json finalize failed: %d", rc);
|
||||
}
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
__QCLOUD_API__ qcloud_err_t qcloud_shadow_client_report_construct_array(qcloud_shadow_client_t *client,
|
||||
char *json_doc,
|
||||
size_t json_doc_size,
|
||||
int count,
|
||||
shadow_dev_property_t *dev_propertys[])
|
||||
{
|
||||
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(json_doc, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(dev_propertys, QCLOUD_ERR_INVAL);
|
||||
|
||||
qcloud_err_t rc;
|
||||
int8_t i = 0, rc_snprintf = 0;
|
||||
size_t remain_size = 0;
|
||||
shadow_dev_property_t *dev_property;
|
||||
|
||||
rc = shadow_jsondoc_init(client, json_doc, json_doc_size, QCLOUD_FALSE);
|
||||
if (rc != QCLOUD_ERR_SUCCESS) {
|
||||
QCLOUD_LOG_E("json init failed: %d", rc);
|
||||
QCLOUD_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
if ((remain_size = json_doc_size - strlen(json_doc)) <= 1) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_JSON_BUFFER_TOO_SHORT);
|
||||
}
|
||||
|
||||
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size, "\"reported\":{");
|
||||
rc = shadow_json_snprintf_rc2errno(rc_snprintf, remain_size);
|
||||
QCLOUD_FUNC_EXIT_RC_IF_NOT(rc, QCLOUD_ERR_SUCCESS, rc);
|
||||
|
||||
for (i = 0; i < count; ++i) {
|
||||
dev_property = dev_propertys[i];
|
||||
if (!dev_property || !dev_property->key) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_INVAL);
|
||||
}
|
||||
|
||||
rc = shadow_json_node_add(json_doc, remain_size, dev_property->key, dev_property->data, dev_property->type);
|
||||
QCLOUD_FUNC_EXIT_RC_IF_NOT(rc, QCLOUD_ERR_SUCCESS, rc);
|
||||
}
|
||||
|
||||
if ((remain_size = json_doc_size - strlen(json_doc)) <= 1) {
|
||||
return QCLOUD_ERR_JSON_BUFFER_TOO_SHORT;
|
||||
}
|
||||
|
||||
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc) - 1, remain_size, "},");
|
||||
rc = shadow_json_snprintf_rc2errno(rc_snprintf, remain_size);
|
||||
if (rc != QCLOUD_ERR_SUCCESS) {
|
||||
QCLOUD_LOG_E("json add report failed: %d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = shadow_jsondoc_finalize(client, json_doc, json_doc_size);
|
||||
if (rc != QCLOUD_ERR_SUCCESS) {
|
||||
QCLOUD_LOG_E("json finalize failed: %d", rc);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
__QCLOUD_API__ qcloud_err_t qcloud_shadow_client_overwrite_report_construct(qcloud_shadow_client_t *client,
|
||||
char *json_doc,
|
||||
size_t json_doc_size,
|
||||
int count, ...)
|
||||
{
|
||||
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(json_doc, QCLOUD_ERR_INVAL);
|
||||
|
||||
qcloud_err_t rc;
|
||||
va_list args;
|
||||
size_t remain_size = 0;
|
||||
int rc_snprintf = 0, i = 0;
|
||||
shadow_dev_property_t *dev_property;
|
||||
|
||||
rc = shadow_jsondoc_init(client, json_doc, json_doc_size, QCLOUD_TRUE);
|
||||
if (rc != QCLOUD_ERR_SUCCESS) {
|
||||
QCLOUD_LOG_E("json init failed: %d", rc);
|
||||
QCLOUD_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
if ((remain_size = json_doc_size - strlen(json_doc)) <= 1) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_JSON_BUFFER_TOO_SHORT);
|
||||
}
|
||||
|
||||
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size, "\"reported\":{");
|
||||
rc = shadow_json_snprintf_rc2errno(rc_snprintf, remain_size);
|
||||
QCLOUD_FUNC_EXIT_RC_IF_NOT(rc, QCLOUD_ERR_SUCCESS, rc);
|
||||
|
||||
va_start(args, count);
|
||||
|
||||
for (i = 0; i < count; ++i) {
|
||||
dev_property = va_arg(args, shadow_dev_property_t *);
|
||||
if (!dev_property || !dev_property->key) {
|
||||
va_end(args);
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_INVAL);
|
||||
}
|
||||
|
||||
rc = shadow_json_node_add(json_doc, remain_size, dev_property->key, dev_property->data, dev_property->type);
|
||||
if (rc != QCLOUD_ERR_SUCCESS) {
|
||||
va_end(args);
|
||||
QCLOUD_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
}
|
||||
|
||||
va_end(args);
|
||||
|
||||
if ((remain_size = json_doc_size - strlen(json_doc)) <= 1) {
|
||||
return QCLOUD_ERR_JSON_BUFFER_TOO_SHORT;
|
||||
}
|
||||
|
||||
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc) - 1, remain_size, "},");
|
||||
rc = shadow_json_snprintf_rc2errno(rc_snprintf, remain_size);
|
||||
if (rc != QCLOUD_ERR_SUCCESS) {
|
||||
QCLOUD_LOG_E("shadow json add report failed: %d", rc);
|
||||
QCLOUD_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
rc = shadow_jsondoc_finalize(client, json_doc, json_doc_size);
|
||||
if (rc != QCLOUD_ERR_SUCCESS) {
|
||||
QCLOUD_LOG_E("shadow json finalize failed: %d", rc);
|
||||
}
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
__QCLOUD_API__ qcloud_err_t qcloud_shadow_client_report_with_desire_null_construct(qcloud_shadow_client_t *client,
|
||||
char *json_doc,
|
||||
size_t json_doc_size,
|
||||
int count, ...)
|
||||
{
|
||||
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(json_doc, QCLOUD_ERR_INVAL);
|
||||
|
||||
qcloud_err_t rc;
|
||||
va_list args;
|
||||
size_t remain_size = 0;
|
||||
int rc_snprintf = 0, i = 0;
|
||||
shadow_dev_property_t *dev_property;
|
||||
|
||||
rc = shadow_jsondoc_init(client, json_doc, json_doc_size, QCLOUD_FALSE);
|
||||
if (rc != QCLOUD_ERR_SUCCESS) {
|
||||
QCLOUD_LOG_E("json init failed: %d", rc);
|
||||
QCLOUD_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
if ((remain_size = json_doc_size - strlen(json_doc)) <= 1) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_JSON_BUFFER_TOO_SHORT);
|
||||
}
|
||||
|
||||
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size, "\"reported\":{");
|
||||
rc = shadow_json_snprintf_rc2errno(rc_snprintf, remain_size);
|
||||
QCLOUD_FUNC_EXIT_RC_IF_NOT(rc, QCLOUD_ERR_SUCCESS, rc);
|
||||
|
||||
va_start(args, count);
|
||||
|
||||
for (i = 0; i < count; ++i) {
|
||||
dev_property = va_arg(args, shadow_dev_property_t *);
|
||||
|
||||
if (!dev_property || !dev_property->key) {
|
||||
va_end(args);
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_INVAL);
|
||||
}
|
||||
|
||||
rc = shadow_json_node_add(json_doc, remain_size, dev_property->key, dev_property->data, dev_property->type);
|
||||
if (rc != QCLOUD_ERR_SUCCESS) {
|
||||
va_end(args);
|
||||
QCLOUD_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
}
|
||||
|
||||
va_end(args);
|
||||
|
||||
if ((remain_size = json_doc_size - strlen(json_doc)) <= 1) {
|
||||
return QCLOUD_ERR_JSON_BUFFER_TOO_SHORT;
|
||||
}
|
||||
|
||||
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc) - 1, remain_size, "},");
|
||||
rc = shadow_json_snprintf_rc2errno(rc_snprintf, remain_size);
|
||||
if (rc != QCLOUD_ERR_SUCCESS) {
|
||||
QCLOUD_LOG_E("shadow json add report failed: %d", rc);
|
||||
QCLOUD_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size, "\"desired\": null ");
|
||||
rc = shadow_json_snprintf_rc2errno(rc_snprintf, remain_size);
|
||||
QCLOUD_FUNC_EXIT_RC_IF_NOT(rc, QCLOUD_ERR_SUCCESS, rc);
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(shadow_jsondoc_finalize(client, json_doc, json_doc_size));
|
||||
}
|
||||
|
||||
__QCLOUD_API__ qcloud_err_t qcloud_shadow_client_desire_null_construct(qcloud_shadow_client_t *client,
|
||||
char *json_doc,
|
||||
size_t json_doc_size)
|
||||
{
|
||||
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(json_doc, QCLOUD_ERR_INVAL);
|
||||
|
||||
qcloud_err_t rc;
|
||||
size_t remain_size = 0;
|
||||
int rc_snprintf = 0;
|
||||
|
||||
rc = shadow_jsondoc_init(client, json_doc, json_doc_size, QCLOUD_FALSE);
|
||||
if (rc != QCLOUD_ERR_SUCCESS) {
|
||||
QCLOUD_LOG_E("json init failed: %d", rc);
|
||||
QCLOUD_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
if ((remain_size = json_doc_size - strlen(json_doc)) <= 1) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_JSON_BUFFER_TOO_SHORT);
|
||||
}
|
||||
|
||||
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size, "\"desired\": null ");
|
||||
rc = shadow_json_snprintf_rc2errno(rc_snprintf, remain_size);
|
||||
QCLOUD_FUNC_EXIT_RC_IF_NOT(rc, QCLOUD_ERR_SUCCESS, rc);
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(shadow_jsondoc_finalize(client, json_doc, json_doc_size));
|
||||
}
|
||||
|
||||
__QCLOUD_API__ qcloud_err_t qcloud_shadow_client_desire_construct(qcloud_shadow_client_t *client,
|
||||
char *json_doc,
|
||||
size_t json_doc_size,
|
||||
int count, ...)
|
||||
{
|
||||
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(json_doc, QCLOUD_ERR_INVAL);
|
||||
|
||||
qcloud_err_t rc;
|
||||
va_list args;
|
||||
int i = 0, rc_snprintf = 0;
|
||||
size_t remain_size = 0;
|
||||
shadow_dev_property_t *dev_property;
|
||||
|
||||
rc = shadow_jsondoc_init(client, json_doc, json_doc_size, QCLOUD_FALSE);
|
||||
if (rc != QCLOUD_ERR_SUCCESS) {
|
||||
QCLOUD_LOG_E("shadow json init failed: %d", rc);
|
||||
QCLOUD_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
if ((remain_size = json_doc_size - strlen(json_doc)) <= 1) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_JSON_BUFFER_TOO_SHORT);
|
||||
}
|
||||
|
||||
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size, "\"desired\":{");
|
||||
rc = shadow_json_snprintf_rc2errno(rc_snprintf, remain_size);
|
||||
QCLOUD_FUNC_EXIT_RC_IF_NOT(rc, QCLOUD_ERR_SUCCESS, rc);
|
||||
|
||||
va_start(args, count);
|
||||
|
||||
for (i = 0; i < count; ++i) {
|
||||
dev_property = va_arg(args, shadow_dev_property_t *);
|
||||
if (!dev_property || !dev_property->key) {
|
||||
va_end(args);
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_INVAL);
|
||||
}
|
||||
|
||||
rc = shadow_json_node_add(json_doc, remain_size, dev_property->key, dev_property->data, dev_property->type);
|
||||
if (rc != QCLOUD_ERR_SUCCESS) {
|
||||
va_end(args);
|
||||
QCLOUD_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
}
|
||||
|
||||
va_end(args);
|
||||
|
||||
if ((remain_size = json_doc_size - strlen(json_doc)) <= 1) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_JSON_BUFFER_TOO_SHORT);
|
||||
}
|
||||
|
||||
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc) - 1, remain_size, "},");
|
||||
rc = shadow_json_snprintf_rc2errno(rc_snprintf, remain_size);
|
||||
if (rc != QCLOUD_ERR_SUCCESS) {
|
||||
QCLOUD_LOG_E("json add desired failed: %d", rc);
|
||||
QCLOUD_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(shadow_jsondoc_finalize(client, json_doc, json_doc_size));
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@@ -0,0 +1,466 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright (C) 2016 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 "qcloud.h"
|
||||
|
||||
static char incoming_msg_from_cloud[QCLOUD_SHADOW_JSON_BUF_MAX];
|
||||
|
||||
__QCLOUD_STATIC__ void shadow_glue_json_node_insert(char *json_doc, char *json_node, int pos)
|
||||
{
|
||||
int i, n;
|
||||
int len = strlen(json_doc);
|
||||
int nlen = strlen(json_node);
|
||||
|
||||
for (i = len - 1; i >= pos; --i) {
|
||||
*(json_doc + i + nlen) = *(json_doc + i);
|
||||
}
|
||||
|
||||
for (n = 0; n < nlen; n++) {
|
||||
*(json_doc + pos + n) = *json_node++;
|
||||
}
|
||||
|
||||
*(json_doc + len + nlen) = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 根据RequestParams、Method来给json填入type字段的值
|
||||
*/
|
||||
__QCLOUD_STATIC__ qcloud_err_t shadow_glue_json_request_method_set(char *json_doc, size_t json_doc_size, qcloud_shadow_req_method_t requst_method)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
QCLOUD_POINTER_SANITY_CHECK(json_doc, QCLOUD_ERR_INVAL);
|
||||
|
||||
char *type = NULL;
|
||||
char json_node[64] = {0};
|
||||
size_t json_len, size_remaining, json_node_len;
|
||||
|
||||
switch (requst_method) {
|
||||
case QCLOUD_SHADOW_REQUEST_METHOD_GET:
|
||||
type = SHADOW_OPERATION_GET;
|
||||
break;
|
||||
|
||||
case QCLOUD_SHADOW_REQUEST_METHOD_UPDATE:
|
||||
type = SHADOW_OPERATION_UPDATE;
|
||||
break;
|
||||
}
|
||||
|
||||
json_len = strlen(json_doc);
|
||||
size_remaining = json_doc_size - json_len;
|
||||
|
||||
osal_snprintf(json_node, 64, "\"type\":\"%s\", ", type);
|
||||
json_node_len = strlen(json_node);
|
||||
|
||||
if (json_node_len >= size_remaining - 1) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_INVAL);
|
||||
}
|
||||
|
||||
shadow_glue_json_node_insert(json_doc, json_node, 1);
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
|
||||
}
|
||||
|
||||
__QCLOUD_STATIC__ void shadow_glue_request_destroy(qcloud_shadow_request_t *request)
|
||||
{
|
||||
qcloud_list_del(&request->list);
|
||||
osal_free(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 执行设备影子操作的回调函数
|
||||
*/
|
||||
__QCLOUD_STATIC__ void shadow_glue_operation_request_do_handle(qcloud_shadow_client_t *client, const char *client_token, const char *method_type)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
QCLOUD_POINTER_SANITY_CHECK_RTN(client);
|
||||
QCLOUD_POINTER_SANITY_CHECK_RTN(client_token);
|
||||
QCLOUD_POINTER_SANITY_CHECK_RTN(method_type);
|
||||
|
||||
char *delta = NULL;
|
||||
int16_t result_code;
|
||||
qcloud_list_t *curr, *next;
|
||||
qcloud_shadow_request_t *request = NULL;
|
||||
qcloud_shadow_req_state_t req_state = QCLOUD_SHADOW_REQUEST_STATE_NONE;
|
||||
|
||||
if (qcloud_list_empty(&client->request_list)) {
|
||||
return;
|
||||
}
|
||||
|
||||
osal_mutex_lock(client->global_lock);
|
||||
|
||||
QCLOUD_LIST_FOR_EACH_SAFE(curr, next, &client->request_list) {
|
||||
request = QCLOUD_LIST_ENTRY(curr, qcloud_shadow_request_t, list);
|
||||
|
||||
if (strcmp(request->client_token, client_token) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// ͨ<><CDA8> payload <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> result <20><>ȷ<EFBFBD><C8B7><EFBFBD><EFBFBD>Ӧ<EFBFBD>IJ<EFBFBD><C4B2><EFBFBD><EFBFBD>Ƿ<EFBFBD><C7B7>ɹ<EFBFBD>
|
||||
// <20><>result = 0ʱ<30><CAB1>payload<61><64>Ϊ<EFBFBD>գ<EFBFBD>result<6C><74>0ʱ<30><CAB1><EFBFBD><EFBFBD><EFBFBD><EFBFBD>updateʧ<65><CAA7>
|
||||
if (!shadow_json_operation_result_code_parse(incoming_msg_from_cloud, &result_code)) {
|
||||
QCLOUD_LOG_E("parse result code failed.");
|
||||
shadow_glue_request_destroy(request);
|
||||
continue;
|
||||
}
|
||||
|
||||
req_state = (result_code == 0 ? QCLOUD_SHADOW_REQUEST_STATE_ACCEPTED : QCLOUD_SHADOW_REQUEST_STATE_REJECTED);
|
||||
|
||||
if ((strcmp(method_type, SHADOW_OPERATION_GET) == 0 && req_state == QCLOUD_SHADOW_REQUEST_STATE_ACCEPTED) ||
|
||||
(strcmp(method_type, SHADOW_OPERATION_UPDATE) == 0 && req_state == QCLOUD_SHADOW_REQUEST_STATE_REJECTED)) {
|
||||
if (shadow_json_operation_delta_get(incoming_msg_from_cloud, &delta)) {
|
||||
shadow_glue_delta_handle(client, delta);
|
||||
osal_free(delta);
|
||||
}
|
||||
}
|
||||
|
||||
if (request->handler) {
|
||||
request->handler(client, request->method, req_state, incoming_msg_from_cloud, request->context);
|
||||
}
|
||||
|
||||
shadow_glue_request_destroy(request);
|
||||
--client->request_list_counter;
|
||||
}
|
||||
|
||||
osal_mutex_unlock(client->global_lock);
|
||||
|
||||
QCLOUD_FUNC_EXIT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 文档操作请求结果的回调函数
|
||||
* 客户端先订阅 $shadow/operation/result/{ProductId}/{DeviceName}, 收到该topic的消息则会调用该回调函数
|
||||
* 在这个回调函数中, 解析出各个设备影子文档操作的结果
|
||||
*/
|
||||
__QCLOUD_STATIC__ void shadow_glue_operation_result_handler(void *client, mqtt_incoming_msg_t *message, void *private_data)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
QCLOUD_POINTER_SANITY_CHECK_RTN(client);
|
||||
QCLOUD_POINTER_SANITY_CHECK_RTN(message);
|
||||
|
||||
int cloud_rcv_len;
|
||||
uint32_t version = 0;
|
||||
char *client_token = NULL, *method_type = NULL, *delta = NULL;
|
||||
qcloud_mqtt_client_t *mqtt_client = NULL;
|
||||
qcloud_shadow_client_t *shadow_client = NULL;
|
||||
|
||||
mqtt_client = (qcloud_mqtt_client_t *)client;
|
||||
shadow_client = (qcloud_shadow_client_t*)mqtt_client->event_handler.context;
|
||||
|
||||
if (!message->topic || message->topic_len <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (message->payload_len > QCLOUD_SHADOW_JSON_BUF_MAX) {
|
||||
QCLOUD_LOG_E("received len exceeds limit");
|
||||
goto out;
|
||||
}
|
||||
|
||||
cloud_rcv_len = QCLOUD_MIN(QCLOUD_SHADOW_JSON_BUF_MAX - 1, message->payload_len);
|
||||
memcpy(incoming_msg_from_cloud, message->payload, cloud_rcv_len + 1);
|
||||
incoming_msg_from_cloud[cloud_rcv_len] = '\0'; // json_parse relies on a string
|
||||
|
||||
// 解析shadow result topic消息类型
|
||||
if (!shadow_json_operation_type_parse(incoming_msg_from_cloud, &method_type)) {
|
||||
QCLOUD_LOG_E("fail to parse type!");
|
||||
goto out;
|
||||
}
|
||||
|
||||
// 非delta消息的push,一定由设备端触发,找到设备段对应的client_token
|
||||
if (strcmp(method_type, SHADOW_OPERATION_DELTA) != 0 &&
|
||||
!shadow_json_client_token_parse(incoming_msg_from_cloud, &client_token)) {
|
||||
QCLOUD_LOG_E("fail to parse client token! json=%s", incoming_msg_from_cloud);
|
||||
goto out;
|
||||
}
|
||||
|
||||
// 获取shadow push消息version,如果比本地的version则修改本地version,比本地可能是由于服务器回滚或出错
|
||||
if (shadow_json_version_parse(incoming_msg_from_cloud, &version) &&
|
||||
version > shadow_client->version) {
|
||||
shadow_client->version = version;
|
||||
}
|
||||
|
||||
if (strcmp(method_type, SHADOW_OPERATION_DELTA) == 0) {
|
||||
if (shadow_json_delta_parse(incoming_msg_from_cloud, &delta)) {
|
||||
QCLOUD_LOG_D("delta: %s", delta);
|
||||
shadow_glue_delta_handle(shadow_client, delta);
|
||||
}
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (shadow_client) {
|
||||
shadow_glue_operation_request_do_handle(shadow_client, client_token, method_type);
|
||||
}
|
||||
|
||||
out:
|
||||
if (!method_type) {
|
||||
osal_free(method_type);
|
||||
}
|
||||
|
||||
if (!client_token) {
|
||||
osal_free(client_token);
|
||||
}
|
||||
|
||||
if (!delta) {
|
||||
osal_free(delta);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 发布文档请求到物联云
|
||||
*
|
||||
* @param client Qcloud_IoT_Client对象
|
||||
* @param method 文档操作方式
|
||||
* @param pJsonDoc 等待发送的文档
|
||||
* @return 返回QCLOUD_ERR_SUCCESS, 表示发布文档请求成功
|
||||
*/
|
||||
__QCLOUD_STATIC__ qcloud_err_t shadow_glue_operation_request_publish(qcloud_shadow_client_t *client,
|
||||
char *json_doc)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
mqtt_publish_opt_t publish_opt;
|
||||
|
||||
memset(&publish_opt, 0, sizeof(mqtt_publish_opt_t));
|
||||
publish_opt.qos = MQTT_QOS0;
|
||||
publish_opt.payload = (void *)json_doc;
|
||||
publish_opt.payload_len = strlen(json_doc);
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(qcloud_mqtt_client_publish(&client->mqtt_client, client->request_topic_publish, &publish_opt));
|
||||
}
|
||||
|
||||
__QCLOUD_INTERNAL__ qcloud_err_t shadow_glue_operation_request_subscribe(qcloud_shadow_client_t *client)
|
||||
{
|
||||
mqtt_subscribe_opt_t subscribe_opt;
|
||||
|
||||
subscribe_opt.message_handler = shadow_glue_operation_result_handler;
|
||||
subscribe_opt.private_data = NULL;
|
||||
subscribe_opt.qos = MQTT_QOS0;
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(qcloud_mqtt_client_subscribe(&client->mqtt_client, client->request_topic_subscribe, &subscribe_opt));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 取消订阅topic: $shadow/operation/result/{ProductId}/{DeviceName}
|
||||
*/
|
||||
__QCLOUD_INTERNAL__ qcloud_err_t shadow_glue_operation_request_unsubscribe(qcloud_shadow_client_t *client)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(qcloud_mqtt_client_unsubscribe(&client->mqtt_client, client->request_topic_subscribe));
|
||||
}
|
||||
|
||||
__QCLOUD_INTERNAL__ qcloud_err_t shadow_glue_request_list_scan(qcloud_shadow_client_t *client)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
|
||||
qcloud_list_t *curr, *next;
|
||||
qcloud_shadow_request_t *request;
|
||||
|
||||
if (qcloud_list_empty(&client->request_list)) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
|
||||
}
|
||||
|
||||
osal_mutex_lock(client->global_lock);
|
||||
|
||||
QCLOUD_LIST_FOR_EACH_SAFE(curr, next, &client->request_list) {
|
||||
request = QCLOUD_LIST_ENTRY(curr, qcloud_shadow_request_t, list);
|
||||
|
||||
// check whether the ack is timeout
|
||||
if (!osal_timer_is_expired(&request->timer)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (request->handler) {
|
||||
request->handler(client, request->method, QCLOUD_SHADOW_REQUEST_STATE_TIMEOUT, incoming_msg_from_cloud, request->context);
|
||||
}
|
||||
|
||||
shadow_glue_request_destroy(request);
|
||||
--client->request_list_counter;
|
||||
}
|
||||
|
||||
osal_mutex_unlock(client->global_lock);
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 将设备影子文档的操作请求保存在列表中
|
||||
*/
|
||||
__QCLOUD_STATIC__ qcloud_err_t shadow_glue_request_record(qcloud_shadow_client_t *client,
|
||||
const char *client_token,
|
||||
qcloud_shadow_req_opt_t *request_opt)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
qcloud_shadow_request_t *request = NULL;
|
||||
|
||||
if (client->request_list_counter >= QCLOUD_SHADOW_REQUEST_PENDING_MAX) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_MAX_APPENDING_REQUEST);
|
||||
}
|
||||
|
||||
request = (qcloud_shadow_request_t *)osal_malloc(sizeof(qcloud_shadow_request_t));
|
||||
if (!request) {
|
||||
QCLOUD_LOG_E("malloc failed!");
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
|
||||
}
|
||||
|
||||
request->handler = request_opt->handler;
|
||||
request->context = request_opt->context;
|
||||
request->method = request_opt->method;
|
||||
strncpy(request->client_token, client_token, QCLOUD_SHADOW_CLIENT_TOKEN_MAX);
|
||||
|
||||
osal_timer_init(&request->timer);
|
||||
osal_timer_countdown(&request->timer, request_opt->timeout);
|
||||
|
||||
qcloud_list_init(&request->list);
|
||||
|
||||
osal_mutex_lock(client->global_lock);
|
||||
qcloud_list_add(&request->list, &client->request_list);
|
||||
++client->request_list_counter;
|
||||
osal_mutex_unlock(client->global_lock);
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
|
||||
}
|
||||
|
||||
__QCLOUD_INTERNAL__ qcloud_err_t shadow_glue_request_post(qcloud_shadow_client_t *client,
|
||||
qcloud_shadow_req_opt_t *request_opt,
|
||||
char *json_doc,
|
||||
size_t json_doc_size)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(request_opt, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(json_doc, QCLOUD_ERR_INVAL);
|
||||
|
||||
qcloud_err_t rc = QCLOUD_ERR_SUCCESS;
|
||||
char *client_token = NULL;
|
||||
|
||||
// 解析文档中的clientToken, 如果解析失败, 直接返回错误
|
||||
if (!shadow_json_client_token_parse(json_doc, &client_token)) {
|
||||
QCLOUD_LOG_E("fail to parse client token!");
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_INVAL);
|
||||
}
|
||||
|
||||
rc = shadow_glue_json_request_method_set(json_doc, json_doc_size, request_opt->method);
|
||||
QCLOUD_FUNC_EXIT_RC_IF_NOT(rc, QCLOUD_ERR_SUCCESS, rc);
|
||||
|
||||
// 相应的 operation topic 订阅成功或已经订阅
|
||||
rc = shadow_glue_operation_request_publish(client, json_doc);
|
||||
if (rc == QCLOUD_ERR_SUCCESS) {
|
||||
rc = shadow_glue_request_record(client, client_token, request_opt);
|
||||
}
|
||||
|
||||
osal_free(client_token);
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 处理注册属性的回调函数
|
||||
* 当订阅的$shadow/operation/result/{ProductId}/{DeviceName}返回消息时,
|
||||
* 若对应的type为delta, 则执行该函数
|
||||
*
|
||||
*/
|
||||
__QCLOUD_INTERNAL__ void shadow_glue_delta_handle(qcloud_shadow_client_t *client, char *delta)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
qcloud_list_t *curr, *next;
|
||||
qcloud_shadow_prop_info_t *property_info = NULL;
|
||||
shadow_dev_property_t *dev_property = NULL;
|
||||
|
||||
if (qcloud_list_empty(&client->property_list)) {
|
||||
return;
|
||||
}
|
||||
|
||||
osal_mutex_lock(client->global_lock);
|
||||
|
||||
QCLOUD_LIST_FOR_EACH_SAFE(curr, next, &client->property_list) {
|
||||
property_info = QCLOUD_LIST_ENTRY(curr, qcloud_shadow_prop_info_t, list);
|
||||
dev_property = property_info->dev_property;
|
||||
|
||||
if (shadow_json_value_update(delta, dev_property)) {
|
||||
property_info->handler(client, delta, strlen(delta), dev_property);
|
||||
}
|
||||
}
|
||||
|
||||
osal_mutex_unlock(client->global_lock);
|
||||
|
||||
QCLOUD_FUNC_EXIT;
|
||||
}
|
||||
|
||||
__QCLOUD_INTERNAL__ void shadow_glue_property_list_destroy(qcloud_shadow_client_t *client)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
qcloud_list_t *curr, *next;
|
||||
qcloud_shadow_prop_info_t *property_info = NULL;
|
||||
|
||||
if (qcloud_list_empty(&client->property_list)) {
|
||||
return;
|
||||
}
|
||||
|
||||
osal_mutex_lock(client->global_lock);
|
||||
|
||||
QCLOUD_LIST_FOR_EACH_SAFE(curr, next, &client->property_list) {
|
||||
property_info = QCLOUD_LIST_ENTRY(curr, qcloud_shadow_prop_info_t, list);
|
||||
|
||||
qcloud_list_del(&property_info->list);
|
||||
osal_free(property_info);
|
||||
}
|
||||
|
||||
osal_mutex_unlock(client->global_lock);
|
||||
|
||||
QCLOUD_FUNC_EXIT;
|
||||
}
|
||||
|
||||
__QCLOUD_INTERNAL__ void shadow_glue_request_list_destroy(qcloud_shadow_client_t *client)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
qcloud_list_t *curr, *next;
|
||||
qcloud_shadow_request_t *request = NULL;
|
||||
|
||||
if (qcloud_list_empty(&client->request_list)) {
|
||||
return;
|
||||
}
|
||||
|
||||
osal_mutex_lock(client->global_lock);
|
||||
|
||||
QCLOUD_LIST_FOR_EACH_SAFE(curr, next, &client->request_list) {
|
||||
request = QCLOUD_LIST_ENTRY(curr, qcloud_shadow_request_t, list);
|
||||
|
||||
qcloud_list_del(&request->list);
|
||||
osal_free(request);
|
||||
}
|
||||
|
||||
osal_mutex_unlock(client->global_lock);
|
||||
|
||||
QCLOUD_FUNC_EXIT;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@@ -0,0 +1,285 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright (C) 2016 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 "qcloud.h"
|
||||
|
||||
#define PRIi32 "i"
|
||||
#define PRIi16 "i"
|
||||
#define PRIi8 "i"
|
||||
#define PRIu32 "u"
|
||||
#define PRIu16 "u"
|
||||
#define PRIu8 "u"
|
||||
#define SCNi8 "hhi"
|
||||
#define SCNu8 "hhu"
|
||||
#define SCNi16 "hi"
|
||||
#define SCNu16 "hu"
|
||||
#define SCNi32 "i"
|
||||
#define SCNu32 "u"
|
||||
|
||||
__QCLOUD_STATIC__ qcloud_err_t shadow_json_value_do_update(char *value, shadow_dev_property_t *dev_property)
|
||||
{
|
||||
switch (dev_property->type) {
|
||||
case JSON_DATA_TYPE_BOOL:
|
||||
return LITE_get_boolean(dev_property->data, value);
|
||||
|
||||
case JSON_DATA_TYPE_INT32:
|
||||
return LITE_get_int32(dev_property->data, value);
|
||||
|
||||
case JSON_DATA_TYPE_INT16:
|
||||
return LITE_get_int16(dev_property->data, value);
|
||||
|
||||
case JSON_DATA_TYPE_INT8:
|
||||
return LITE_get_int8(dev_property->data, value);
|
||||
|
||||
case JSON_DATA_TYPE_UINT32:
|
||||
return LITE_get_uint32(dev_property->data, value);
|
||||
|
||||
case JSON_DATA_TYPE_UINT16:
|
||||
return LITE_get_uint16(dev_property->data, value);
|
||||
|
||||
case JSON_DATA_TYPE_UINT8:
|
||||
return LITE_get_uint8(dev_property->data, value);
|
||||
|
||||
case JSON_DATA_TYPE_FLOAT:
|
||||
return LITE_get_float(dev_property->data, value);
|
||||
|
||||
case JSON_DATA_TYPE_DOUBLE:
|
||||
return LITE_get_double(dev_property->data, value);
|
||||
|
||||
case JSON_DATA_TYPE_STRING:
|
||||
case JSON_DATA_TYPE_OBJECT:
|
||||
QCLOUD_LOG_D("string/object to be deal, %d %s", dev_property->type, value);
|
||||
return QCLOUD_ERR_SUCCESS;
|
||||
|
||||
default:
|
||||
QCLOUD_LOG_E("unknow type, %d",dev_property->type);
|
||||
return QCLOUD_ERR_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查函数snprintf的返回值
|
||||
*
|
||||
* @param returnCode 函数snprintf的返回值
|
||||
* @param maxSizeOfWrite 可写最大字节数
|
||||
* @return 返回QCLOUD_ERR_JSON, 表示出错; 返回QCLOUD_ERR_JSON_BUFFER_TRUNCATED, 表示截断
|
||||
*/
|
||||
__QCLOUD_INTERNAL__ qcloud_err_t shadow_json_snprintf_rc2errno(int rc, size_t write_max)
|
||||
{
|
||||
if (rc < 0) {
|
||||
return QCLOUD_ERR_JSON;
|
||||
}
|
||||
|
||||
if (rc >= write_max) {
|
||||
return QCLOUD_ERR_JSON_BUFFER_TRUNCATED;
|
||||
}
|
||||
|
||||
return QCLOUD_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
__QCLOUD_INTERNAL__ qcloud_err_t shadow_json_client_token_generate(char *json_doc, size_t json_doc_size, uint32_t token_num, char *device_product_id)
|
||||
{
|
||||
int rc_snprintf;
|
||||
|
||||
rc_snprintf = osal_snprintf(json_doc, json_doc_size, "%s-%u", device_product_id, token_num);
|
||||
return shadow_json_snprintf_rc2errno(rc_snprintf, json_doc_size);
|
||||
}
|
||||
|
||||
__QCLOUD_INTERNAL__ qcloud_err_t shadow_json_node_add(char *json_doc, size_t json_doc_size, const char *key, void *data, json_data_type_t type)
|
||||
{
|
||||
qcloud_err_t rc;
|
||||
int32_t rc_snprintf = 0;
|
||||
size_t remain_size = 0;
|
||||
|
||||
if ((remain_size = json_doc_size - strlen(json_doc)) <= 1) {
|
||||
return QCLOUD_ERR_JSON_BUFFER_TOO_SHORT;
|
||||
}
|
||||
|
||||
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size, "\"%s\":", key);
|
||||
rc = shadow_json_snprintf_rc2errno(rc_snprintf, remain_size);
|
||||
QCLOUD_FUNC_EXIT_RC_IF_NOT(rc, QCLOUD_ERR_SUCCESS, rc);
|
||||
|
||||
if ((remain_size = json_doc_size - strlen(json_doc)) <= 1) {
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_JSON_BUFFER_TOO_SHORT);
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size, "null,");
|
||||
QCLOUD_FUNC_EXIT_RC(shadow_json_snprintf_rc2errno(rc_snprintf, remain_size));
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case JSON_DATA_TYPE_INT32:
|
||||
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size, "%"
|
||||
PRIi32
|
||||
",", *(int32_t *)(data));
|
||||
break;
|
||||
|
||||
case JSON_DATA_TYPE_INT16:
|
||||
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size, "%"
|
||||
PRIi16
|
||||
",", *(int16_t *)(data));
|
||||
break;
|
||||
|
||||
case JSON_DATA_TYPE_INT8:
|
||||
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size, "%"
|
||||
PRIi8
|
||||
",", *(int8_t *)(data));
|
||||
break;
|
||||
|
||||
|
||||
case JSON_DATA_TYPE_UINT32:
|
||||
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size, "%"
|
||||
PRIu32
|
||||
",", *(uint32_t *)(data));
|
||||
break;
|
||||
|
||||
case JSON_DATA_TYPE_UINT16:
|
||||
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size, "%"
|
||||
PRIu16
|
||||
",", *(uint16_t *)(data));
|
||||
break;
|
||||
|
||||
case JSON_DATA_TYPE_UINT8:
|
||||
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size, "%"
|
||||
PRIu8
|
||||
",", *(uint8_t *)(data));
|
||||
break;
|
||||
|
||||
case JSON_DATA_TYPE_DOUBLE:
|
||||
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size, "%f,", *(double *)(data));
|
||||
break;
|
||||
|
||||
case JSON_DATA_TYPE_FLOAT:
|
||||
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size, "%f,", *(float *)(data));
|
||||
break;
|
||||
|
||||
case JSON_DATA_TYPE_BOOL:
|
||||
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size, "%s,", *(bool *)(data) ? "true" : "false");
|
||||
break;
|
||||
|
||||
case JSON_DATA_TYPE_STRING:
|
||||
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size, "\"%s\",", (char *)(data));
|
||||
break;
|
||||
|
||||
case JSON_DATA_TYPE_OBJECT:
|
||||
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size, "%s,", (char *)(data));
|
||||
break;
|
||||
}
|
||||
|
||||
return shadow_json_snprintf_rc2errno(rc_snprintf, remain_size);
|
||||
}
|
||||
|
||||
__QCLOUD_INTERNAL__ qcloud_err_t shadow_json_empty_doc_build(char *json_doc, uint32_t token_num, char *device_product_id)
|
||||
{
|
||||
int rc_snprintf;
|
||||
|
||||
rc_snprintf = osal_snprintf(json_doc, QCLOUD_SHADOW_JSON_WITH_CLIENT_TOKEN_MAX, "{\"clientToken\":\"%s-%u\"}", device_product_id, token_num);
|
||||
return shadow_json_snprintf_rc2errno(rc_snprintf, QCLOUD_SHADOW_JSON_WITH_CLIENT_TOKEN_MAX);
|
||||
}
|
||||
|
||||
__QCLOUD_INTERNAL__ int shadow_json_client_token_parse(char *json_doc, char **client_token)
|
||||
{
|
||||
*client_token = LITE_json_value_of(SHADOW_FIELD_CLIENT_TOKEN, json_doc);
|
||||
return *client_token != NULL;
|
||||
}
|
||||
|
||||
__QCLOUD_INTERNAL__ int shadow_json_version_parse(char *json_doc, uint32_t *version)
|
||||
{
|
||||
int rc = QCLOUD_TRUE;
|
||||
char *version_str;
|
||||
|
||||
version_str = LITE_json_value_of(SHADOW_PAYLOAD_VERSION, json_doc);
|
||||
if (!version_str) {
|
||||
return QCLOUD_FALSE;
|
||||
}
|
||||
|
||||
if (sscanf(version_str, "%" SCNu32, version) != 1) {
|
||||
QCLOUD_LOG_E("parse shadow version failed, rc: %d", QCLOUD_ERR_JSON_PARSE);
|
||||
rc = QCLOUD_FALSE;
|
||||
}
|
||||
|
||||
osal_free(version_str);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
__QCLOUD_INTERNAL__ int shadow_json_operation_type_parse(char *json_doc, char **type)
|
||||
{
|
||||
*type = LITE_json_value_of(SHADOW_FIELD_TYPE, json_doc);
|
||||
return *type != NULL;
|
||||
}
|
||||
|
||||
__QCLOUD_INTERNAL__ int shadow_json_operation_result_code_parse(char *json_doc, int16_t *result_code)
|
||||
{
|
||||
int rc = QCLOUD_TRUE;
|
||||
char *result_code_str;
|
||||
|
||||
result_code_str = LITE_json_value_of(SHADOW_FIELD_RESULT, json_doc);
|
||||
if (!result_code_str) {
|
||||
return QCLOUD_FALSE;
|
||||
}
|
||||
|
||||
if (sscanf(result_code_str, "%" SCNi16, result_code) != 1) {
|
||||
QCLOUD_LOG_E("parse result code failed, %d", QCLOUD_ERR_JSON_PARSE);
|
||||
rc = QCLOUD_FALSE;
|
||||
}
|
||||
|
||||
osal_free(result_code_str);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
__QCLOUD_INTERNAL__ int shadow_json_delta_parse(char *json_doc, char **delta)
|
||||
{
|
||||
*delta = LITE_json_value_of(SHADOW_PAYLOAD_STATE, json_doc);
|
||||
return *delta != NULL;
|
||||
}
|
||||
|
||||
__QCLOUD_INTERNAL__ int shadow_json_operation_delta_get(char *json_doc, char **delta)
|
||||
{
|
||||
*delta = LITE_json_value_of(SHADOW_PAYLOAD_STATE_DELTA, json_doc);
|
||||
return *delta != NULL;
|
||||
}
|
||||
|
||||
__QCLOUD_INTERNAL__ int shadow_state_parse(char *json_doc, char **state)
|
||||
{
|
||||
*state = LITE_json_value_of(SHADOW_PAYLOAD_VERSION, json_doc);
|
||||
return *state != NULL;
|
||||
}
|
||||
|
||||
__QCLOUD_INTERNAL__ int shadow_json_value_update(char *json, shadow_dev_property_t *dev_property)
|
||||
{
|
||||
char *data;
|
||||
|
||||
data = LITE_json_value_of((char *)dev_property->key, json);
|
||||
if (!data || strncmp(data, "null", 4) == 0 || strncmp(data, "NULL", 4) == 0) {
|
||||
return QCLOUD_FALSE;
|
||||
}
|
||||
|
||||
shadow_json_value_do_update(data, dev_property);
|
||||
osal_free(data);
|
||||
|
||||
return QCLOUD_TRUE;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,204 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright (C) 2016 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 "qcloud_utils/qcloud_base64.h"
|
||||
|
||||
static const unsigned char base64_enc_map[64] =
|
||||
{
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
|
||||
'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
|
||||
'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
|
||||
'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
|
||||
'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
|
||||
'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
|
||||
'8', '9', '+', '/'
|
||||
};
|
||||
|
||||
static const unsigned char base64_dec_map[128] =
|
||||
{
|
||||
127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
|
||||
127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
|
||||
127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
|
||||
127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
|
||||
127, 127, 127, 62, 127, 127, 127, 63, 52, 53,
|
||||
54, 55, 56, 57, 58, 59, 60, 61, 127, 127,
|
||||
127, 64, 127, 127, 127, 0, 1, 2, 3, 4,
|
||||
5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
||||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
|
||||
25, 127, 127, 127, 127, 127, 127, 26, 27, 28,
|
||||
29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
|
||||
39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
|
||||
49, 50, 51, 127, 127, 127, 127, 127
|
||||
};
|
||||
|
||||
#define BASE64_SIZE_T_MAX ( (size_t) -1 ) /* SIZE_T_MAX is not standard */
|
||||
|
||||
int qcloud_utils_base64_encode( unsigned char *dst, size_t dlen, size_t *olen,
|
||||
const unsigned char *src, size_t slen )
|
||||
{
|
||||
size_t i, n;
|
||||
unsigned char *p;
|
||||
|
||||
if( slen == 0 )
|
||||
{
|
||||
*olen = 0;
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
n = slen / 3 + ( slen % 3 != 0 );
|
||||
|
||||
if( n > ( BASE64_SIZE_T_MAX - 1 ) / 4 )
|
||||
{
|
||||
*olen = BASE64_SIZE_T_MAX;
|
||||
return( QCLOUD_ERR_FAILURE );
|
||||
}
|
||||
|
||||
n *= 4;
|
||||
|
||||
if( ( dlen < n + 1 ) || ( NULL == dst ) )
|
||||
{
|
||||
*olen = n + 1;
|
||||
return( QCLOUD_ERR_FAILURE );
|
||||
}
|
||||
|
||||
n = ( slen / 3 ) * 3;
|
||||
|
||||
int C1, C2, C3;
|
||||
for( i = 0, p = dst; i < n; i += 3 )
|
||||
{
|
||||
C1 = *src++;
|
||||
C2 = *src++;
|
||||
C3 = *src++;
|
||||
|
||||
*p++ = base64_enc_map[(C1 >> 2) & 0x3F];
|
||||
*p++ = base64_enc_map[(((C1 & 3) << 4) + (C2 >> 4)) & 0x3F];
|
||||
*p++ = base64_enc_map[(((C2 & 15) << 2) + (C3 >> 6)) & 0x3F];
|
||||
*p++ = base64_enc_map[C3 & 0x3F];
|
||||
}
|
||||
|
||||
if( i < slen )
|
||||
{
|
||||
C1 = *src++;
|
||||
C2 = ( ( i + 1 ) < slen ) ? *src++ : 0;
|
||||
|
||||
*p++ = base64_enc_map[(C1 >> 2) & 0x3F];
|
||||
*p++ = base64_enc_map[(((C1 & 3) << 4) + (C2 >> 4)) & 0x3F];
|
||||
|
||||
if( ( i + 1 ) < slen )
|
||||
*p++ = base64_enc_map[((C2 & 15) << 2) & 0x3F];
|
||||
else *p++ = '=';
|
||||
|
||||
*p++ = '=';
|
||||
}
|
||||
|
||||
*olen = p - dst;
|
||||
*p = 0;
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
int qcloud_utils_base64_decode( unsigned char *dst, size_t dlen, size_t *olen,
|
||||
const unsigned char *src, size_t slen )
|
||||
{
|
||||
size_t i, n;
|
||||
uint32_t j, x;
|
||||
unsigned char *p;
|
||||
|
||||
/* First pass: check for validity and get output length */
|
||||
for( i = n = j = 0; i < slen; i++ )
|
||||
{
|
||||
/* Skip spaces before checking for EOL */
|
||||
x = 0;
|
||||
while( i < slen && src[i] == ' ' )
|
||||
{
|
||||
++i;
|
||||
++x;
|
||||
}
|
||||
|
||||
/* Spaces at end of buffer are OK */
|
||||
if( i == slen )
|
||||
break;
|
||||
|
||||
if( ( slen - i ) >= 2 &&
|
||||
src[i] == '\r' && src[i + 1] == '\n' )
|
||||
continue;
|
||||
|
||||
if( src[i] == '\n' )
|
||||
continue;
|
||||
|
||||
/* Space inside a line is an error */
|
||||
if( x != 0 )
|
||||
return( QCLOUD_ERR_FAILURE );
|
||||
|
||||
if( src[i] == '=' && ++j > 2 )
|
||||
return( QCLOUD_ERR_FAILURE );
|
||||
|
||||
if( src[i] > 127 || base64_dec_map[src[i]] == 127 )
|
||||
return( QCLOUD_ERR_FAILURE );
|
||||
|
||||
if( base64_dec_map[src[i]] < 64 && j != 0 )
|
||||
return( QCLOUD_ERR_FAILURE );
|
||||
|
||||
n++;
|
||||
}
|
||||
|
||||
if( n == 0 )
|
||||
{
|
||||
*olen = 0;
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
n = ( ( n * 6 ) + 7 ) >> 3;
|
||||
n -= j;
|
||||
|
||||
if( dst == NULL || dlen < n )
|
||||
{
|
||||
*olen = n;
|
||||
return( QCLOUD_ERR_FAILURE );
|
||||
}
|
||||
|
||||
for( j = 3, n = x = 0, p = dst; i > 0; i--, src++ )
|
||||
{
|
||||
if( *src == '\r' || *src == '\n' || *src == ' ' )
|
||||
continue;
|
||||
|
||||
j -= ( base64_dec_map[*src] == 64 );
|
||||
x = ( x << 6 ) | ( base64_dec_map[*src] & 0x3F );
|
||||
|
||||
if( ++n == 4 )
|
||||
{
|
||||
n = 0;
|
||||
if( j > 0 ) *p++ = (unsigned char)( x >> 16 );
|
||||
if( j > 1 ) *p++ = (unsigned char)( x >> 8 );
|
||||
if( j > 2 ) *p++ = (unsigned char)( x );
|
||||
}
|
||||
}
|
||||
|
||||
*olen = p - dst;
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright (C) 2016 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 "qcloud.h"
|
||||
|
||||
#define KEY_IOPAD_SIZE 64
|
||||
|
||||
#define MD5_DIGEST_SIZE 16
|
||||
#define SHA1_DIGEST_SIZE 20
|
||||
|
||||
void utils_hmac_md5(const char *msg, int msg_len, char *digest, const char *key, int key_len)
|
||||
{
|
||||
if((NULL == msg) || (NULL == digest) || (NULL == key)) {
|
||||
QCLOUD_LOG_E("parameter is Null,failed!");
|
||||
return;
|
||||
}
|
||||
|
||||
if(key_len > KEY_IOPAD_SIZE) {
|
||||
QCLOUD_LOG_E("key_len > size(%d) of array",KEY_IOPAD_SIZE);
|
||||
return;
|
||||
}
|
||||
|
||||
iot_md5_context context;
|
||||
unsigned char k_ipad[KEY_IOPAD_SIZE]; /* inner padding - key XORd with ipad */
|
||||
unsigned char k_opad[KEY_IOPAD_SIZE]; /* outer padding - key XORd with opad */
|
||||
unsigned char out[MD5_DIGEST_SIZE];
|
||||
int i;
|
||||
|
||||
/* start out by storing key in pads */
|
||||
memset(k_ipad, 0, sizeof(k_ipad));
|
||||
memset(k_opad, 0, sizeof(k_opad));
|
||||
memcpy(k_ipad, key, key_len);
|
||||
memcpy(k_opad, key, key_len);
|
||||
|
||||
/* XOR key with ipad and opad values */
|
||||
for (i = 0; i < KEY_IOPAD_SIZE; i++) {
|
||||
k_ipad[i] ^= 0x36;
|
||||
k_opad[i] ^= 0x5c;
|
||||
}
|
||||
|
||||
/* perform inner MD5 */
|
||||
utils_md5_init(&context); /* init context for 1st pass */
|
||||
utils_md5_starts(&context); /* setup context for 1st pass */
|
||||
utils_md5_update(&context, k_ipad, KEY_IOPAD_SIZE); /* start with inner pad */
|
||||
utils_md5_update(&context, (unsigned char *) msg, msg_len); /* then text of datagram */
|
||||
utils_md5_finish(&context, out); /* finish up 1st pass */
|
||||
|
||||
/* perform outer MD5 */
|
||||
utils_md5_init(&context); /* init context for 2nd pass */
|
||||
utils_md5_starts(&context); /* setup context for 2nd pass */
|
||||
utils_md5_update(&context, k_opad, KEY_IOPAD_SIZE); /* start with outer pad */
|
||||
utils_md5_update(&context, out, MD5_DIGEST_SIZE); /* then results of 1st hash */
|
||||
utils_md5_finish(&context, out); /* finish up 2nd pass */
|
||||
|
||||
for (i = 0; i < MD5_DIGEST_SIZE; ++i) {
|
||||
digest[i * 2] = utils_hb2hex(out[i] >> 4);
|
||||
digest[i * 2 + 1] = utils_hb2hex(out[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void utils_hmac_sha1(const char *msg, int msg_len, char *digest, const char *key, int key_len)
|
||||
{
|
||||
if((NULL == msg) || (NULL == digest) || (NULL == key)) {
|
||||
QCLOUD_LOG_E("parameter is Null,failed!");
|
||||
return;
|
||||
}
|
||||
|
||||
if(key_len > KEY_IOPAD_SIZE) {
|
||||
QCLOUD_LOG_E("key_len > size(%d) of array",KEY_IOPAD_SIZE);
|
||||
return;
|
||||
}
|
||||
|
||||
iot_sha1_context context;
|
||||
unsigned char k_ipad[KEY_IOPAD_SIZE]; /* inner padding - key XORd with ipad */
|
||||
unsigned char k_opad[KEY_IOPAD_SIZE]; /* outer padding - key XORd with opad */
|
||||
unsigned char out[SHA1_DIGEST_SIZE];
|
||||
int i;
|
||||
|
||||
/* start out by storing key in pads */
|
||||
memset(k_ipad, 0, sizeof(k_ipad));
|
||||
memset(k_opad, 0, sizeof(k_opad));
|
||||
memcpy(k_ipad, key, key_len);
|
||||
memcpy(k_opad, key, key_len);
|
||||
|
||||
/* XOR key with ipad and opad values */
|
||||
for (i = 0; i < KEY_IOPAD_SIZE; i++) {
|
||||
k_ipad[i] ^= 0x36;
|
||||
k_opad[i] ^= 0x5c;
|
||||
}
|
||||
|
||||
/* perform inner SHA */
|
||||
utils_sha1_init(&context); /* init context for 1st pass */
|
||||
utils_sha1_starts(&context); /* setup context for 1st pass */
|
||||
utils_sha1_update(&context, k_ipad, KEY_IOPAD_SIZE); /* start with inner pad */
|
||||
utils_sha1_update(&context, (unsigned char *) msg, msg_len); /* then text of datagram */
|
||||
utils_sha1_finish(&context, out); /* finish up 1st pass */
|
||||
|
||||
/* perform outer SHA */
|
||||
utils_sha1_init(&context); /* init context for 2nd pass */
|
||||
utils_sha1_starts(&context); /* setup context for 2nd pass */
|
||||
utils_sha1_update(&context, k_opad, KEY_IOPAD_SIZE); /* start with outer pad */
|
||||
utils_sha1_update(&context, out, SHA1_DIGEST_SIZE); /* then results of 1st hash */
|
||||
utils_sha1_finish(&context, out); /* finish up 2nd pass */
|
||||
|
||||
for (i = 0; i < SHA1_DIGEST_SIZE; ++i) {
|
||||
digest[i * 2] = utils_hb2hex(out[i] >> 4);
|
||||
digest[i * 2 + 1] = utils_hb2hex(out[i]);
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,829 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright (C) 2016 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 "qcloud.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) {
|
||||
QCLOUD_LOG_E("Could not find host");
|
||||
return QCLOUD_ERR_HTTP_PARSE;
|
||||
}
|
||||
|
||||
if (max_scheme_len < host_ptr - scheme_ptr + 1) {
|
||||
QCLOUD_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_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
if (host_len == 0) {
|
||||
host_len = path_ptr - host_ptr;
|
||||
}
|
||||
|
||||
if (maxhost_len < host_len + 1) {
|
||||
QCLOUD_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) {
|
||||
QCLOUD_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_ERR_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) {
|
||||
QCLOUD_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) {
|
||||
QCLOUD_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_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 拼接发送的数据
|
||||
*
|
||||
* @param client http client
|
||||
* @param send_buf 发送数据buffer
|
||||
* @param send_idx 标志send_buf数据结束的位置
|
||||
* @param buf 需要被发送的数据,拼接到send_buf中
|
||||
* @param len buf的长度
|
||||
* @return 返回QCLOUD_ERR_SUCCESS, 表示设置成功
|
||||
*/
|
||||
static int _http_client_get_info(HTTPClient *client, unsigned char *send_buf, int *send_idx, char *buf, uint32_t len)
|
||||
{
|
||||
int rc = QCLOUD_ERR_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.write(&(client->network), 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);
|
||||
osal_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_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 根据请求url和method,拼接请求头
|
||||
*
|
||||
* @param client http client
|
||||
* @param url 请求url
|
||||
* @param method 请求方法
|
||||
* @param client_data http数据负载
|
||||
* @return 返回QCLOUD_ERR_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_ERR_SUCCESS) {
|
||||
QCLOUD_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;
|
||||
|
||||
osal_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) {
|
||||
QCLOUD_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) {
|
||||
osal_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) {
|
||||
osal_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);
|
||||
|
||||
//QCLOUD_LOG_D("REQUEST:\n%s", send_buf);
|
||||
|
||||
size_t written_len = 0;
|
||||
rc = client->network.write(&client->network, send_buf, len, 5000, &written_len);
|
||||
if (written_len > 0) {
|
||||
//QCLOUD_LOG_D("Written %lu bytes", written_len);
|
||||
} else if (written_len == 0) {
|
||||
QCLOUD_LOG_E("written_len == 0,Connection was closed by server");
|
||||
return QCLOUD_ERR_HTTP_CLOSED; /* Connection was closed by server */
|
||||
} else {
|
||||
QCLOUD_LOG_E("Connection error (send returned %d)", rc);
|
||||
return QCLOUD_ERR_HTTP_CONN;
|
||||
}
|
||||
|
||||
return QCLOUD_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 发送post的请求体数据
|
||||
*
|
||||
* @param client http client
|
||||
* @param client_data http数据负载
|
||||
* @return 返回QCLOUD_ERR_SUCCESS, 表示设置成功
|
||||
*/
|
||||
static int _http_client_send_userdata(HTTPClient *client, HTTPClientData *client_data)
|
||||
{
|
||||
if (client_data->post_buf && client_data->post_buf_len) {
|
||||
//QCLOUD_LOG_D("client_data->post_buf: %s", client_data->post_buf);
|
||||
{
|
||||
size_t written_len = 0;
|
||||
int rc = client->network.write(&client->network, (unsigned char *)client_data->post_buf, client_data->post_buf_len, 5000, &written_len);
|
||||
if (written_len > 0) {
|
||||
//QCLOUD_LOG_D("Written %d bytes", written_len);
|
||||
} else if (written_len == 0) {
|
||||
QCLOUD_LOG_E("written_len == 0,Connection was closed by server");
|
||||
return QCLOUD_ERR_HTTP_CLOSED;
|
||||
} else {
|
||||
QCLOUD_LOG_E("Connection error (send returned %d)", rc);
|
||||
return QCLOUD_ERR_HTTP_CONN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return QCLOUD_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 读取http数据
|
||||
*
|
||||
* @param client http client
|
||||
* @param buf 数据buffer
|
||||
* @param min_len 读取数据的最小长度
|
||||
* @param max_len 读取数据的最大长度
|
||||
* @param p_read_len 成功读取到的数据的长度
|
||||
* @param timeout_ms 超时时间
|
||||
* @param client_data http数据负载
|
||||
* @return 返回QCLOUD_ERR_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)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
int rc = 0;
|
||||
osal_timer_t timer;
|
||||
|
||||
osal_timer_init(&timer);
|
||||
osal_timer_countdown_ms(&timer, (unsigned int)timeout_ms);
|
||||
|
||||
*p_read_len = 0;
|
||||
|
||||
rc = client->network.read(&client->network, (unsigned char *)buf, max_len, (uint32_t)osal_timer_remain(&timer), (size_t *)p_read_len);
|
||||
|
||||
if (rc == QCLOUD_ERR_SSL_NOTHING_TO_READ || rc == QCLOUD_ERR_TCP_NOTHING_TO_READ) {
|
||||
QCLOUD_LOG_D("HTTP read nothing and timeout");
|
||||
rc = QCLOUD_ERR_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_ERR_SUCCESS;
|
||||
}
|
||||
else if (rc == QCLOUD_ERR_TCP_PEER_SHUTDOWN && *p_read_len > 0) {
|
||||
/* HTTP server give response and close this connection */
|
||||
client->network.disconnect(&client->network);
|
||||
rc = QCLOUD_ERR_SUCCESS;
|
||||
}
|
||||
else if (rc != QCLOUD_ERR_SUCCESS) { // 其他错误
|
||||
QCLOUD_LOG_E("Connection error rc = %d (recv returned %d)", rc, *p_read_len);
|
||||
QCLOUD_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
|
||||
}
|
||||
|
||||
static int _http_client_retrieve_content(HTTPClient *client, char *data, int len, uint32_t timeout_ms,
|
||||
HTTPClientData *client_data)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
int count = 0;
|
||||
int templen = 0;
|
||||
int crlf_pos;
|
||||
osal_timer_t timer;
|
||||
|
||||
osal_timer_init(&timer);
|
||||
osal_timer_countdown_ms(&timer, (unsigned int)timeout_ms);
|
||||
|
||||
client_data->is_more = QCLOUD_TRUE;
|
||||
|
||||
if (client_data->response_content_len == -1 && client_data->is_chunked == QCLOUD_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)osal_timer_remain(&timer), client_data);
|
||||
|
||||
/* Receive data */
|
||||
//QCLOUD_LOG_D("data len: %d %d", len, count);
|
||||
|
||||
if (rc != QCLOUD_ERR_SUCCESS) {
|
||||
QCLOUD_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
if (len == 0) {
|
||||
/* read no more data */
|
||||
QCLOUD_LOG_D("no more data, len == 0");
|
||||
client_data->is_more = QCLOUD_FALSE;
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_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 = QCLOUD_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 = QCLOUD_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,
|
||||
osal_timer_remain(&timer),
|
||||
client_data);
|
||||
len += new_trf_len;
|
||||
if (rc != QCLOUD_ERR_SUCCESS) {
|
||||
QCLOUD_FUNC_EXIT_RC(rc);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
QCLOUD_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 = QCLOUD_FALSE;
|
||||
QCLOUD_LOG_D("no more (last chunk)");
|
||||
}
|
||||
|
||||
if (n != 1) {
|
||||
QCLOUD_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);
|
||||
QCLOUD_FUNC_EXIT_RC(HTTP_RETRIEVE_MORE_DATA);
|
||||
}
|
||||
|
||||
if (len > readLen) {
|
||||
QCLOUD_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, osal_timer_remain(&timer), client_data);
|
||||
if (rc != QCLOUD_ERR_SUCCESS) {
|
||||
QCLOUD_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
}
|
||||
} 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,
|
||||
osal_timer_remain(&timer), client_data);
|
||||
if (rc != QCLOUD_ERR_SUCCESS) {
|
||||
QCLOUD_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
len += new_trf_len;
|
||||
}
|
||||
|
||||
if ((data[0] != '\r') || (data[1] != '\n')) {
|
||||
QCLOUD_LOG_E("Format error, %s", data); /* after memmove, the beginning of next chunk */
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_HTTP_UNRESOLVED_DNS);
|
||||
}
|
||||
memmove(data, &data[2], len - 2); /* remove the \r\n */
|
||||
len -= 2;
|
||||
} else {
|
||||
//QCLOUD_LOG_D("no more (content-length)");
|
||||
client_data->is_more = QCLOUD_FALSE;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 解析response body数据
|
||||
*
|
||||
* @param client HTTPClient数据
|
||||
* @param data 读取到的数据
|
||||
* @param len 读取到的数据的长度
|
||||
* @param timeout_ms 读取数据的超时时间
|
||||
* @param client_data http数据负载
|
||||
* @return 返回QCLOUD_ERR_SUCCESS, 表示成功
|
||||
*/
|
||||
static int _http_client_response_parse(HTTPClient *client, char *data, int len, uint32_t timeout_ms,
|
||||
HTTPClientData *client_data)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
int crlf_pos;
|
||||
osal_timer_t timer;
|
||||
char *tmp_ptr, *ptr_body_end;
|
||||
|
||||
osal_timer_init(&timer);
|
||||
osal_timer_countdown_ms(&timer, timeout_ms);
|
||||
|
||||
client_data->response_content_len = -1;
|
||||
|
||||
char *crlf_ptr = strstr(data, "\r\n");
|
||||
if (crlf_ptr == NULL) {
|
||||
QCLOUD_LOG_E("\\r\\n not found");
|
||||
QCLOUD_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) {
|
||||
QCLOUD_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)) {
|
||||
QCLOUD_LOG_W("Response code %d", client->response_code);
|
||||
|
||||
if (client->response_code == 403)
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_HTTP_AUTH);
|
||||
|
||||
if (client->response_code == 404)
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_HTTP_NOT_FOUND);
|
||||
}
|
||||
|
||||
//QCLOUD_LOG_D("Reading headers : %s", data);
|
||||
|
||||
// 移除null终止字符
|
||||
memmove(data, &data[crlf_pos + 2], len - (crlf_pos + 2) + 1);
|
||||
len -= (crlf_pos + 2);
|
||||
|
||||
client_data->is_chunked = QCLOUD_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, osal_timer_remain(&timer), client_data);
|
||||
if (rc != QCLOUD_ERR_SUCCESS) {
|
||||
QCLOUD_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
len += new_trf_len;
|
||||
data[len] = '\0';
|
||||
if (NULL == (ptr_body_end = strstr(data, "\r\n\r\n"))) {
|
||||
QCLOUD_LOG_E("parse error: no end of the request body");
|
||||
QCLOUD_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 = QCLOUD_TRUE;
|
||||
client_data->response_content_len = 0;
|
||||
client_data->retrieve_len = 0;
|
||||
}
|
||||
} else {
|
||||
QCLOUD_LOG_E("Could not parse header");
|
||||
QCLOUD_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, osal_timer_remain(&timer), client_data);
|
||||
QCLOUD_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
static int _http_client_connect(HTTPClient *client)
|
||||
{
|
||||
if (QCLOUD_ERR_SUCCESS != client->network.connect(&client->network)) {
|
||||
return QCLOUD_ERR_HTTP_CONN;
|
||||
}
|
||||
|
||||
return QCLOUD_ERR_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) {
|
||||
QCLOUD_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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 接收http返回的数据
|
||||
*
|
||||
* @param client http client
|
||||
* @param timeout_ms 读取数据的超时时间
|
||||
* @param client_data http数据负载
|
||||
* @return 返回QCLOUD_ERR_SUCCESS, 表示设置成功
|
||||
*/
|
||||
static int _http_client_recv_response(HTTPClient *client, uint32_t timeout_ms, HTTPClientData *client_data)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
int reclen = 0, rc = QCLOUD_ERR_HTTP_CONN;
|
||||
char buf[HTTP_CLIENT_CHUNK_SIZE] = { 0 };
|
||||
osal_timer_t timer;
|
||||
|
||||
osal_timer_init(&timer);
|
||||
osal_timer_countdown_ms(&timer, timeout_ms);
|
||||
|
||||
if (!client->network.is_connected(&client->network)) {
|
||||
QCLOUD_LOG_E("Connection has not been established");
|
||||
QCLOUD_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
if (client_data->is_more) {
|
||||
client_data->response_buf[0] = '\0';
|
||||
rc = _http_client_retrieve_content(client, buf, reclen, osal_timer_remain(&timer), client_data);
|
||||
} else {
|
||||
client_data->is_more = QCLOUD_TRUE;
|
||||
rc = _http_client_recv(client, buf, 1, HTTP_CLIENT_CHUNK_SIZE - 1, &reclen, osal_timer_remain(&timer), client_data);
|
||||
|
||||
if (rc != QCLOUD_ERR_SUCCESS) {
|
||||
QCLOUD_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
buf[reclen] = '\0';
|
||||
|
||||
if (reclen) {
|
||||
//osal_printf("RESPONSE:\n%s", buf);
|
||||
rc = _http_client_response_parse(client, buf, reclen, osal_timer_remain(&timer), client_data);
|
||||
}
|
||||
}
|
||||
|
||||
QCLOUD_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
static int _http_network_init(qcloud_network_t *network, const char *host, int port, const char *ca_crt_dir)
|
||||
{
|
||||
int rc = QCLOUD_ERR_SUCCESS;
|
||||
if (network == NULL) {
|
||||
return QCLOUD_ERR_INVAL;
|
||||
}
|
||||
#if (QCLOUD_CFG_TLS_EN > 0u)
|
||||
if (ca_crt_dir != NULL) {
|
||||
network->tls_opt.ca_cert = ca_crt_dir;
|
||||
network->tls_opt.ca_cert_len = strlen(network->tls_opt.ca_cert);
|
||||
network->tls_opt.timeout = 10000;
|
||||
}
|
||||
#endif
|
||||
|
||||
memset(network->host, 0, sizeof(network->host));
|
||||
strncpy(network->host, host, sizeof(network->host) - 1);
|
||||
|
||||
network->port = port;
|
||||
|
||||
rc = qcloud_network_tcp_init(network);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int qcloud_http_client_connect(HTTPClient *client, const char *url, int port, const char *ca_crt)
|
||||
{
|
||||
if (!client->network.is_connected(&client->network)) {
|
||||
QCLOUD_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_ERR_SUCCESS) return rc;
|
||||
|
||||
rc = _http_network_init(&client->network, host, port, ca_crt);
|
||||
if (rc != QCLOUD_ERR_SUCCESS)
|
||||
return rc;
|
||||
|
||||
rc = _http_client_connect(client);
|
||||
if (rc != QCLOUD_ERR_SUCCESS) {
|
||||
QCLOUD_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 */
|
||||
QCLOUD_LOG_D("http client connect success");
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
void qcloud_http_client_close(HTTPClient *client)
|
||||
{
|
||||
if (client->network.is_connected(&client->network)) {
|
||||
client->network.disconnect(&client->network);
|
||||
}
|
||||
}
|
||||
|
||||
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.is_connected(&client->network)) {
|
||||
rc = qcloud_http_client_connect(client, url, port, ca_crt);
|
||||
if (rc != QCLOUD_ERR_SUCCESS) return rc;
|
||||
}
|
||||
|
||||
rc = _http_client_send_request(client, url, method, client_data);
|
||||
if (rc != QCLOUD_ERR_SUCCESS) {
|
||||
QCLOUD_LOG_E("http_client_send_request is error,rc = %d", rc);
|
||||
qcloud_http_client_close(client);
|
||||
return rc;
|
||||
}
|
||||
|
||||
return QCLOUD_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
int qcloud_http_recv_data(HTTPClient *client, uint32_t timeout_ms, HTTPClientData *client_data)
|
||||
{
|
||||
QCLOUD_FUNC_ENTRY;
|
||||
|
||||
int rc = QCLOUD_ERR_SUCCESS;
|
||||
osal_timer_t timer;
|
||||
|
||||
osal_timer_init(&timer);
|
||||
osal_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, osal_timer_remain(&timer), client_data);
|
||||
if (rc < 0) {
|
||||
QCLOUD_LOG_E("http_client_recv_response is error,rc = %d", rc);
|
||||
qcloud_http_client_close(client);
|
||||
QCLOUD_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
}
|
||||
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,250 @@
|
||||
/*
|
||||
* Copyright (c) 2017-2019 Tencent Group. All rights reserved.
|
||||
* License-Identifier: Apache-2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "qcloud.h"
|
||||
|
||||
#define json_debug QCLOUD_LOG_D
|
||||
|
||||
typedef struct JSON_NV {
|
||||
int nLen;
|
||||
int vLen;
|
||||
int vType;
|
||||
char *pN;
|
||||
char *pV;
|
||||
} JSON_NV;
|
||||
|
||||
char *json_get_object(int type, char *str)
|
||||
{
|
||||
char *pos = 0;
|
||||
char ch = (type == JSOBJECT) ? '{' : '[';
|
||||
while (str != 0 && *str != 0) {
|
||||
if (*str == ' ') {
|
||||
str++;
|
||||
continue;
|
||||
}
|
||||
pos = (*str == ch) ? str : 0;
|
||||
break;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
char *json_get_next_object(int type, char *str, char **key, int *key_len,
|
||||
char **val, int *val_len, int *val_type)
|
||||
{
|
||||
char JsonMark[JSTYPEMAX][2] = { { '\"', '\"' }, { '{', '}' }, { '[', ']' }, { '0', ' ' } };
|
||||
int iMarkDepth = 0, iValueType = JSNONE, iNameLen = 0, iValueLen = 0;
|
||||
char *p_cName = 0, *p_cValue = 0, *p_cPos = str;
|
||||
|
||||
if (type == JSOBJECT) {
|
||||
/* Get Key */
|
||||
p_cPos = strchr(p_cPos, '"');
|
||||
if (!p_cPos) {
|
||||
return 0;
|
||||
}
|
||||
p_cName = ++p_cPos;
|
||||
p_cPos = strchr(p_cPos, '"');
|
||||
if (!p_cPos) {
|
||||
return 0;
|
||||
}
|
||||
iNameLen = p_cPos - p_cName;
|
||||
|
||||
/* Get Value */
|
||||
p_cPos = strchr(p_cPos, ':');
|
||||
}
|
||||
while (p_cPos && *p_cPos) {
|
||||
if (*p_cPos == '"') {
|
||||
iValueType = JSSTRING;
|
||||
p_cValue = ++p_cPos;
|
||||
break;
|
||||
} else if (*p_cPos == '{') {
|
||||
iValueType = JSOBJECT;
|
||||
p_cValue = p_cPos++;
|
||||
break;
|
||||
} else if (*p_cPos == '[') {
|
||||
iValueType = JSARRAY;
|
||||
p_cValue = p_cPos++;
|
||||
break;
|
||||
} else if ((*p_cPos == '-') || (*p_cPos >= '0' && *p_cPos <= '9')) {
|
||||
iValueType = JSNUMBER;
|
||||
p_cValue = p_cPos++;
|
||||
break;
|
||||
} else if (*p_cPos == 't' || *p_cPos == 'T' || *p_cPos == 'f' || *p_cPos == 'F') {
|
||||
iValueType = JSBOOLEAN;
|
||||
p_cValue = p_cPos;
|
||||
break;
|
||||
}else if (*p_cPos == 'n' || *p_cPos == 'N') {
|
||||
iValueType = JSNULL;
|
||||
p_cValue = p_cPos;
|
||||
break;
|
||||
}
|
||||
p_cPos++;
|
||||
}
|
||||
while (p_cPos && *p_cPos && iValueType > JSNONE) {
|
||||
if (iValueType == JSBOOLEAN) {
|
||||
int len = strlen(p_cValue);
|
||||
|
||||
if ((*p_cValue == 't' || *p_cValue == 'T') && len >= 4
|
||||
&& (!strncmp(p_cValue, "true", 4)
|
||||
|| !strncmp(p_cValue, "TRUE", 4))) {
|
||||
iValueLen = 4;
|
||||
p_cPos = p_cValue + iValueLen;
|
||||
break;
|
||||
} else if ((*p_cValue == 'f' || *p_cValue == 'F') && len >= 5
|
||||
&& (!strncmp(p_cValue, "false", 5)
|
||||
|| !strncmp(p_cValue, "FALSE", 5))) {
|
||||
iValueLen = 5;
|
||||
p_cPos = p_cValue + iValueLen;
|
||||
break;
|
||||
}
|
||||
} else if (iValueType == JSNULL) { //support null/NULL
|
||||
int nlen = strlen(p_cValue);
|
||||
|
||||
if ((*p_cValue == 'n' || *p_cValue == 'N') && nlen >= 4
|
||||
&& (!strncmp(p_cValue, "null", 4)
|
||||
|| !strncmp(p_cValue, "NULL", 4))) {
|
||||
iValueLen = 4;
|
||||
p_cPos = p_cValue + iValueLen;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (iValueType == JSNUMBER) {
|
||||
//if (*p_cPos < '0' || *p_cPos > '9') {
|
||||
if ((*p_cPos < '0' || *p_cPos > '9')&&(*p_cPos != '.')) { //support float
|
||||
iValueLen = p_cPos - p_cValue;
|
||||
break;
|
||||
}
|
||||
} else if (*p_cPos == JsonMark[iValueType][1]) {
|
||||
if (iMarkDepth == 0) {
|
||||
iValueLen = p_cPos - p_cValue + (iValueType == JSSTRING ? 0 : 1);
|
||||
p_cPos++;
|
||||
break;
|
||||
} else {
|
||||
iMarkDepth--;
|
||||
}
|
||||
} else if (*p_cPos == JsonMark[iValueType][0]) {
|
||||
iMarkDepth++;
|
||||
}
|
||||
p_cPos++;
|
||||
}
|
||||
|
||||
if (type == JSOBJECT) {
|
||||
*key = p_cName;
|
||||
*key_len = iNameLen;
|
||||
}
|
||||
|
||||
*val = p_cValue;
|
||||
*val_len = iValueLen;
|
||||
*val_type = iValueType;
|
||||
if (iValueType == JSSTRING) {
|
||||
return p_cValue + iValueLen + 1;
|
||||
} else {
|
||||
return p_cValue + iValueLen;
|
||||
}
|
||||
}
|
||||
|
||||
int json_parse_name_value(char *p_cJsonStr, int iStrLen, json_parse_cb pfnCB, void *p_CBData)
|
||||
{
|
||||
char *pos = 0, *key = 0, *val = 0;
|
||||
int klen = 0, vlen = 0, vtype = 0;
|
||||
char last_char = 0;
|
||||
int ret = JSON_RESULT_ERR;
|
||||
|
||||
if (p_cJsonStr == NULL || iStrLen == 0 || pfnCB == NULL) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (iStrLen != strlen(p_cJsonStr)) {
|
||||
QCLOUD_LOG_W("Backup last_char since %d != %d", iStrLen, (int)strlen(p_cJsonStr));
|
||||
backup_json_str_last_char(p_cJsonStr, iStrLen, last_char);
|
||||
}
|
||||
|
||||
json_object_for_each_kv(p_cJsonStr, pos, key, klen, val, vlen, vtype) {
|
||||
if (key && klen && val && vlen) {
|
||||
ret = JSON_RESULT_OK;
|
||||
if (JSON_PARSE_FINISH == pfnCB(key, klen, val, vlen, vtype, p_CBData)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (iStrLen != strlen(p_cJsonStr)) {
|
||||
restore_json_str_last_char(p_cJsonStr, iStrLen, last_char);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int json_get_value_by_name_cb(char *p_cName, int iNameLen, char *p_cValue, int iValueLen, int iValueType,
|
||||
void *p_CBData)
|
||||
{
|
||||
JSON_NV *p_stNameValue = (JSON_NV *)p_CBData;
|
||||
|
||||
#if (JSON_DEBUG == 1)
|
||||
int i;
|
||||
|
||||
if (p_cName) {
|
||||
json_debug("Name:");
|
||||
for (i = 0; i < iNameLen; i++) {
|
||||
json_debug("%c", *(p_cName + i));
|
||||
}
|
||||
}
|
||||
|
||||
if (p_cValue) {
|
||||
json_debug("Value:");
|
||||
for (i = 0; i < iValueLen; i++) {
|
||||
json_debug("%c", *(p_cValue + i));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!strncmp(p_cName, p_stNameValue->pN, p_stNameValue->nLen)) {
|
||||
p_stNameValue->pV = p_cValue;
|
||||
p_stNameValue->vLen = iValueLen;
|
||||
p_stNameValue->vType = iValueType;
|
||||
return JSON_PARSE_FINISH;
|
||||
} else {
|
||||
return JSON_PARSE_OK;
|
||||
}
|
||||
}
|
||||
|
||||
char *json_get_value_by_name(char *p_cJsonStr, int iStrLen, char *p_cName, int *p_iValueLen, int *p_iValueType)
|
||||
{
|
||||
JSON_NV stNV;
|
||||
|
||||
memset(&stNV, 0, sizeof(stNV));
|
||||
stNV.pN = p_cName;
|
||||
stNV.nLen = strlen(p_cName);
|
||||
if (JSON_RESULT_OK == json_parse_name_value(p_cJsonStr, iStrLen, json_get_value_by_name_cb, (void *)&stNV)) {
|
||||
if (p_iValueLen) {
|
||||
*p_iValueLen = stNV.vLen;
|
||||
}
|
||||
if (p_iValueType) {
|
||||
*p_iValueType = stNV.vType;
|
||||
if(JSNULL == stNV.vType){
|
||||
stNV.pV = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
return stNV.pV;
|
||||
}
|
||||
|
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
* Copyright (c) 2017-2019 Tencent Group. All rights reserved.
|
||||
* License-Identifier: Apache-2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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 "qcloud.h"
|
||||
|
||||
#ifndef SCNi8
|
||||
#define SCNi8 "hhi"
|
||||
#endif
|
||||
|
||||
#ifndef SCNu8
|
||||
#define SCNu8 "hhu"
|
||||
#endif
|
||||
|
||||
#ifndef SCNi16
|
||||
#define SCNi16 "hi"
|
||||
#endif
|
||||
|
||||
#ifndef SCNu16
|
||||
#define SCNu16 "hu"
|
||||
#endif
|
||||
|
||||
#ifndef SCNi32
|
||||
#define SCNi32 "i"
|
||||
#endif
|
||||
|
||||
#ifndef SCNu32
|
||||
#define SCNu32 "u"
|
||||
#endif
|
||||
|
||||
char *LITE_json_value_of(char *key, char *src)
|
||||
{
|
||||
char *value = NULL;
|
||||
int value_len = -1;
|
||||
char *ret = NULL;
|
||||
|
||||
char *delim = NULL;
|
||||
char *key_iter;
|
||||
char *key_next;
|
||||
int key_len;
|
||||
char *src_iter;
|
||||
|
||||
src_iter = src;
|
||||
key_iter = key;
|
||||
|
||||
do {
|
||||
if ((delim = strchr(key_iter, '.')) != NULL) {
|
||||
key_len = delim - key_iter;
|
||||
key_next = osal_malloc(key_len + 1);
|
||||
strncpy(key_next, key_iter, key_len);
|
||||
key_next[key_len] = '\0';
|
||||
value = json_get_value_by_name(src_iter, strlen(src_iter), key_next, &value_len, 0);
|
||||
|
||||
if (value == NULL) {
|
||||
osal_free(key_next);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
src_iter = value;
|
||||
key_iter = delim + 1;
|
||||
osal_free(key_next);
|
||||
}
|
||||
} while (delim);
|
||||
|
||||
value = json_get_value_by_name(src_iter, strlen(src_iter), key_iter, &value_len, 0);
|
||||
if (NULL == value) {
|
||||
return NULL;
|
||||
}
|
||||
ret = osal_malloc((value_len + 1) * sizeof(char));
|
||||
if (NULL == ret) {
|
||||
return NULL;
|
||||
}
|
||||
osal_snprintf(ret, value_len + 1, "%s", value);
|
||||
return ret;
|
||||
}
|
||||
|
||||
list_head_t *LITE_json_keys_of(char *src, char *prefix)
|
||||
{
|
||||
static LIST_HEAD(keylist);
|
||||
|
||||
char *pos = 0, *key = 0, *val = 0;
|
||||
int klen = 0, vlen = 0, vtype = 0;
|
||||
|
||||
if (src == NULL || prefix == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!strcmp("", prefix)) {
|
||||
INIT_LIST_HEAD(&keylist);
|
||||
}
|
||||
|
||||
json_object_for_each_kv(src, pos, key, klen, val, vlen, vtype) {
|
||||
if (key && klen && val && vlen) {
|
||||
|
||||
json_key_t *entry = NULL;
|
||||
|
||||
entry = osal_malloc(sizeof(json_key_t));
|
||||
memset(entry, 0, sizeof(json_key_t));
|
||||
entry->key = LITE_format_string("%s%.*s", prefix, klen, key);
|
||||
list_add_tail(&entry->list, &keylist);
|
||||
|
||||
if (JSOBJECT == vtype) {
|
||||
char *iter_val = LITE_format_string("%.*s", vlen, val);
|
||||
char *iter_pre = LITE_format_string("%s%.*s.", prefix, klen, key);
|
||||
LITE_json_keys_of(iter_val, iter_pre);
|
||||
osal_free(iter_val);
|
||||
osal_free(iter_pre);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!strcmp("", prefix)) {
|
||||
json_key_t *entry = NULL;
|
||||
|
||||
entry = osal_malloc(sizeof(json_key_t));
|
||||
memset(entry, 0, sizeof(json_key_t));
|
||||
list_add_tail(&entry->list, &keylist);
|
||||
|
||||
return &keylist;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void LITE_json_keys_release(list_head_t *keylist)
|
||||
{
|
||||
json_key_t *pos, *tmp;
|
||||
|
||||
list_for_each_entry_safe(pos, tmp, keylist, list, json_key_t) {
|
||||
if (pos->key) {
|
||||
osal_free(pos->key);
|
||||
}
|
||||
list_del(&pos->list);
|
||||
osal_free(pos);
|
||||
}
|
||||
}
|
||||
|
||||
qcloud_err_t LITE_get_int32(int32_t *value, char *src) {
|
||||
return (sscanf(src, "%" SCNi32, value) == 1) ? QCLOUD_ERR_SUCCESS : QCLOUD_ERR_FAILURE;
|
||||
}
|
||||
|
||||
qcloud_err_t LITE_get_int16(int16_t *value, char *src) {
|
||||
return (sscanf(src, "%" SCNi16, value) == 1) ? QCLOUD_ERR_SUCCESS : QCLOUD_ERR_FAILURE;
|
||||
}
|
||||
|
||||
qcloud_err_t LITE_get_int8(int8_t *value, char *src) {
|
||||
return (sscanf(src, "%" SCNi8, value) == 1) ? QCLOUD_ERR_SUCCESS : QCLOUD_ERR_FAILURE;
|
||||
}
|
||||
|
||||
qcloud_err_t LITE_get_uint32(uint32_t *value, char *src) {
|
||||
return (sscanf(src, "%" SCNu32, value) == 1) ? QCLOUD_ERR_SUCCESS : QCLOUD_ERR_FAILURE;
|
||||
}
|
||||
|
||||
qcloud_err_t LITE_get_uint16(uint16_t *value, char *src) {
|
||||
return (sscanf(src, "%" SCNu16, value) == 1) ? QCLOUD_ERR_SUCCESS : QCLOUD_ERR_FAILURE;
|
||||
}
|
||||
|
||||
qcloud_err_t LITE_get_uint8(uint8_t *value, char *src) {
|
||||
return (sscanf(src, "%" SCNu8, value) == 1) ? QCLOUD_ERR_SUCCESS : QCLOUD_ERR_FAILURE;
|
||||
}
|
||||
|
||||
qcloud_err_t LITE_get_float(float *value, char *src) {
|
||||
return (sscanf(src, "%f", value) == 1) ? QCLOUD_ERR_SUCCESS : QCLOUD_ERR_FAILURE;
|
||||
}
|
||||
|
||||
qcloud_err_t LITE_get_double(double *value, char *src) {
|
||||
return (sscanf(src, "%lf", value) == 1) ? QCLOUD_ERR_SUCCESS : QCLOUD_ERR_FAILURE;
|
||||
}
|
||||
|
||||
qcloud_err_t LITE_get_boolean(bool *value, char *src) {
|
||||
if (!strcmp(src, "false")) {
|
||||
*value = false;
|
||||
}
|
||||
else {
|
||||
*value = true;
|
||||
}
|
||||
|
||||
return QCLOUD_ERR_SUCCESS;
|
||||
}
|
||||
|
@@ -0,0 +1,315 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright (C) 2016 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 <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "qcloud.h"
|
||||
|
||||
#define MD5_DIGEST_SIZE 16
|
||||
|
||||
|
||||
/* Implementation that should never be optimized out by the compiler */
|
||||
static void _utils_md5_zeroize(void *v, size_t n)
|
||||
{
|
||||
volatile unsigned char *p = v;
|
||||
while (n--) *p++ = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 32-bit integer manipulation macros (little endian)
|
||||
*/
|
||||
#ifndef IOT_MD5_GET_UINT32_LE
|
||||
#define IOT_MD5_GET_UINT32_LE(n,b,i) \
|
||||
{ \
|
||||
(n) = ( (uint32_t) (b)[(i) ] ) \
|
||||
| ( (uint32_t) (b)[(i) + 1] << 8 ) \
|
||||
| ( (uint32_t) (b)[(i) + 2] << 16 ) \
|
||||
| ( (uint32_t) (b)[(i) + 3] << 24 ); \
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef IOT_MD5_PUT_UINT32_LE
|
||||
#define IOT_MD5_PUT_UINT32_LE(n,b,i) \
|
||||
{ \
|
||||
(b)[(i) ] = (unsigned char) ( ( (n) ) & 0xFF ); \
|
||||
(b)[(i) + 1] = (unsigned char) ( ( (n) >> 8 ) & 0xFF ); \
|
||||
(b)[(i) + 2] = (unsigned char) ( ( (n) >> 16 ) & 0xFF ); \
|
||||
(b)[(i) + 3] = (unsigned char) ( ( (n) >> 24 ) & 0xFF ); \
|
||||
}
|
||||
#endif
|
||||
|
||||
void utils_md5_init(iot_md5_context *ctx)
|
||||
{
|
||||
memset(ctx, 0, sizeof(iot_md5_context));
|
||||
}
|
||||
|
||||
void utils_md5_free(iot_md5_context *ctx)
|
||||
{
|
||||
if (ctx == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
_utils_md5_zeroize(ctx, sizeof(iot_md5_context));
|
||||
}
|
||||
|
||||
void utils_md5_clone(iot_md5_context *dst,
|
||||
const iot_md5_context *src)
|
||||
{
|
||||
*dst = *src;
|
||||
}
|
||||
|
||||
/*
|
||||
* MD5 context setup
|
||||
*/
|
||||
void utils_md5_starts(iot_md5_context *ctx)
|
||||
{
|
||||
ctx->total[0] = 0;
|
||||
ctx->total[1] = 0;
|
||||
|
||||
ctx->state[0] = 0x67452301;
|
||||
ctx->state[1] = 0xEFCDAB89;
|
||||
ctx->state[2] = 0x98BADCFE;
|
||||
ctx->state[3] = 0x10325476;
|
||||
}
|
||||
|
||||
void utils_md5_process(iot_md5_context *ctx, const unsigned char data[64])
|
||||
{
|
||||
uint32_t X[16], A, B, C, D;
|
||||
|
||||
IOT_MD5_GET_UINT32_LE(X[ 0], data, 0);
|
||||
IOT_MD5_GET_UINT32_LE(X[ 1], data, 4);
|
||||
IOT_MD5_GET_UINT32_LE(X[ 2], data, 8);
|
||||
IOT_MD5_GET_UINT32_LE(X[ 3], data, 12);
|
||||
IOT_MD5_GET_UINT32_LE(X[ 4], data, 16);
|
||||
IOT_MD5_GET_UINT32_LE(X[ 5], data, 20);
|
||||
IOT_MD5_GET_UINT32_LE(X[ 6], data, 24);
|
||||
IOT_MD5_GET_UINT32_LE(X[ 7], data, 28);
|
||||
IOT_MD5_GET_UINT32_LE(X[ 8], data, 32);
|
||||
IOT_MD5_GET_UINT32_LE(X[ 9], data, 36);
|
||||
IOT_MD5_GET_UINT32_LE(X[10], data, 40);
|
||||
IOT_MD5_GET_UINT32_LE(X[11], data, 44);
|
||||
IOT_MD5_GET_UINT32_LE(X[12], data, 48);
|
||||
IOT_MD5_GET_UINT32_LE(X[13], data, 52);
|
||||
IOT_MD5_GET_UINT32_LE(X[14], data, 56);
|
||||
IOT_MD5_GET_UINT32_LE(X[15], data, 60);
|
||||
|
||||
#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n)))
|
||||
|
||||
#define P(a,b,c,d,k,s,t) \
|
||||
{ \
|
||||
a += F(b,c,d) + X[k] + t; a = S(a,s) + b; \
|
||||
}
|
||||
|
||||
A = ctx->state[0];
|
||||
B = ctx->state[1];
|
||||
C = ctx->state[2];
|
||||
D = ctx->state[3];
|
||||
|
||||
#define F(x,y,z) (z ^ (x & (y ^ z)))
|
||||
|
||||
P(A, B, C, D, 0, 7, 0xD76AA478);
|
||||
P(D, A, B, C, 1, 12, 0xE8C7B756);
|
||||
P(C, D, A, B, 2, 17, 0x242070DB);
|
||||
P(B, C, D, A, 3, 22, 0xC1BDCEEE);
|
||||
P(A, B, C, D, 4, 7, 0xF57C0FAF);
|
||||
P(D, A, B, C, 5, 12, 0x4787C62A);
|
||||
P(C, D, A, B, 6, 17, 0xA8304613);
|
||||
P(B, C, D, A, 7, 22, 0xFD469501);
|
||||
P(A, B, C, D, 8, 7, 0x698098D8);
|
||||
P(D, A, B, C, 9, 12, 0x8B44F7AF);
|
||||
P(C, D, A, B, 10, 17, 0xFFFF5BB1);
|
||||
P(B, C, D, A, 11, 22, 0x895CD7BE);
|
||||
P(A, B, C, D, 12, 7, 0x6B901122);
|
||||
P(D, A, B, C, 13, 12, 0xFD987193);
|
||||
P(C, D, A, B, 14, 17, 0xA679438E);
|
||||
P(B, C, D, A, 15, 22, 0x49B40821);
|
||||
|
||||
#undef F
|
||||
|
||||
#define F(x,y,z) (y ^ (z & (x ^ y)))
|
||||
|
||||
P(A, B, C, D, 1, 5, 0xF61E2562);
|
||||
P(D, A, B, C, 6, 9, 0xC040B340);
|
||||
P(C, D, A, B, 11, 14, 0x265E5A51);
|
||||
P(B, C, D, A, 0, 20, 0xE9B6C7AA);
|
||||
P(A, B, C, D, 5, 5, 0xD62F105D);
|
||||
P(D, A, B, C, 10, 9, 0x02441453);
|
||||
P(C, D, A, B, 15, 14, 0xD8A1E681);
|
||||
P(B, C, D, A, 4, 20, 0xE7D3FBC8);
|
||||
P(A, B, C, D, 9, 5, 0x21E1CDE6);
|
||||
P(D, A, B, C, 14, 9, 0xC33707D6);
|
||||
P(C, D, A, B, 3, 14, 0xF4D50D87);
|
||||
P(B, C, D, A, 8, 20, 0x455A14ED);
|
||||
P(A, B, C, D, 13, 5, 0xA9E3E905);
|
||||
P(D, A, B, C, 2, 9, 0xFCEFA3F8);
|
||||
P(C, D, A, B, 7, 14, 0x676F02D9);
|
||||
P(B, C, D, A, 12, 20, 0x8D2A4C8A);
|
||||
|
||||
#undef F
|
||||
|
||||
#define F(x,y,z) (x ^ y ^ z)
|
||||
|
||||
P(A, B, C, D, 5, 4, 0xFFFA3942);
|
||||
P(D, A, B, C, 8, 11, 0x8771F681);
|
||||
P(C, D, A, B, 11, 16, 0x6D9D6122);
|
||||
P(B, C, D, A, 14, 23, 0xFDE5380C);
|
||||
P(A, B, C, D, 1, 4, 0xA4BEEA44);
|
||||
P(D, A, B, C, 4, 11, 0x4BDECFA9);
|
||||
P(C, D, A, B, 7, 16, 0xF6BB4B60);
|
||||
P(B, C, D, A, 10, 23, 0xBEBFBC70);
|
||||
P(A, B, C, D, 13, 4, 0x289B7EC6);
|
||||
P(D, A, B, C, 0, 11, 0xEAA127FA);
|
||||
P(C, D, A, B, 3, 16, 0xD4EF3085);
|
||||
P(B, C, D, A, 6, 23, 0x04881D05);
|
||||
P(A, B, C, D, 9, 4, 0xD9D4D039);
|
||||
P(D, A, B, C, 12, 11, 0xE6DB99E5);
|
||||
P(C, D, A, B, 15, 16, 0x1FA27CF8);
|
||||
P(B, C, D, A, 2, 23, 0xC4AC5665);
|
||||
|
||||
#undef F
|
||||
|
||||
#define F(x,y,z) (y ^ (x | ~z))
|
||||
|
||||
P(A, B, C, D, 0, 6, 0xF4292244);
|
||||
P(D, A, B, C, 7, 10, 0x432AFF97);
|
||||
P(C, D, A, B, 14, 15, 0xAB9423A7);
|
||||
P(B, C, D, A, 5, 21, 0xFC93A039);
|
||||
P(A, B, C, D, 12, 6, 0x655B59C3);
|
||||
P(D, A, B, C, 3, 10, 0x8F0CCC92);
|
||||
P(C, D, A, B, 10, 15, 0xFFEFF47D);
|
||||
P(B, C, D, A, 1, 21, 0x85845DD1);
|
||||
P(A, B, C, D, 8, 6, 0x6FA87E4F);
|
||||
P(D, A, B, C, 15, 10, 0xFE2CE6E0);
|
||||
P(C, D, A, B, 6, 15, 0xA3014314);
|
||||
P(B, C, D, A, 13, 21, 0x4E0811A1);
|
||||
P(A, B, C, D, 4, 6, 0xF7537E82);
|
||||
P(D, A, B, C, 11, 10, 0xBD3AF235);
|
||||
P(C, D, A, B, 2, 15, 0x2AD7D2BB);
|
||||
P(B, C, D, A, 9, 21, 0xEB86D391);
|
||||
|
||||
#undef F
|
||||
|
||||
ctx->state[0] += A;
|
||||
ctx->state[1] += B;
|
||||
ctx->state[2] += C;
|
||||
ctx->state[3] += D;
|
||||
}
|
||||
|
||||
/*
|
||||
* MD5 process buffer
|
||||
*/
|
||||
void utils_md5_update(iot_md5_context *ctx, const unsigned char *input, size_t ilen)
|
||||
{
|
||||
size_t fill;
|
||||
uint32_t left;
|
||||
|
||||
if (ilen == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
left = ctx->total[0] & 0x3F;
|
||||
fill = 64 - left;
|
||||
|
||||
ctx->total[0] += (uint32_t) ilen;
|
||||
ctx->total[0] &= 0xFFFFFFFF;
|
||||
|
||||
if (ctx->total[0] < (uint32_t) ilen) {
|
||||
ctx->total[1]++;
|
||||
}
|
||||
|
||||
if (left && ilen >= fill) {
|
||||
memcpy((void *)(ctx->buffer + left), input, fill);
|
||||
utils_md5_process(ctx, ctx->buffer);
|
||||
input += fill;
|
||||
ilen -= fill;
|
||||
left = 0;
|
||||
}
|
||||
|
||||
while (ilen >= 64) {
|
||||
utils_md5_process(ctx, input);
|
||||
input += 64;
|
||||
ilen -= 64;
|
||||
}
|
||||
|
||||
if (ilen > 0) {
|
||||
memcpy((void *)(ctx->buffer + left), input, ilen);
|
||||
}
|
||||
}
|
||||
|
||||
static const unsigned char iot_md5_padding[64] = {
|
||||
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
|
||||
/*
|
||||
* MD5 final digest
|
||||
*/
|
||||
void utils_md5_finish(iot_md5_context *ctx, unsigned char output[16])
|
||||
{
|
||||
uint32_t last, padn;
|
||||
uint32_t high, low;
|
||||
unsigned char msglen[8];
|
||||
|
||||
high = (ctx->total[0] >> 29)
|
||||
| (ctx->total[1] << 3);
|
||||
low = (ctx->total[0] << 3);
|
||||
|
||||
IOT_MD5_PUT_UINT32_LE(low, msglen, 0);
|
||||
IOT_MD5_PUT_UINT32_LE(high, msglen, 4);
|
||||
|
||||
last = ctx->total[0] & 0x3F;
|
||||
padn = (last < 56) ? (56 - last) : (120 - last);
|
||||
|
||||
utils_md5_update(ctx, iot_md5_padding, padn);
|
||||
utils_md5_update(ctx, msglen, 8);
|
||||
|
||||
IOT_MD5_PUT_UINT32_LE(ctx->state[0], output, 0);
|
||||
IOT_MD5_PUT_UINT32_LE(ctx->state[1], output, 4);
|
||||
IOT_MD5_PUT_UINT32_LE(ctx->state[2], output, 8);
|
||||
IOT_MD5_PUT_UINT32_LE(ctx->state[3], output, 12);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* output = MD5( input buffer )
|
||||
*/
|
||||
void utils_md5(const unsigned char *input, size_t ilen, unsigned char output[16])
|
||||
{
|
||||
iot_md5_context ctx;
|
||||
|
||||
utils_md5_init(&ctx);
|
||||
utils_md5_starts(&ctx);
|
||||
utils_md5_update(&ctx, input, ilen);
|
||||
utils_md5_finish(&ctx, output);
|
||||
utils_md5_free(&ctx);
|
||||
}
|
||||
|
||||
int8_t utils_hb2hex(uint8_t hb)
|
||||
{
|
||||
hb = hb & 0xF;
|
||||
return (int8_t)(hb < 10 ? '0' + hb : hb - 10 + 'a');
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,335 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright (C) 2016 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 <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "qcloud.h"
|
||||
|
||||
/* Implementation that should never be optimized out by the compiler */
|
||||
static void utils_sha1_zeroize(void *v, size_t n)
|
||||
{
|
||||
volatile unsigned char *p = v;
|
||||
while (n--) {
|
||||
*p++ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 32-bit integer manipulation macros (big endian)
|
||||
*/
|
||||
#ifndef IOT_SHA1_GET_UINT32_BE
|
||||
#define IOT_SHA1_GET_UINT32_BE(n,b,i) \
|
||||
{ \
|
||||
(n) = ( (uint32_t) (b)[(i) ] << 24 ) \
|
||||
| ( (uint32_t) (b)[(i) + 1] << 16 ) \
|
||||
| ( (uint32_t) (b)[(i) + 2] << 8 ) \
|
||||
| ( (uint32_t) (b)[(i) + 3] ); \
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef IOT_SHA1_PUT_UINT32_BE
|
||||
#define IOT_SHA1_PUT_UINT32_BE(n,b,i) \
|
||||
{ \
|
||||
(b)[(i) ] = (unsigned char) ( (n) >> 24 ); \
|
||||
(b)[(i) + 1] = (unsigned char) ( (n) >> 16 ); \
|
||||
(b)[(i) + 2] = (unsigned char) ( (n) >> 8 ); \
|
||||
(b)[(i) + 3] = (unsigned char) ( (n) ); \
|
||||
}
|
||||
#endif
|
||||
|
||||
void utils_sha1_init(iot_sha1_context *ctx)
|
||||
{
|
||||
memset(ctx, 0, sizeof(iot_sha1_context));
|
||||
}
|
||||
|
||||
void utils_sha1_free(iot_sha1_context *ctx)
|
||||
{
|
||||
if (ctx == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
utils_sha1_zeroize(ctx, sizeof(iot_sha1_context));
|
||||
}
|
||||
|
||||
void utils_sha1_clone(iot_sha1_context *dst,
|
||||
const iot_sha1_context *src)
|
||||
{
|
||||
*dst = *src;
|
||||
}
|
||||
|
||||
/*
|
||||
* SHA-1 context setup
|
||||
*/
|
||||
void utils_sha1_starts(iot_sha1_context *ctx)
|
||||
{
|
||||
ctx->total[0] = 0;
|
||||
ctx->total[1] = 0;
|
||||
|
||||
ctx->state[0] = 0x67452301;
|
||||
ctx->state[1] = 0xEFCDAB89;
|
||||
ctx->state[2] = 0x98BADCFE;
|
||||
ctx->state[3] = 0x10325476;
|
||||
ctx->state[4] = 0xC3D2E1F0;
|
||||
}
|
||||
|
||||
void utils_sha1_process(iot_sha1_context *ctx, const unsigned char data[64])
|
||||
{
|
||||
uint32_t temp, W[16], A, B, C, D, E;
|
||||
|
||||
IOT_SHA1_GET_UINT32_BE(W[ 0], data, 0);
|
||||
IOT_SHA1_GET_UINT32_BE(W[ 1], data, 4);
|
||||
IOT_SHA1_GET_UINT32_BE(W[ 2], data, 8);
|
||||
IOT_SHA1_GET_UINT32_BE(W[ 3], data, 12);
|
||||
IOT_SHA1_GET_UINT32_BE(W[ 4], data, 16);
|
||||
IOT_SHA1_GET_UINT32_BE(W[ 5], data, 20);
|
||||
IOT_SHA1_GET_UINT32_BE(W[ 6], data, 24);
|
||||
IOT_SHA1_GET_UINT32_BE(W[ 7], data, 28);
|
||||
IOT_SHA1_GET_UINT32_BE(W[ 8], data, 32);
|
||||
IOT_SHA1_GET_UINT32_BE(W[ 9], data, 36);
|
||||
IOT_SHA1_GET_UINT32_BE(W[10], data, 40);
|
||||
IOT_SHA1_GET_UINT32_BE(W[11], data, 44);
|
||||
IOT_SHA1_GET_UINT32_BE(W[12], data, 48);
|
||||
IOT_SHA1_GET_UINT32_BE(W[13], data, 52);
|
||||
IOT_SHA1_GET_UINT32_BE(W[14], data, 56);
|
||||
IOT_SHA1_GET_UINT32_BE(W[15], data, 60);
|
||||
|
||||
#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n)))
|
||||
|
||||
#define R(t) \
|
||||
( \
|
||||
temp = W[( t - 3 ) & 0x0F] ^ W[( t - 8 ) & 0x0F] ^ \
|
||||
W[( t - 14 ) & 0x0F] ^ W[ t & 0x0F], \
|
||||
( W[t & 0x0F] = S(temp,1) ) \
|
||||
)
|
||||
|
||||
#define P(a,b,c,d,e,x) \
|
||||
{ \
|
||||
e += S(a,5) + F(b,c,d) + K + x; b = S(b,30); \
|
||||
}
|
||||
|
||||
A = ctx->state[0];
|
||||
B = ctx->state[1];
|
||||
C = ctx->state[2];
|
||||
D = ctx->state[3];
|
||||
E = ctx->state[4];
|
||||
|
||||
#define F(x,y,z) (z ^ (x & (y ^ z)))
|
||||
#define K 0x5A827999
|
||||
|
||||
P(A, B, C, D, E, W[0]);
|
||||
P(E, A, B, C, D, W[1]);
|
||||
P(D, E, A, B, C, W[2]);
|
||||
P(C, D, E, A, B, W[3]);
|
||||
P(B, C, D, E, A, W[4]);
|
||||
P(A, B, C, D, E, W[5]);
|
||||
P(E, A, B, C, D, W[6]);
|
||||
P(D, E, A, B, C, W[7]);
|
||||
P(C, D, E, A, B, W[8]);
|
||||
P(B, C, D, E, A, W[9]);
|
||||
P(A, B, C, D, E, W[10]);
|
||||
P(E, A, B, C, D, W[11]);
|
||||
P(D, E, A, B, C, W[12]);
|
||||
P(C, D, E, A, B, W[13]);
|
||||
P(B, C, D, E, A, W[14]);
|
||||
P(A, B, C, D, E, W[15]);
|
||||
P(E, A, B, C, D, R(16));
|
||||
P(D, E, A, B, C, R(17));
|
||||
P(C, D, E, A, B, R(18));
|
||||
P(B, C, D, E, A, R(19));
|
||||
|
||||
#undef K
|
||||
#undef F
|
||||
|
||||
#define F(x,y,z) (x ^ y ^ z)
|
||||
#define K 0x6ED9EBA1
|
||||
|
||||
P(A, B, C, D, E, R(20));
|
||||
P(E, A, B, C, D, R(21));
|
||||
P(D, E, A, B, C, R(22));
|
||||
P(C, D, E, A, B, R(23));
|
||||
P(B, C, D, E, A, R(24));
|
||||
P(A, B, C, D, E, R(25));
|
||||
P(E, A, B, C, D, R(26));
|
||||
P(D, E, A, B, C, R(27));
|
||||
P(C, D, E, A, B, R(28));
|
||||
P(B, C, D, E, A, R(29));
|
||||
P(A, B, C, D, E, R(30));
|
||||
P(E, A, B, C, D, R(31));
|
||||
P(D, E, A, B, C, R(32));
|
||||
P(C, D, E, A, B, R(33));
|
||||
P(B, C, D, E, A, R(34));
|
||||
P(A, B, C, D, E, R(35));
|
||||
P(E, A, B, C, D, R(36));
|
||||
P(D, E, A, B, C, R(37));
|
||||
P(C, D, E, A, B, R(38));
|
||||
P(B, C, D, E, A, R(39));
|
||||
|
||||
#undef K
|
||||
#undef F
|
||||
|
||||
#define F(x,y,z) ((x & y) | (z & (x | y)))
|
||||
#define K 0x8F1BBCDC
|
||||
|
||||
P(A, B, C, D, E, R(40));
|
||||
P(E, A, B, C, D, R(41));
|
||||
P(D, E, A, B, C, R(42));
|
||||
P(C, D, E, A, B, R(43));
|
||||
P(B, C, D, E, A, R(44));
|
||||
P(A, B, C, D, E, R(45));
|
||||
P(E, A, B, C, D, R(46));
|
||||
P(D, E, A, B, C, R(47));
|
||||
P(C, D, E, A, B, R(48));
|
||||
P(B, C, D, E, A, R(49));
|
||||
P(A, B, C, D, E, R(50));
|
||||
P(E, A, B, C, D, R(51));
|
||||
P(D, E, A, B, C, R(52));
|
||||
P(C, D, E, A, B, R(53));
|
||||
P(B, C, D, E, A, R(54));
|
||||
P(A, B, C, D, E, R(55));
|
||||
P(E, A, B, C, D, R(56));
|
||||
P(D, E, A, B, C, R(57));
|
||||
P(C, D, E, A, B, R(58));
|
||||
P(B, C, D, E, A, R(59));
|
||||
|
||||
#undef K
|
||||
#undef F
|
||||
|
||||
#define F(x,y,z) (x ^ y ^ z)
|
||||
#define K 0xCA62C1D6
|
||||
|
||||
P(A, B, C, D, E, R(60));
|
||||
P(E, A, B, C, D, R(61));
|
||||
P(D, E, A, B, C, R(62));
|
||||
P(C, D, E, A, B, R(63));
|
||||
P(B, C, D, E, A, R(64));
|
||||
P(A, B, C, D, E, R(65));
|
||||
P(E, A, B, C, D, R(66));
|
||||
P(D, E, A, B, C, R(67));
|
||||
P(C, D, E, A, B, R(68));
|
||||
P(B, C, D, E, A, R(69));
|
||||
P(A, B, C, D, E, R(70));
|
||||
P(E, A, B, C, D, R(71));
|
||||
P(D, E, A, B, C, R(72));
|
||||
P(C, D, E, A, B, R(73));
|
||||
P(B, C, D, E, A, R(74));
|
||||
P(A, B, C, D, E, R(75));
|
||||
P(E, A, B, C, D, R(76));
|
||||
P(D, E, A, B, C, R(77));
|
||||
P(C, D, E, A, B, R(78));
|
||||
P(B, C, D, E, A, R(79));
|
||||
|
||||
#undef K
|
||||
#undef F
|
||||
|
||||
ctx->state[0] += A;
|
||||
ctx->state[1] += B;
|
||||
ctx->state[2] += C;
|
||||
ctx->state[3] += D;
|
||||
ctx->state[4] += E;
|
||||
}
|
||||
|
||||
/*
|
||||
* SHA-1 process buffer
|
||||
*/
|
||||
void utils_sha1_update(iot_sha1_context *ctx, const unsigned char *input, size_t ilen)
|
||||
{
|
||||
size_t fill;
|
||||
uint32_t left;
|
||||
|
||||
if (ilen == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
left = ctx->total[0] & 0x3F;
|
||||
fill = 64 - left;
|
||||
|
||||
ctx->total[0] += (uint32_t) ilen;
|
||||
ctx->total[0] &= 0xFFFFFFFF;
|
||||
|
||||
if (ctx->total[0] < (uint32_t) ilen) {
|
||||
ctx->total[1]++;
|
||||
}
|
||||
|
||||
if (left && ilen >= fill) {
|
||||
memcpy((void *)(ctx->buffer + left), input, fill);
|
||||
utils_sha1_process(ctx, ctx->buffer);
|
||||
input += fill;
|
||||
ilen -= fill;
|
||||
left = 0;
|
||||
}
|
||||
|
||||
while (ilen >= 64) {
|
||||
utils_sha1_process(ctx, input);
|
||||
input += 64;
|
||||
ilen -= 64;
|
||||
}
|
||||
|
||||
if (ilen > 0) {
|
||||
memcpy((void *)(ctx->buffer + left), input, ilen);
|
||||
}
|
||||
}
|
||||
|
||||
static const unsigned char iot_sha1_padding[64] = {
|
||||
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
|
||||
/*
|
||||
* SHA-1 final digest
|
||||
*/
|
||||
void utils_sha1_finish(iot_sha1_context *ctx, unsigned char output[20])
|
||||
{
|
||||
uint32_t last, padn;
|
||||
uint32_t high, low;
|
||||
unsigned char msglen[8];
|
||||
|
||||
high = (ctx->total[0] >> 29)
|
||||
| (ctx->total[1] << 3);
|
||||
low = (ctx->total[0] << 3);
|
||||
|
||||
IOT_SHA1_PUT_UINT32_BE(high, msglen, 0);
|
||||
IOT_SHA1_PUT_UINT32_BE(low, msglen, 4);
|
||||
|
||||
last = ctx->total[0] & 0x3F;
|
||||
padn = (last < 56) ? (56 - last) : (120 - last);
|
||||
|
||||
utils_sha1_update(ctx, iot_sha1_padding, padn);
|
||||
utils_sha1_update(ctx, msglen, 8);
|
||||
|
||||
IOT_SHA1_PUT_UINT32_BE(ctx->state[0], output, 0);
|
||||
IOT_SHA1_PUT_UINT32_BE(ctx->state[1], output, 4);
|
||||
IOT_SHA1_PUT_UINT32_BE(ctx->state[2], output, 8);
|
||||
IOT_SHA1_PUT_UINT32_BE(ctx->state[3], output, 12);
|
||||
IOT_SHA1_PUT_UINT32_BE(ctx->state[4], output, 16);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* output = SHA-1( input buffer )
|
||||
*/
|
||||
void utils_sha1(const unsigned char *input, size_t ilen, unsigned char output[20])
|
||||
{
|
||||
iot_sha1_context ctx;
|
||||
|
||||
utils_sha1_init(&ctx);
|
||||
utils_sha1_starts(&ctx);
|
||||
utils_sha1_update(&ctx, input, ilen);
|
||||
utils_sha1_finish(&ctx, output);
|
||||
utils_sha1_free(&ctx);
|
||||
}
|
||||
|
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Copyright (c) 2017-2019 Tencent Group. All rights reserved.
|
||||
* License-Identifier: Apache-2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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 "qcloud.h"
|
||||
|
||||
char *LITE_format_string(const char *fmt, ...)
|
||||
{
|
||||
#define TEMP_STRING_MAXLEN (512)
|
||||
|
||||
va_list ap;
|
||||
char *tmp = NULL;
|
||||
char *dst;
|
||||
int rc = -1;
|
||||
|
||||
va_start(ap, fmt);
|
||||
tmp = osal_malloc(TEMP_STRING_MAXLEN);
|
||||
memset(tmp, 0, TEMP_STRING_MAXLEN);
|
||||
rc = osal_vsnprintf(tmp, TEMP_STRING_MAXLEN, fmt, ap);
|
||||
va_end(ap);
|
||||
LITE_ASSERT(tmp);
|
||||
LITE_ASSERT(rc < 1024);
|
||||
|
||||
dst = LITE_strdup(tmp);
|
||||
osal_free(tmp);
|
||||
|
||||
return dst;
|
||||
|
||||
#undef TEMP_STRING_MAXLEN
|
||||
}
|
||||
|
||||
char *LITE_format_nstring(const int len, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char *tmp = NULL;
|
||||
char *dst;
|
||||
int rc = -1;
|
||||
|
||||
va_start(ap, fmt);
|
||||
tmp = osal_malloc(len+2);
|
||||
memset(tmp, 0, len+2);
|
||||
rc = osal_vsnprintf(tmp, len+1, fmt, ap);
|
||||
va_end(ap);
|
||||
LITE_ASSERT(tmp);
|
||||
LITE_ASSERT(rc < 1024);
|
||||
|
||||
dst = osal_malloc(len + 1);
|
||||
osal_snprintf(dst, (len + 1), "%s", tmp);
|
||||
osal_free(tmp);
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
char *LITE_strdup(const char *src)
|
||||
{
|
||||
int len = 0;
|
||||
char *dst = NULL;
|
||||
|
||||
if (!src) {
|
||||
return NULL;
|
||||
}
|
||||
len = strlen(src) + 1;
|
||||
if (len > 1024) {
|
||||
QCLOUD_LOG_E("Too long string to duplicate, abort! len = %d", len);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dst = (char *)osal_malloc(sizeof(char) * len);
|
||||
if (!dst) {
|
||||
return NULL;
|
||||
}
|
||||
strncpy(dst, src, len);
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
void LITE_hexbuf_convert(unsigned char *digest, char *out, int in_len, int uppercase)
|
||||
{
|
||||
static char *zEncode[] = {"0123456789abcdef", "0123456789ABCDEF"};
|
||||
int j = 0;
|
||||
int i = 0;
|
||||
int idx = uppercase ? 1 : 0;
|
||||
|
||||
for (i = 0; i < in_len; i ++) {
|
||||
int a = digest[i];
|
||||
|
||||
out[j++] = zEncode[idx][(a >> 4) & 0xf];
|
||||
out[j++] = zEncode[idx][a & 0xf];
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t _hexval_of_char(char hex)
|
||||
{
|
||||
if (LITE_isdigit(hex)) {
|
||||
return (hex - '0');
|
||||
}
|
||||
if (hex >= 'a' && hex <= 'f') {
|
||||
return (hex - 'a' + 10);
|
||||
}
|
||||
if (hex >= 'A' && hex <= 'F') {
|
||||
return (hex - 'A' + 10);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void LITE_hexstr_convert(char *hexstr, uint8_t *out_buf, int in_len)
|
||||
{
|
||||
int i = 0;
|
||||
uint8_t ch0, ch1;
|
||||
|
||||
if (in_len % 2 != 0) {
|
||||
QCLOUD_LOG_E("hexstr length (%d) is not even", in_len);
|
||||
return;
|
||||
}
|
||||
|
||||
while (i < in_len) {
|
||||
ch0 = _hexval_of_char((char)hexstr[2 * i]);
|
||||
ch1 = _hexval_of_char((char)hexstr[2 * i + 1]);
|
||||
out_buf[i] = (ch0 << 4 | ch1);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
void LITE_replace_substr(char originalString[], char key[], char swap[])
|
||||
{
|
||||
int lengthOfOriginalString, lengthOfKey, lengthOfSwap, i, j, flag;
|
||||
char tmp[512];
|
||||
|
||||
lengthOfOriginalString = strlen(originalString);
|
||||
lengthOfKey = strlen(key);
|
||||
lengthOfSwap = strlen(swap);
|
||||
|
||||
for (i = 0; i <= lengthOfOriginalString - lengthOfKey; i++) {
|
||||
flag = 1;
|
||||
for (j = 0; j < lengthOfKey; j++) {
|
||||
if (originalString[i + j] != key[j]) {
|
||||
flag = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (flag) {
|
||||
strcpy(tmp, originalString);
|
||||
strcpy(&tmp[i], swap);
|
||||
strcpy(&tmp[i + lengthOfSwap], &originalString[i + lengthOfKey]);
|
||||
strcpy(originalString, tmp);
|
||||
i += lengthOfSwap - 1;
|
||||
lengthOfOriginalString = strlen(originalString);
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user