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
|
||||
|
Reference in New Issue
Block a user