feat: 移植腾讯云物联网开发平台 C SDK
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
file(GLOB src ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c)
|
||||
set(inc ${CMAKE_CURRENT_SOURCE_DIR}/inc/)
|
||||
|
||||
set(src_common ${src_common} ${src} PARENT_SCOPE)
|
||||
set(inc_common ${inc_common} ${inc} PARENT_SCOPE)
|
||||
|
||||
if( ${CONFIG_IOT_TEST} STREQUAL "ON")
|
||||
file(GLOB src_unit_test ${CMAKE_CURRENT_SOURCE_DIR}/test/*.cc)
|
||||
set(src_test ${src_test} ${src_unit_test} PARENT_SCOPE)
|
||||
endif()
|
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 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.
|
||||
*
|
||||
* @file utils_base64.h
|
||||
* @brief utils base64 header file
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-05-28
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-05-28 <td>1.0 <td>fancyxu <td>first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#ifndef IOT_HUB_DEVICE_C_SDK_COMMON_CRYPTOLOGY_INC_UTILS_BASE64_H_
|
||||
#define IOT_HUB_DEVICE_C_SDK_COMMON_CRYPTOLOGY_INC_UTILS_BASE64_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/**
|
||||
* @brief Encode a buffer into base64 format.
|
||||
*
|
||||
* @param[out] dst destination buffer
|
||||
* @param[in] dlen size of the destination buffer
|
||||
* @param[out] olen number of bytes written
|
||||
* @param[in] src source buffer
|
||||
* @param[in] slen amount of data to be encoded
|
||||
* @return 0 if successful, or MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL.
|
||||
* *olen is always updated to reflect the amount
|
||||
* of data that has (or would have) been written.
|
||||
* If that length cannot be represented, then no data is
|
||||
* written to the buffer and *olen is set to the maximum
|
||||
* length representable as a size_t.
|
||||
*
|
||||
* @note Call this function with dlen = 0 to obtain the
|
||||
* required buffer size in *olen
|
||||
*/
|
||||
int utils_base64encode(unsigned char *dst, size_t dlen, size_t *olen, const unsigned char *src, size_t slen);
|
||||
|
||||
/**
|
||||
* @brief Decode a base64-formatted buffer.
|
||||
*
|
||||
* @param[out] dst destination buffer (can be NULL for checking size)
|
||||
* @param[in] dlen size of the destination buffer
|
||||
* @param[out] olen number of bytes written
|
||||
* @param[in] src source buffer
|
||||
* @param[in] slen amount of data to be decoded
|
||||
* @return 0 if successful, MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL, or
|
||||
* MBEDTLS_ERR_BASE64_INVALID_CHARACTER if the input data is
|
||||
* not correct. *olen is always updated to reflect the amount
|
||||
* of data that has (or would have) been written.
|
||||
*
|
||||
* @note Call this function with *dst = NULL or dlen = 0 to obtain
|
||||
* the required buffer size in *olen
|
||||
*/
|
||||
int utils_base64decode(unsigned char *dst, size_t dlen, size_t *olen, const unsigned char *src, size_t slen);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // IOT_HUB_DEVICE_C_SDK_COMMON_CRYPTOLOGY_INC_UTILS_BASE64_H_
|
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 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.
|
||||
*
|
||||
* @file utils_hmac.h
|
||||
* @brief header file for utils_hmac
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-05-31
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-05-31 <td>1.0 <td>fancyxu <td>first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#ifndef IOT_HUB_DEVICE_C_SDK_COMMON_CRYPTOLOGY_INC_UTILS_HMAC_H_
|
||||
#define IOT_HUB_DEVICE_C_SDK_COMMON_CRYPTOLOGY_INC_UTILS_HMAC_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "utils_sha1.h"
|
||||
|
||||
/**
|
||||
* @brief Sha1 digest size.
|
||||
*
|
||||
*/
|
||||
#define SHA1_DIGEST_SIZE 20
|
||||
|
||||
/**
|
||||
* @brief Get digest of hmac-sha1.
|
||||
*
|
||||
* @param[in] msg message to hmac-sha1
|
||||
* @param[in] msg_len message len
|
||||
* @param[in] key key using in hmac-sha1
|
||||
* @param[in] key_len key len
|
||||
* @param[out] digest digest to calculate
|
||||
* @return 0 for success
|
||||
*/
|
||||
int utils_hmac_sha1(const char *msg, int msg_len, const uint8_t *key, int key_len, char *digest);
|
||||
|
||||
/**
|
||||
* @brief Get digest hex of hmac-sha1. Output hexstr format.
|
||||
*
|
||||
* @param[in] msg message to hmac-sha1
|
||||
* @param[in] msg_len message len
|
||||
* @param[in] key key using in hmac-sha1
|
||||
* @param[in] key_len key len
|
||||
* @param[out] digest digest to calculate
|
||||
* @return 0 for success
|
||||
*/
|
||||
int utils_hmac_sha1_hex(const char *msg, int msg_len, const uint8_t *key, int key_len, char *digest);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // IOT_HUB_DEVICE_C_SDK_COMMON_CRYPTOLOGY_INC_UTILS_HMAC_H_
|
@@ -0,0 +1,94 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 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.
|
||||
*
|
||||
* @file utils_md5.h
|
||||
* @brief
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-10-20
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-10-20 <td>1.0 <td>fancyxu <td>first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#ifndef IOT_HUB_DEVICE_C_SDK_COMMON_CRYPTOLOGY_INC_UTILS_MD5_H_
|
||||
#define IOT_HUB_DEVICE_C_SDK_COMMON_CRYPTOLOGY_INC_UTILS_MD5_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "qcloud_iot_config.h"
|
||||
|
||||
#ifdef AUTH_WITH_NO_TLS
|
||||
|
||||
typedef struct {
|
||||
uint32_t total[2]; /**< number of bytes processed */
|
||||
uint32_t state[4]; /**< intermediate digest state */
|
||||
uint8_t buffer[64]; /**< data block being processed */
|
||||
char md5sum[33]; /**< md5sum result in hex string */
|
||||
} IotMd5Context;
|
||||
|
||||
#else
|
||||
#include "mbedtls/md5.h"
|
||||
typedef struct {
|
||||
mbedtls_md5_context ctx;
|
||||
char md5sum[33]; /**< md5sum result in hex string */
|
||||
} IotMd5Context;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Reset MD5 context.
|
||||
*
|
||||
* @param[in,out] ctx MD5 context
|
||||
*/
|
||||
void utils_md5_reset(IotMd5Context *ctx);
|
||||
|
||||
/**
|
||||
* @brief MD5 update.
|
||||
*
|
||||
* @param[in,out] ctx MD5 context
|
||||
* @param[in] input input data
|
||||
* @param[in] ilen data length
|
||||
*/
|
||||
void utils_md5_update(IotMd5Context *ctx, const uint8_t *input, size_t ilen);
|
||||
|
||||
/**
|
||||
* @brief Finish MD5 calculation, result will store in md5sum.
|
||||
*
|
||||
* @param[in,out] ctx MD5 context
|
||||
*/
|
||||
void utils_md5_finish(IotMd5Context *ctx);
|
||||
|
||||
/**
|
||||
* @brief Compare md5sum with context.
|
||||
*
|
||||
* @param[in,out] ctx MD5 context
|
||||
* @param[in] md5sum md5sum to compare
|
||||
* @return 0 for the same
|
||||
*/
|
||||
int utils_md5_compare(IotMd5Context *ctx, const char md5sum[33]);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // IOT_HUB_DEVICE_C_SDK_COMMON_CRYPTOLOGY_INC_UTILS_MD5_H_
|
@@ -0,0 +1,119 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 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.
|
||||
*
|
||||
* @file utils_sha1.h
|
||||
* @brief header file for utils-sha1
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-05-31
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-05-31 <td>1.0 <td>fancyxu <td>first commit
|
||||
* <tr><td>2021-07-08 <td>1.1 <td>fancyxu <td>fix code standard of IotSha1Context
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#ifndef IOT_HUB_DEVICE_C_SDK_COMMON_CRYPTOLOGY_INC_UTILS_SHA1_H_
|
||||
#define IOT_HUB_DEVICE_C_SDK_COMMON_CRYPTOLOGY_INC_UTILS_SHA1_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* @brief SHA-1 context structure.
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t total[2]; /**< number of bytes processed */
|
||||
uint32_t state[5]; /**< intermediate digest state */
|
||||
unsigned char buffer[64]; /**< data block being processed */
|
||||
} IotSha1Context;
|
||||
|
||||
/**
|
||||
* @brief Initialize SHA-1 context.
|
||||
*
|
||||
* @param[in,out] ctx SHA-1 context to be initialized
|
||||
*/
|
||||
void utils_sha1_init(IotSha1Context *ctx);
|
||||
|
||||
/**
|
||||
* @brief Clear SHA-1 context.
|
||||
*
|
||||
* @param[in,out] ctx SHA-1 context to be cleared
|
||||
*/
|
||||
void utils_sha1_free(IotSha1Context *ctx);
|
||||
|
||||
/**
|
||||
* @brief Clone (the state of) a SHA-1 context.
|
||||
*
|
||||
* @param[out] dst The destination context
|
||||
* @param[in] src The context to be cloned
|
||||
*/
|
||||
void utils_sha1_clone(IotSha1Context *dst, const IotSha1Context *src);
|
||||
|
||||
/**
|
||||
* @brief SHA-1 context setup
|
||||
*
|
||||
* @param[in,out] ctx context to be initialized
|
||||
*/
|
||||
void utils_sha1_starts(IotSha1Context *ctx);
|
||||
|
||||
/**
|
||||
* @brief SHA-1 process buffer.
|
||||
*
|
||||
* @param[in,out] ctx SHA-1 context
|
||||
* @param[in] input buffer holding the data
|
||||
* @param[in] ilen length of the input data
|
||||
*/
|
||||
void utils_sha1_update(IotSha1Context *ctx, const unsigned char *input, size_t ilen);
|
||||
|
||||
/**
|
||||
* @brief SHA-1 final digest
|
||||
*
|
||||
* @param[in,out] ctx SHA-1 context
|
||||
* @param[out] output SHA-1 checksum result
|
||||
*/
|
||||
void utils_sha1_finish(IotSha1Context *ctx, unsigned char output[20]);
|
||||
|
||||
/**
|
||||
* @brief Output = SHA-1( input buffer )
|
||||
*
|
||||
* @param[in] input buffer holding the data
|
||||
* @param[in] ilen length of the input data
|
||||
* @param[out] output SHA-1 checksum result
|
||||
*/
|
||||
void utils_sha1(const unsigned char *input, size_t ilen, unsigned char output[20]);
|
||||
|
||||
/**
|
||||
* @brief Output = SHA-1( input buffer )
|
||||
*
|
||||
* @param[in] input buffer holding the data
|
||||
* @param[in] ilen length of the input data
|
||||
* @param[out] output SHA-1 checksum result hex
|
||||
*/
|
||||
void utils_sha1_hex(const unsigned char *input, size_t ilen, unsigned char output_hex[40]);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // IOT_HUB_DEVICE_C_SDK_COMMON_CRYPTOLOGY_INC_UTILS_SHA1_H_
|
@@ -0,0 +1,240 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 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.
|
||||
*
|
||||
* @file utils_base64.c
|
||||
* @brief base64 encdoe & decode. Reference mbedtls/base64.c.
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-05-27
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-05-27 <td>1.0 <td>fancyxu <td>first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include "utils_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 */
|
||||
|
||||
/**
|
||||
* @brief Check if src buf and dst buf.
|
||||
*
|
||||
* @param[in] dst destination buffer
|
||||
* @param[in] dlen size of the destination buffer
|
||||
* @param[in] src source buffer
|
||||
* @param[in] slen amount of data to be encoded
|
||||
* @return 0 for valid, -1 for invalid
|
||||
*/
|
||||
static int _utils_base64_check_valid(unsigned char *dst, size_t dlen, const unsigned char *src, size_t slen)
|
||||
{
|
||||
size_t i, n;
|
||||
uint32_t j, x;
|
||||
|
||||
/* 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 -1;
|
||||
|
||||
if (src[i] == '=' && ++j > 2)
|
||||
return -1;
|
||||
|
||||
if (src[i] > 127 || base64_dec_map[src[i]] == 127)
|
||||
return -1;
|
||||
|
||||
if (base64_dec_map[src[i]] < 64 && j != 0)
|
||||
return -1;
|
||||
|
||||
n++;
|
||||
}
|
||||
|
||||
// if no useful data, means error
|
||||
if (n == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* The following expression is to calculate the following formula without
|
||||
* risk of integer overflow in n:
|
||||
* n = ( ( n * 6 ) + 7 ) >> 3;
|
||||
*/
|
||||
n = (6 * (n >> 3)) + ((6 * (n & 0x7) + 7) >> 3);
|
||||
n -= j;
|
||||
|
||||
if (!dst || dlen < n) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Encode a buffer into base64 format.
|
||||
*
|
||||
* @param[out] dst destination buffer
|
||||
* @param[in] dlen size of the destination buffer
|
||||
* @param[out] olen number of bytes written
|
||||
* @param[in] src source buffer
|
||||
* @param[in] slen amount of data to be encoded
|
||||
* @return 0 if successful, or MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL.
|
||||
* *olen is always updated to reflect the amount
|
||||
* of data that has (or would have) been written.
|
||||
* If that length cannot be represented, then no data is
|
||||
* written to the buffer and *olen is set to the maximum
|
||||
* length representable as a size_t.
|
||||
*
|
||||
* @note Call this function with dlen = 0 to obtain the
|
||||
* required buffer size in *olen
|
||||
*/
|
||||
int utils_base64encode(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 -1;
|
||||
}
|
||||
|
||||
n *= 4;
|
||||
|
||||
if ((dlen < n + 1) || (NULL == dst)) {
|
||||
*olen = n + 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Decode a base64-formatted buffer.
|
||||
*
|
||||
* @param[out] dst destination buffer (can be NULL for checking size)
|
||||
* @param[in] dlen size of the destination buffer
|
||||
* @param[out] olen number of bytes written
|
||||
* @param[in] src source buffer
|
||||
* @param[in] slen amount of data to be decoded
|
||||
* @return 0 if successful, MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL, or
|
||||
* MBEDTLS_ERR_BASE64_INVALID_CHARACTER if the input data is
|
||||
* not correct. *olen is always updated to reflect the amount
|
||||
* of data that has (or would have) been written.
|
||||
*
|
||||
* @note Call this function with *dst = NULL or dlen = 0 to obtain
|
||||
* the required buffer size in *olen
|
||||
*/
|
||||
int utils_base64decode(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 */
|
||||
if (_utils_base64_check_valid(dst, dlen, src, slen)) {
|
||||
*olen = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = slen, 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;
|
||||
}
|
@@ -0,0 +1,144 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 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.
|
||||
*
|
||||
* @file utils_hmac.c
|
||||
* @brief Support hmac
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-05-31
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-05-31 <td>1.0 <td>fancyxu <td>first commit
|
||||
* <tr><td>2021-07-08 <td>1.1 <td>fancyxu <td>fix code standard of IotSha1Context
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include "utils_hmac.h"
|
||||
|
||||
/**
|
||||
* @brief inner/outer padding size
|
||||
*
|
||||
*/
|
||||
#define KEY_IO_PAD_SIZE 64
|
||||
|
||||
/**
|
||||
* @brief sha1 digest size
|
||||
*
|
||||
*/
|
||||
#define SHA1_DIGEST_SIZE 20
|
||||
|
||||
/**
|
||||
* @brief Binary half byte to hex char
|
||||
*
|
||||
* @param[in] hb half byte
|
||||
* @return hex char(0~f)
|
||||
*/
|
||||
static char _hb2hex(uint8_t hb)
|
||||
{
|
||||
hb = hb & 0xF;
|
||||
return (char)(hb < 10 ? '0' + hb : hb - 10 + 'a');
|
||||
}
|
||||
|
||||
static int _utils_hmac_sha1_process(const char *msg, int msg_len, const uint8_t *key, int key_len, unsigned char *out)
|
||||
{
|
||||
IotSha1Context context;
|
||||
unsigned char k_ipad[KEY_IO_PAD_SIZE]; /* inner padding - key XORd with ipad */
|
||||
unsigned char k_opad[KEY_IO_PAD_SIZE]; /* outer padding - key XORd with opad */
|
||||
|
||||
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_IO_PAD_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_IO_PAD_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_IO_PAD_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 */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get digest of hmac-sha1.
|
||||
*
|
||||
* @param[in] msg message to hmac-sha1
|
||||
* @param[in] msg_len message len
|
||||
* @param[in] key key using in hmac-sha1
|
||||
* @param[in] key_len key len
|
||||
* @param[out] digest digest to calculate
|
||||
* @return 0 for success
|
||||
*/
|
||||
int utils_hmac_sha1(const char *msg, int msg_len, const uint8_t *key, int key_len, char *digest)
|
||||
{
|
||||
unsigned char out[SHA1_DIGEST_SIZE] = {0};
|
||||
if (!msg || !digest || !key) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (key_len > KEY_IO_PAD_SIZE) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
_utils_hmac_sha1_process(msg, msg_len, key, key_len, out);
|
||||
memcpy(digest, out, SHA1_DIGEST_SIZE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get digest hex of hmac-sha1.
|
||||
*
|
||||
* @param[in] msg message to hmac-sha1
|
||||
* @param[in] msg_len message len
|
||||
* @param[in] key key using in hmac-sha1
|
||||
* @param[in] key_len key len
|
||||
* @param[out] digest digest to calculate
|
||||
* @return 0 for success
|
||||
*/
|
||||
int utils_hmac_sha1_hex(const char *msg, int msg_len, const uint8_t *key, int key_len, char *digest)
|
||||
{
|
||||
unsigned char out[SHA1_DIGEST_SIZE] = {0};
|
||||
if (!msg || !digest || !key) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (key_len > KEY_IO_PAD_SIZE) {
|
||||
return -1;
|
||||
}
|
||||
_utils_hmac_sha1_process(msg, msg_len, key, key_len, out);
|
||||
for (int i = 0; i < SHA1_DIGEST_SIZE; ++i) {
|
||||
digest[i * 2] = _hb2hex(out[i] >> 4);
|
||||
digest[i * 2 + 1] = _hb2hex(out[i]);
|
||||
}
|
||||
return 0;
|
||||
}
|
@@ -0,0 +1,409 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 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.
|
||||
*
|
||||
* @file utils_md5.c
|
||||
* @brief
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-10-27
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-10-27 <td>1.0 <td>fancyxu <td>first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include "utils_md5.h"
|
||||
|
||||
/**
|
||||
* @brief Binary half byte to hex char
|
||||
*
|
||||
* @param[in] hb half byte
|
||||
* @return hex char(0~f)
|
||||
*/
|
||||
static char _hb2hex(uint8_t hb)
|
||||
{
|
||||
hb = hb & 0xF;
|
||||
return (char)(hb < 10 ? '0' + hb : hb - 10 + 'a');
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Lower md5sum.
|
||||
*
|
||||
* @param[out] md5_lower md5 sum lower
|
||||
* @param[in] md5 md5 sum
|
||||
*/
|
||||
static void _lower(char md5_lower[33], const char md5[33])
|
||||
{
|
||||
int i = 0;
|
||||
for (i = 0; i < 32; i++) {
|
||||
md5_lower[i] = md5[i];
|
||||
if (md5[i] >= 'A' && md5[i] <= 'F') {
|
||||
md5_lower[i] += ('a' - 'A');
|
||||
}
|
||||
}
|
||||
md5_lower[32] = '\0';
|
||||
}
|
||||
|
||||
#ifdef AUTH_WITH_NO_TLS
|
||||
/**
|
||||
* @brief 32-bit integer manipulation macros (little endian)
|
||||
*
|
||||
*/
|
||||
#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); \
|
||||
}
|
||||
|
||||
#define IOT_MD5_PUT_UINT32_LE(n, b, i) \
|
||||
{ \
|
||||
(b)[(i)] = (uint8_t)(((n)) & 0xFF); \
|
||||
(b)[(i) + 1] = (uint8_t)(((n) >> 8) & 0xFF); \
|
||||
(b)[(i) + 2] = (uint8_t)(((n) >> 16) & 0xFF); \
|
||||
(b)[(i) + 3] = (uint8_t)(((n) >> 24) & 0xFF); \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Padding for md5.
|
||||
*
|
||||
*/
|
||||
static const uint8_t sg_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,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Initialize md5 context.
|
||||
*
|
||||
* @param[in,out] ctx MD5 context
|
||||
*/
|
||||
static void _utils_md5_init(IotMd5Context *ctx)
|
||||
{
|
||||
memset(ctx, 0, sizeof(IotMd5Context));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Start md5 calculate.
|
||||
*
|
||||
* @param[in,out] ctx MD5 context
|
||||
*/
|
||||
static void _utils_md5_start(IotMd5Context *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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Calculate md5.
|
||||
*
|
||||
* @param[in,out] ctx MD5 context
|
||||
* @param[in] data data to calculate
|
||||
*/
|
||||
static void _utils_md5_process(IotMd5Context *ctx, const uint8_t 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reset MD5 context.
|
||||
*
|
||||
* @param[in,out] ctx MD5 context
|
||||
*/
|
||||
void utils_md5_reset(IotMd5Context *ctx)
|
||||
{
|
||||
memset(ctx, 0, sizeof(IotMd5Context));
|
||||
_utils_md5_init(ctx);
|
||||
_utils_md5_start(ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief MD5 update.
|
||||
*
|
||||
* @param[in,out] ctx MD5 context
|
||||
* @param[in] input input data
|
||||
* @param[in] ilen data length
|
||||
*/
|
||||
void utils_md5_update(IotMd5Context *ctx, const uint8_t *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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Finish MD5 calculation, result will store in md5sum.
|
||||
*
|
||||
* @param[in,out] ctx MD5 context
|
||||
*/
|
||||
void utils_md5_finish(IotMd5Context *ctx)
|
||||
{
|
||||
int i;
|
||||
uint32_t last, padn;
|
||||
uint32_t high, low;
|
||||
uint8_t msglen[8];
|
||||
|
||||
uint8_t output_tmp[16];
|
||||
|
||||
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, sg_iot_md5_padding, padn);
|
||||
utils_md5_update(ctx, msglen, 8);
|
||||
|
||||
IOT_MD5_PUT_UINT32_LE(ctx->state[0], output_tmp, 0);
|
||||
IOT_MD5_PUT_UINT32_LE(ctx->state[1], output_tmp, 4);
|
||||
IOT_MD5_PUT_UINT32_LE(ctx->state[2], output_tmp, 8);
|
||||
IOT_MD5_PUT_UINT32_LE(ctx->state[3], output_tmp, 12);
|
||||
|
||||
for (i = 0; i < 16; ++i) {
|
||||
ctx->md5sum[i * 2] = _hb2hex(output_tmp[i] >> 4);
|
||||
ctx->md5sum[i * 2 + 1] = _hb2hex(output_tmp[i]);
|
||||
}
|
||||
ctx->md5sum[32] = '\0';
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compare md5sum with context.
|
||||
*
|
||||
* @param[in,out] ctx MD5 context
|
||||
* @param[in] md5sum md5sum to compare
|
||||
* @return 0 for the same
|
||||
*/
|
||||
int utils_md5_compare(IotMd5Context *ctx, const char md5sum[33])
|
||||
{
|
||||
char md5sum_lower[33];
|
||||
_lower(md5sum_lower, md5sum);
|
||||
return strncmp(ctx->md5sum, md5sum_lower, 32);
|
||||
}
|
||||
#else
|
||||
|
||||
/**
|
||||
* @brief Reset MD5 context.
|
||||
*
|
||||
* @param[in,out] ctx MD5 context
|
||||
*/
|
||||
void utils_md5_reset(IotMd5Context *ctx)
|
||||
{
|
||||
memset(ctx, 0, sizeof(IotMd5Context));
|
||||
mbedtls_md5_init(&ctx->ctx);
|
||||
mbedtls_md5_starts(&ctx->ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief MD5 update.
|
||||
*
|
||||
* @param[in,out] ctx MD5 context
|
||||
* @param[in] input input data
|
||||
* @param[in] ilen data length
|
||||
*/
|
||||
void utils_md5_update(IotMd5Context *ctx, const uint8_t *input, size_t ilen)
|
||||
{
|
||||
mbedtls_md5_update(&ctx->ctx, input, ilen);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Finish MD5 calculation, result will store in md5sum.
|
||||
*
|
||||
* @param[in,out] ctx MD5 context
|
||||
*/
|
||||
void utils_md5_finish(IotMd5Context *ctx)
|
||||
{
|
||||
int i;
|
||||
uint8_t output_tmp[16];
|
||||
mbedtls_md5_finish(&ctx->ctx, output_tmp);
|
||||
for (i = 0; i < 16; ++i) {
|
||||
ctx->md5sum[i * 2] = _hb2hex(output_tmp[i] >> 4);
|
||||
ctx->md5sum[i * 2 + 1] = _hb2hex(output_tmp[i]);
|
||||
}
|
||||
ctx->md5sum[32] = '\0';
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compare md5sum with context.
|
||||
*
|
||||
* @param[in,out] ctx MD5 context
|
||||
* @param[in] md5sum md5sum to compare
|
||||
* @return 0 for the same
|
||||
*/
|
||||
int utils_md5_compare(IotMd5Context *ctx, const char md5sum[33])
|
||||
{
|
||||
char md5sum_lower[33];
|
||||
_lower(md5sum_lower, md5sum);
|
||||
return strncmp(ctx->md5sum, md5sum_lower, 32);
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,408 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 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.
|
||||
*
|
||||
* @file utils_sha1.c
|
||||
* @brief SHA-1 operation, reference mbedtls
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-05-31
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-05-31 <td>1.0 <td>fancyxu <td>first commit
|
||||
* <tr><td>2021-07-08 <td>1.1 <td>fancyxu <td>fix code standard of IotSha1Context
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include "utils_sha1.h"
|
||||
|
||||
#ifndef IOT_SHA1_GET_UINT32_BE
|
||||
/**
|
||||
* @brief Get 32-bit integer manipulation macros (big endian)
|
||||
*
|
||||
*/
|
||||
#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
|
||||
/**
|
||||
* @brief Put 32-bit integer manipulation macros (big endian)
|
||||
*
|
||||
*/
|
||||
#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
|
||||
|
||||
/**
|
||||
* @brief Binary half byte to hex char
|
||||
*
|
||||
* @param[in] hb half byte
|
||||
* @return hex char(0~f)
|
||||
*/
|
||||
static char _hb2hex(uint8_t hb)
|
||||
{
|
||||
hb = hb & 0xF;
|
||||
return (char)(hb < 10 ? '0' + hb : hb - 10 + 'a');
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Implementation that should never be optimized out by the compiler.
|
||||
*
|
||||
* @param[in] v pointer to ctx
|
||||
* @param[in] n sizeof ctx
|
||||
*/
|
||||
static void utils_sha1_zeroize(void *v, size_t n)
|
||||
{
|
||||
volatile unsigned char *p = v;
|
||||
while (n--) {
|
||||
*p++ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize SHA-1 context.
|
||||
*
|
||||
* @param[in,out] ctx SHA-1 context to be initialized
|
||||
*/
|
||||
void utils_sha1_init(IotSha1Context *ctx)
|
||||
{
|
||||
memset(ctx, 0, sizeof(IotSha1Context));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clear SHA-1 context.
|
||||
*
|
||||
* @param[in,out] ctx SHA-1 context to be cleared
|
||||
*/
|
||||
void utils_sha1_free(IotSha1Context *ctx)
|
||||
{
|
||||
if (!ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
utils_sha1_zeroize(ctx, sizeof(IotSha1Context));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clone (the state of) a SHA-1 context.
|
||||
*
|
||||
* @param[out] dst The destination context
|
||||
* @param[in] src The context to be cloned
|
||||
*/
|
||||
void utils_sha1_clone(IotSha1Context *dst, const IotSha1Context *src)
|
||||
{
|
||||
*dst = *src;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief SHA-1 context setup
|
||||
*
|
||||
* @param[in,out] ctx context to be initialized
|
||||
*/
|
||||
void utils_sha1_starts(IotSha1Context *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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief SHA-1 process
|
||||
*
|
||||
* @param[in,out] ctx pointer to ctx
|
||||
* @param[in] data data to be calculated
|
||||
*/
|
||||
void utils_sha1_process(IotSha1Context *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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief SHA-1 process buffer.
|
||||
*
|
||||
* @param[in,out] ctx SHA-1 context
|
||||
* @param[in] input buffer holding the data
|
||||
* @param[in] ilen length of the input data
|
||||
*/
|
||||
void utils_sha1_update(IotSha1Context *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};
|
||||
|
||||
/**
|
||||
* @brief SHA-1 final digest
|
||||
*
|
||||
* @param[in,out] ctx SHA-1 context
|
||||
* @param[out] output SHA-1 checksum result
|
||||
*/
|
||||
void utils_sha1_finish(IotSha1Context *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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Output = SHA-1( input buffer )
|
||||
*
|
||||
* @param[in] input buffer holding the data
|
||||
* @param[in] ilen length of the input data
|
||||
* @param[out] output SHA-1 checksum result
|
||||
*/
|
||||
void utils_sha1(const unsigned char *input, size_t ilen, unsigned char output[20])
|
||||
{
|
||||
IotSha1Context ctx;
|
||||
|
||||
utils_sha1_init(&ctx);
|
||||
utils_sha1_starts(&ctx);
|
||||
utils_sha1_update(&ctx, input, ilen);
|
||||
utils_sha1_finish(&ctx, output);
|
||||
utils_sha1_free(&ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Output = SHA-1( input buffer )
|
||||
*
|
||||
* @param[in] input buffer holding the data
|
||||
* @param[in] ilen length of the input data
|
||||
* @param[out] output SHA-1 checksum result hex
|
||||
*/
|
||||
void utils_sha1_hex(const unsigned char *input, size_t ilen, unsigned char output_hex[40])
|
||||
{
|
||||
unsigned char out[20 + 1] = {0};
|
||||
utils_sha1(input, ilen, out);
|
||||
for (int i = 0; i < 20; ++i) {
|
||||
output_hex[i * 2] = _hb2hex(out[i] >> 4);
|
||||
output_hex[i * 2 + 1] = _hb2hex(out[i]);
|
||||
}
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
Language: Cpp
|
||||
BasedOnStyle: Google
|
||||
ColumnLimit: 120
|
||||
DerivePointerAlignment: true
|
||||
PointerAlignment: Left
|
||||
SortIncludes: true
|
||||
IncludeBlocks: Preserve
|
||||
IndentPPDirectives: AfterHash
|
@@ -0,0 +1,179 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 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.
|
||||
*
|
||||
* @file test_cryptology.cc
|
||||
* @brief test cryptology
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-07-07
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-07-07 <td>1.0 <td>fancyxu <td>first commit
|
||||
* <tr><td>2021-07-08 <td>1.1 <td>fancyxu <td>fix code standard of IotSha1Context
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "utils_base64.h"
|
||||
#include "utils_hmac.h"
|
||||
#include "utils_md5.h"
|
||||
#include "utils_sha1.h"
|
||||
|
||||
namespace cryptology_unittest {
|
||||
|
||||
/**
|
||||
* @brief Test base64.
|
||||
*
|
||||
*/
|
||||
TEST(CryptologyTest, base64) {
|
||||
const uint8_t base64_test_dec[64] = {0x24, 0x48, 0x6E, 0x56, 0x87, 0x62, 0x5A, 0xBD, 0xBF, 0x17, 0xD9, 0xA2, 0xC4,
|
||||
0x17, 0x1A, 0x01, 0x94, 0xED, 0x8F, 0x1E, 0x11, 0xB3, 0xD7, 0x09, 0x0C, 0xB6,
|
||||
0xE9, 0x10, 0x6F, 0x22, 0xEE, 0x13, 0xCA, 0xB3, 0x07, 0x05, 0x76, 0xC9, 0xFA,
|
||||
0x31, 0x6C, 0x08, 0x34, 0xFF, 0x8D, 0xC2, 0x6C, 0x38, 0x00, 0x43, 0xE9, 0x54,
|
||||
0x97, 0xAF, 0x50, 0x4B, 0xD1, 0x41, 0xBA, 0x95, 0x31, 0x5A, 0x0B, 0x97};
|
||||
|
||||
const uint8_t base64_test_enc[] =
|
||||
"JEhuVodiWr2/F9mixBcaAZTtjx4Rs9cJDLbpEG8i7hPK"
|
||||
"swcFdsn6MWwINP+Nwmw4AEPpVJevUEvRQbqVMVoLlw==";
|
||||
|
||||
size_t len;
|
||||
uint8_t buffer[128];
|
||||
|
||||
ASSERT_EQ(utils_base64encode(buffer, sizeof(buffer), &len, base64_test_dec, sizeof(base64_test_dec)), 0);
|
||||
ASSERT_EQ(memcmp(base64_test_enc, buffer, len), 0);
|
||||
ASSERT_EQ(utils_base64decode(buffer, sizeof(buffer), &len, base64_test_enc, sizeof(base64_test_enc) - 1), 0);
|
||||
ASSERT_EQ(memcmp(base64_test_dec, buffer, len), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Test hmac.
|
||||
*
|
||||
*/
|
||||
TEST(CryptologyTest, hmac) {
|
||||
/**
|
||||
* @brief HMAC-SHA1
|
||||
*
|
||||
*/
|
||||
const char test_buf[] = "Here is a test for hmac-sha1!";
|
||||
const uint8_t key[] = "0123456789123456";
|
||||
const char result[] = "614b650ffefff7862c1bc7fdd9de4e472a8184c4";
|
||||
|
||||
char buf[128] = {0};
|
||||
|
||||
ASSERT_EQ(utils_hmac_sha1_hex(test_buf, strlen(test_buf), key, sizeof(key), buf), 0);
|
||||
ASSERT_EQ(memcmp(buf, result, sizeof(result)), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Test sha1.
|
||||
*
|
||||
*/
|
||||
TEST(CryptologyTest, sha1) {
|
||||
/*
|
||||
* FIPS-180-1 test vectors
|
||||
*/
|
||||
const uint8_t sha1_test_buf[3][57] = {{"abc"}, {"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, {""}};
|
||||
|
||||
const int sha1_test_buflen[3] = {3, 56, 1000};
|
||||
|
||||
const uint8_t sha1_test_sum[3][20] = {
|
||||
{
|
||||
0xA9, 0x99, 0x3E, 0x36, 0x47, 0x06, 0x81, 0x6A, 0xBA, 0x3E,
|
||||
0x25, 0x71, 0x78, 0x50, 0xC2, 0x6C, 0x9C, 0xD0, 0xD8, 0x9D,
|
||||
},
|
||||
{
|
||||
0x84, 0x98, 0x3E, 0x44, 0x1C, 0x3B, 0xD2, 0x6E, 0xBA, 0xAE,
|
||||
0x4A, 0xA1, 0xF9, 0x51, 0x29, 0xE5, 0xE5, 0x46, 0x70, 0xF1,
|
||||
},
|
||||
{
|
||||
0x34, 0xAA, 0x97, 0x3C, 0xD4, 0xC4, 0xDA, 0xA4, 0xF6, 0x1E,
|
||||
0xEB, 0x2B, 0xDB, 0xAD, 0x27, 0x31, 0x65, 0x34, 0x01, 0x6F,
|
||||
},
|
||||
};
|
||||
|
||||
int i, j, buflen, rc = 0;
|
||||
uint8_t buf[1024];
|
||||
uint8_t sha1sum[20];
|
||||
|
||||
IotSha1Context ctx;
|
||||
|
||||
utils_sha1_init(&ctx);
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
utils_sha1_starts(&ctx);
|
||||
|
||||
if (i == 2) {
|
||||
memset(buf, 'a', buflen = 1000);
|
||||
for (j = 0; j < 1000; j++) {
|
||||
utils_sha1_update(&ctx, buf, buflen);
|
||||
}
|
||||
} else {
|
||||
utils_sha1_update(&ctx, sha1_test_buf[i], sha1_test_buflen[i]);
|
||||
}
|
||||
|
||||
utils_sha1_finish(&ctx, sha1sum);
|
||||
ASSERT_EQ(memcmp(sha1sum, sha1_test_sum[i], 20), 0);
|
||||
}
|
||||
|
||||
utils_sha1_free(&ctx);
|
||||
|
||||
utils_sha1(sha1_test_buf[0], 3, sha1sum);
|
||||
ASSERT_EQ(memcmp(sha1sum, sha1_test_sum[0], 20), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Test md5.
|
||||
*
|
||||
*/
|
||||
TEST(CryptologyTest, md5) {
|
||||
/*
|
||||
* RFC 1321 test vectors
|
||||
*/
|
||||
const uint8_t md5_test_buf[7][81] = {
|
||||
{""},
|
||||
{"a"},
|
||||
{"abc"},
|
||||
{"message digest"},
|
||||
{"abcdefghijklmnopqrstuvwxyz"},
|
||||
{"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"},
|
||||
{"12345678901234567890123456789012345678901234567890123456789012345678901234567890"},
|
||||
};
|
||||
|
||||
const size_t md5_test_buflen[7] = {0, 1, 3, 14, 26, 62, 80};
|
||||
|
||||
const char md5_test_sum[7][33] = {
|
||||
{"D41D8CD98F00B204E9800998ECF8427E"}, {"0CC175B9C0F1B6A831C399E269772661"}, {"900150983CD24FB0D6963F7D28E17F72"},
|
||||
{"F96B697D7CB7938D525A2F31AAF161D0"}, {"C3FCD3D76192E4007DFB496CCA67E13B"}, {"D174AB98D277D9F5A5611C2C9F419D9F"},
|
||||
{"57EDF4A22BE3C955AC49DA2E2107B67A"},
|
||||
};
|
||||
|
||||
/*
|
||||
* Checkup routine
|
||||
*/
|
||||
IotMd5Context md5_ctx;
|
||||
for (int i = 0; i < 7; i++) {
|
||||
utils_md5_reset(&md5_ctx);
|
||||
utils_md5_update(&md5_ctx, md5_test_buf[i], md5_test_buflen[i]);
|
||||
utils_md5_finish(&md5_ctx);
|
||||
ASSERT_EQ(utils_md5_compare(&md5_ctx, md5_test_sum[i]), 0);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace cryptology_unittest
|
@@ -0,0 +1,10 @@
|
||||
file(GLOB src ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c)
|
||||
set(inc ${CMAKE_CURRENT_SOURCE_DIR}/inc/)
|
||||
|
||||
set(src_common ${src_common} ${src} PARENT_SCOPE)
|
||||
set(inc_common ${inc_common} ${inc} PARENT_SCOPE)
|
||||
|
||||
if( ${CONFIG_IOT_TEST} STREQUAL "ON")
|
||||
file(GLOB src_unit_test ${CMAKE_CURRENT_SOURCE_DIR}/test/*.cc)
|
||||
set(src_test ${src_test} ${src_unit_test} PARENT_SCOPE)
|
||||
endif()
|
@@ -0,0 +1,335 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 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.
|
||||
*
|
||||
* @file mqtt_packet.h
|
||||
* @brief header file for mqtt packet
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-05-24
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-05-24 <td>1.0 <td>fancyxu <td>first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#ifndef IOT_HUB_DEVICE_C_SDK_COMMON_MQTT_PACKET_INC_MQTT_PACKET_H_
|
||||
#define IOT_HUB_DEVICE_C_SDK_COMMON_MQTT_PACKET_INC_MQTT_PACKET_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* @brief header 1 byte + remaining length 1~4 byte(s).
|
||||
*
|
||||
*/
|
||||
#define MAX_NO_OF_REMAINING_LENGTH_BYTES 4
|
||||
#define MAX_MQTT_FIXED_HEADER_LEN (1 + MAX_NO_OF_REMAINING_LENGTH_BYTES)
|
||||
#define MIN_MQTT_FIXED_HEADER_LEN (1 + 1)
|
||||
|
||||
/**
|
||||
* @brief Check if short buffer.
|
||||
*
|
||||
*/
|
||||
#define SHORT_BUFFER_CHECK(buf_len, len) \
|
||||
if (buf_len < len) { \
|
||||
return MQTT_ERR_SHORT_BUFFER; \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Error code for mqtt packet。
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
MQTT_RET_PACKET_OK = 0,
|
||||
MQTT_ERR_INVALID_PACKET_TYPE = -1,
|
||||
MQTT_ERR_SHORT_BUFFER = -2,
|
||||
MQTT_ERR_VERSION_UNSUPPORTED = -3,
|
||||
MQTT_ERR_SUB_COUNT_EXCEED = -4,
|
||||
} MQTTPacketErrCode;
|
||||
|
||||
/**
|
||||
* @brief MQTT packet type.
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
CONNECT = 1,
|
||||
CONNACK,
|
||||
PUBLISH,
|
||||
PUBACK,
|
||||
PUBREC,
|
||||
PUBREL,
|
||||
PUBCOMP,
|
||||
SUBSCRIBE,
|
||||
SUBACK,
|
||||
UNSUBSCRIBE,
|
||||
UNSUBACK,
|
||||
PINGREQ,
|
||||
PINGRESP,
|
||||
DISCONNECT
|
||||
} MQTTPacketType;
|
||||
|
||||
/**
|
||||
* @brief Defines the MQTT "Last Will and Testament" (LWT) settings for the connect packet.
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
const char* topic_name; /**< The LWT topic to which the LWT message will be published */
|
||||
const char* message; /**< The LWT payload */
|
||||
uint8_t retained; /**< The retained flag for the LWT message */
|
||||
uint8_t qos; /**< The quality of service setting for the LWT message */
|
||||
} MQTTPacketWillOptions;
|
||||
|
||||
/**
|
||||
* @brief MQTT packet connect option.
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t mqtt_version; /**< Version of MQTT to be used. 3 = 3.1 4 = 3.1.1 */
|
||||
const char* client_id;
|
||||
uint16_t keep_alive_interval;
|
||||
uint8_t clean_session;
|
||||
uint8_t will_flag;
|
||||
MQTTPacketWillOptions will;
|
||||
char* username;
|
||||
char* password;
|
||||
} MQTTPacketConnectOption;
|
||||
|
||||
/**
|
||||
* @brief Bitfields for the MQTT header byte.
|
||||
*
|
||||
*/
|
||||
typedef union {
|
||||
uint8_t byte; /**< the whole byte */
|
||||
struct {
|
||||
uint8_t retain : 1; /**< retained flag bit */
|
||||
uint8_t qos : 2; /**< QoS value, 0, 1 or 2 */
|
||||
uint8_t dup : 1; /**< DUP flag bit */
|
||||
uint8_t type : 4; /**< message type nibble */
|
||||
} bits;
|
||||
} MQTTHeader;
|
||||
|
||||
/**
|
||||
* @brief Connect flags byte.
|
||||
*
|
||||
*/
|
||||
typedef union {
|
||||
uint8_t all; /**< all connect flags */
|
||||
|
||||
struct {
|
||||
uint8_t : 1; /**< unused */
|
||||
uint8_t clean_session : 1; /**< cleansession flag */
|
||||
uint8_t will : 1; /**< will flag */
|
||||
uint8_t will_qos : 2; /**< will QoS value */
|
||||
uint8_t will_retain : 1; /**< will retain setting */
|
||||
uint8_t password : 1; /**< 3.1 password */
|
||||
uint8_t username : 1; /**< 3.1 user name */
|
||||
} bits;
|
||||
} MQTTConnectFlags;
|
||||
|
||||
/**
|
||||
* @brief connack flags byte.
|
||||
*
|
||||
*/
|
||||
typedef union {
|
||||
unsigned char all; /**< all connack flags */
|
||||
struct {
|
||||
unsigned int sessionpresent : 1; /**< session present flag */
|
||||
unsigned int reserved : 7; /**< unused */
|
||||
} bits;
|
||||
} MQTTConnackFlags;
|
||||
|
||||
/**
|
||||
* @brief Flags for publish.
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t dup;
|
||||
uint8_t qos;
|
||||
uint8_t retain;
|
||||
} MQTTPublishFlags;
|
||||
|
||||
/**
|
||||
* @brief Connect return code.
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
CONNACK_CONNECTION_ACCEPTED = 0, /**< connection accepted */
|
||||
CONNACK_UNACCEPTABLE_PROTOCOL_VERSION_ERROR = 1, /**< connection refused: unaccepted protocol version */
|
||||
CONNACK_IDENTIFIER_REJECTED_ERROR = 2, /**< connection refused: identifier rejected */
|
||||
CONNACK_SERVER_UNAVAILABLE_ERROR = 3, /**< connection refused: server unavailable */
|
||||
CONNACK_BAD_USERDATA_ERROR = 4, /**< connection refused: bad user name or password */
|
||||
CONNACK_NOT_AUTHORIZED_ERROR = 5 /**< connection refused: not authorized */
|
||||
} MQTTConnackReturnCodes;
|
||||
|
||||
/**
|
||||
* @brief Serialize the connect options into the buffer. See 3.1.
|
||||
*
|
||||
* @param[out] buf the buffer into which the packet will be serialized
|
||||
* @param[in] buf_len the length in bytes of the supplied buffer
|
||||
* @param[in] options the options to be used to build the connect packet
|
||||
* @return serialized length, or error if <= 0
|
||||
*/
|
||||
int mqtt_connect_packet_serialize(uint8_t* buf, int buf_len, const MQTTPacketConnectOption* options);
|
||||
|
||||
/**
|
||||
* @brief Serialize a disconnect packet into the supplied buffer, ready for writing to a socket. See 3.14.
|
||||
*
|
||||
* @param[out] buf the buffer into which the packet will be serialized
|
||||
* @param[in] buf_len the length in bytes of the supplied buffer, to avoid overruns
|
||||
* @return serialized length, or error if <= 0
|
||||
*/
|
||||
int mqtt_disconnect_packet_serialize(uint8_t* buf, int buf_len);
|
||||
|
||||
/**
|
||||
* @brief Serialize a disconnect packet into the supplied buffer, ready for writing to a socket. See 3.12.
|
||||
*
|
||||
* @param[out] buf the buffer into which the packet will be serialized
|
||||
* @param[in] buf_len the length in bytes of the supplied buffer, to avoid overruns
|
||||
* @return serialized length, or error if <= 0
|
||||
*/
|
||||
int mqtt_pingreq_packet_serialize(uint8_t* buf, int buf_len);
|
||||
|
||||
/**
|
||||
* @brief Serialize the supplied publish data into the supplied buffer, ready for sending. See 3.3.
|
||||
*
|
||||
* @param[out] buf the buffer into which the packet will be serialized
|
||||
* @param[in] buf_len the length in bytes of the supplied buffer
|
||||
* @param[in] flags the MQTT dup, qos, retained flag
|
||||
* @param[in] packet_id integer - the MQTT packet identifier
|
||||
* @param[in] topic_name char * - the MQTT topic in the publish
|
||||
* @param[in] payload byte buffer - the MQTT publish payload
|
||||
* @param[in] payload_len integer - the length of the MQTT payload
|
||||
* @return serialized length, or error if <= 0
|
||||
*/
|
||||
int mqtt_publish_packet_serialize(uint8_t* buf, int buf_len, const MQTTPublishFlags* flags, uint16_t packet_id,
|
||||
const char* topic_name, const uint8_t* payload, int payload_len);
|
||||
|
||||
/**
|
||||
* @brief Serialize a puback packet into the supplied buffer. See 3.4.
|
||||
*
|
||||
* @param[out] buf the buffer into which the packet will be serialized
|
||||
* @param[in] buf_len the length in bytes of the supplied buffer
|
||||
* @param[in] packet_id integer - the MQTT packet identifier
|
||||
* @return serialized length, or error if <= 0
|
||||
*/
|
||||
int mqtt_puback_packet_serialize(uint8_t* buf, int buf_len, uint16_t packet_id);
|
||||
|
||||
/**
|
||||
* @brief Serialize the supplied subscribe data into the supplied buffer, ready for sending. See 3.8.
|
||||
*
|
||||
* @param[out] buf the buffer into which the packet will be serialized
|
||||
* @param[in] buf_len the length in bytes of the supplied bufferr
|
||||
* @param[in] packet_id integer - the MQTT packet identifier
|
||||
* @param[in] count - number of members in the topicFilters and reqQos arrays
|
||||
* @param[in] topic_filters - array of topic filter names
|
||||
* @param[in] requested_qos - array of requested QoS
|
||||
* @return serialized length, or error if <= 0
|
||||
*/
|
||||
int mqtt_subscribe_packet_serialize(uint8_t* buf, int buf_len, uint16_t packet_id, uint32_t count,
|
||||
char* topic_filters[], const int requested_qos[]);
|
||||
|
||||
/**
|
||||
* @brief Serialize the supplied unsubscribe data into the supplied buffer, ready for sending. See 3.10.
|
||||
*
|
||||
* @param[out] buf the raw buffer data, of the correct length determined by the remaining length field
|
||||
* @param[in] buf_len the length in bytes of the data in the supplied buffer
|
||||
* @param[in] packet_id integer - the MQTT packet identifier
|
||||
* @param[in] count - number of members in the topicFilters array
|
||||
* @param[in] topic_filters - array of topic filter names
|
||||
* @return serialized length, or error if <= 0
|
||||
*/
|
||||
int mqtt_unsubscribe_packet_serialize(uint8_t* buf, int buf_len, uint16_t packet_id, int count, char* topic_filters[]);
|
||||
|
||||
/**
|
||||
* @brief Deserialize the supplied (wire) buffer into connack data. See 3.2.
|
||||
*
|
||||
* @param[in] buf the raw buffer data, of the correct length determined by the remaining length field
|
||||
* @param[in] buf_len the length in bytes of the data in the supplied buffer
|
||||
* @param[out] session_present the session present flag returned (only for MQTT 3.1.1)
|
||||
* @param[out] connack_rc returned integer value of the connack return code
|
||||
* @return @see MQTTPacketErrCode
|
||||
*/
|
||||
int mqtt_connack_packet_deserialize(uint8_t* buf, int buf_len, uint8_t* session_present, uint8_t* connack_rc);
|
||||
|
||||
/**
|
||||
* @brief Deserialize the supplied (wire) buffer into pingresp data. See 3.13.
|
||||
*
|
||||
* @param[in] buf the raw buffer data, of the correct length determined by the remaining length field
|
||||
* @param[in] buf_len the length in bytes of the data in the supplied buffer
|
||||
* @return @see MQTTPacketErrCode
|
||||
*/
|
||||
int mqtt_pingresp_packet_deserialize(uint8_t* buf, int buf_len);
|
||||
|
||||
/**
|
||||
* @brief Deserialize the supplied (wire) buffer into publish data. See 3.3.
|
||||
*
|
||||
* @param[in] buf the raw buffer data, of the correct length determined by the remaining length field
|
||||
* @param[in] buf_len the length in bytes of the data in the supplied buffer
|
||||
* @param[out] flags the MQTT dup, qos, retained flag
|
||||
* @param[out] packet_id returned integer - the MQTT packet identifier
|
||||
* @param[out] topic_name returned string - the MQTT topic in the publish
|
||||
* @param[out] topic_len returned integer - the length of the MQTT topic
|
||||
* @param[out] payload returned byte buffer - the MQTT publish payload
|
||||
* @param[out] payload_len returned integer - the length of the MQTT payload
|
||||
* @return @see MQTTPacketErrCode
|
||||
*/
|
||||
int mqtt_publish_packet_deserialize(uint8_t* buf, int buf_len, MQTTPublishFlags* flags, uint16_t* packet_id,
|
||||
char** topic_name, int* topic_len, uint8_t** payload, int* payload_len);
|
||||
|
||||
/**
|
||||
* @brief Deserialize the supplied (wire) buffer into an ack. See 3.4.
|
||||
*
|
||||
* @param[in] buf the raw buffer data, of the correct length determined by the remaining length field
|
||||
* @param[in] buf_len the length in bytes of the data in the supplied buffer
|
||||
* @param[out] packet_id returned integer - the MQTT packet identifier
|
||||
* @return @see MQTTPacketErrCode
|
||||
*/
|
||||
int mqtt_puback_packet_deserialize(uint8_t* buf, int buf_len, uint16_t* packet_id);
|
||||
|
||||
/**
|
||||
* @brief Deserialize the supplied (wire) buffer into suback data. See 3.9.
|
||||
*
|
||||
* @param[in] buf the raw buffer data, of the correct length determined by the remaining length field
|
||||
* @param[in] buf_len the length in bytes of the data in the supplied buffer
|
||||
* @param[in] maxcount - the maximum number of members allowed in the grantedQoSs array
|
||||
* @param[out] count returned integer - number of members in the grantedQoSs array
|
||||
* @param[out] packet_id returned integer - the MQTT packet identifier
|
||||
* @param[out] granted_qos returned array of integers - the granted qualities of service
|
||||
* @return @see MQTTPacketErrCode
|
||||
*/
|
||||
int mqtt_suback_packet_deserialize(uint8_t* buf, int buf_len, int maxcount, int* count, uint16_t* packet_id,
|
||||
int granted_qos[]);
|
||||
|
||||
/**
|
||||
* @brief Deserialize the supplied (wire) buffer into unsuback data. See 3.11.
|
||||
*
|
||||
* @param[in] buf the raw buffer data, of the correct length determined by the remaining length field
|
||||
* @param[in] buf_len the length in bytes of the data in the supplied buffer
|
||||
* @param[out] packet_id returned integer - the MQTT packet identifier
|
||||
* @return @see MQTTPacketErrCode
|
||||
*/
|
||||
int mqtt_unsuback_packet_deserialize(uint8_t* buf, int buf_len, uint16_t* packet_id);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // IOT_HUB_DEVICE_C_SDK_COMMON_MQTT_PACKET_INC_MQTT_PACKET_H_
|
@@ -0,0 +1,364 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 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.
|
||||
*
|
||||
* @file mqtt_packet_deserialize.c
|
||||
* @brief implements mqtt packet deserialize. Reference paho.mqtt.embedded-c &
|
||||
* http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/mqtt-v3.1.1.pdf
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-05-21
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-05-21 <td>1.0 <td>fancyxu <td>first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include "mqtt_packet.h"
|
||||
|
||||
/**
|
||||
* @brief Reads one character from the input buffer. See 1.5.1.
|
||||
*
|
||||
* @param[in,out] pptr pointer to the input buffer - incremented by the number of bytes used & returned
|
||||
* @return the character read
|
||||
*/
|
||||
static inline char _read_char(uint8_t** pptr)
|
||||
{
|
||||
char c = **pptr;
|
||||
(*pptr)++;
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Calculates an integer from two bytes read from the input buffer. See 1.5.2.
|
||||
*
|
||||
* @param[in,out] pptr pointer to the input buffer - incremented by the number of bytes used & returned
|
||||
* @return the integer value calculated
|
||||
*/
|
||||
static inline uint16_t _read_int(uint8_t** pptr)
|
||||
{
|
||||
uint8_t* ptr = *pptr;
|
||||
int len = 256 * (*ptr) + (*(ptr + 1));
|
||||
*pptr += 2;
|
||||
return (uint16_t)len;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read a UTF-8 encoded string pointer from the input buffer. See 1.5.3.
|
||||
*
|
||||
* @param[out] pstring the pointer of the C string to read.
|
||||
* @param[in,out] pptr pointer to the input buffer - incremented by the number of bytes used & returned
|
||||
* @return the length of string
|
||||
*/
|
||||
static inline int _read_string(char** pstring, uint8_t** pptr)
|
||||
{
|
||||
int str_len = _read_int(pptr);
|
||||
if (str_len > 0) {
|
||||
*pstring = (char*)*pptr;
|
||||
*pptr += str_len;
|
||||
}
|
||||
|
||||
return str_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check the mqtt type and get publish flags. See 2.2.
|
||||
*
|
||||
* @param[in] packet_type type of mqtt control packet, See 2.2.1
|
||||
* @param[in] pflags pointer to the flags of mqtt control packet fixed header. See 2.2.2
|
||||
* @param[in,out] pptr pointer to the input buffer - incremented by the number of bytes used & returned
|
||||
* @return @see MQTTPacketErrCode
|
||||
*/
|
||||
static int _mqtt_packet_type_check(MQTTPacketType packet_type, MQTTPublishFlags* pflags, uint8_t** pptr)
|
||||
{
|
||||
MQTTHeader header = {0};
|
||||
|
||||
header.byte = _read_char(pptr);
|
||||
|
||||
switch (header.bits.type) {
|
||||
case PUBLISH:
|
||||
pflags->dup = header.bits.dup;
|
||||
pflags->qos = header.bits.qos;
|
||||
pflags->retain = header.bits.retain;
|
||||
break;
|
||||
case SUBSCRIBE:
|
||||
case UNSUBSCRIBE:
|
||||
if ((header.byte & 0xf0) != 0x10) {
|
||||
return MQTT_ERR_INVALID_PACKET_TYPE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return packet_type != header.bits.type ? MQTT_ERR_INVALID_PACKET_TYPE : MQTT_RET_PACKET_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deserialize remaining length. See 2.2.3.
|
||||
*
|
||||
* @param[in] buf_len the length in bytes of the supplied buffer
|
||||
* @param[in] plen the pointer to the remaining length
|
||||
* @param[in,out] pptr pointer to the input buffer - incremented by the number of bytes used & returned
|
||||
* @return @see MQTTPacketErrCode
|
||||
*/
|
||||
static int _mqtt_remaining_length_deserialize(int buf_len, int* plen, uint8_t** pptr)
|
||||
{
|
||||
uint8_t* buf = *pptr;
|
||||
uint8_t c = 0;
|
||||
uint32_t multiplier = 1;
|
||||
uint32_t count = 0;
|
||||
uint32_t len = 0;
|
||||
|
||||
do {
|
||||
if (++count > MAX_NO_OF_REMAINING_LENGTH_BYTES) {
|
||||
return MQTT_ERR_INVALID_PACKET_TYPE;
|
||||
}
|
||||
c = *buf++;
|
||||
len += (c & 127) * multiplier;
|
||||
multiplier *= 128;
|
||||
|
||||
SHORT_BUFFER_CHECK(buf_len, count + 1 + len);
|
||||
} while (c & 128);
|
||||
|
||||
if (plen) {
|
||||
*plen = len;
|
||||
}
|
||||
|
||||
*pptr += count;
|
||||
return MQTT_RET_PACKET_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deserialize the supplied (wire) buffer into connack data. See 3.2.
|
||||
*
|
||||
* @param[in] buf the raw buffer data, of the correct length determined by the remaining length field
|
||||
* @param[in] buf_len the length in bytes of the data in the supplied buffer
|
||||
* @param[out] session_present the session present flag returned (only for MQTT 3.1.1)
|
||||
* @param[out] connack_rc returned integer value of the connack return code
|
||||
* @return @see MQTTPacketErrCode
|
||||
*/
|
||||
int mqtt_connack_packet_deserialize(uint8_t* buf, int buf_len, uint8_t* session_present, uint8_t* connack_rc)
|
||||
{
|
||||
SHORT_BUFFER_CHECK(buf_len, MIN_MQTT_FIXED_HEADER_LEN);
|
||||
|
||||
uint8_t* ptr = buf;
|
||||
int rc = 0;
|
||||
MQTTConnackFlags flags = {0};
|
||||
|
||||
rc = _mqtt_packet_type_check(CONNACK, NULL, &ptr);
|
||||
if (rc) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
rc = _mqtt_remaining_length_deserialize(buf_len, NULL, &ptr);
|
||||
if (rc < 0) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
flags.all = _read_char(&ptr);
|
||||
*session_present = flags.bits.sessionpresent;
|
||||
*connack_rc = _read_char(&ptr);
|
||||
exit:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deserialize the supplied (wire) buffer into pingresp data. See 3.13.
|
||||
*
|
||||
* @param[in] buf the raw buffer data, of the correct length determined by the remaining length field
|
||||
* @param[in] buf_len the length in bytes of the data in the supplied buffer
|
||||
* @return @see MQTTPacketErrCode
|
||||
*/
|
||||
int mqtt_pingresp_packet_deserialize(uint8_t* buf, int buf_len)
|
||||
{
|
||||
SHORT_BUFFER_CHECK(buf_len, MIN_MQTT_FIXED_HEADER_LEN);
|
||||
|
||||
uint8_t* ptr = buf;
|
||||
int rc = 0;
|
||||
|
||||
rc = _mqtt_packet_type_check(PINGRESP, NULL, &ptr);
|
||||
if (rc) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
rc = _mqtt_remaining_length_deserialize(buf_len, NULL, &ptr);
|
||||
if (rc < 0) {
|
||||
goto exit;
|
||||
}
|
||||
exit:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deserialize the supplied (wire) buffer into publish data. See 3.3.
|
||||
*
|
||||
* @param[in] buf the raw buffer data, of the correct length determined by the remaining length field
|
||||
* @param[in] buf_len the length in bytes of the data in the supplied buffer
|
||||
* @param[out] flags the MQTT dup, qos, retained flag
|
||||
* @param[out] packet_id returned integer - the MQTT packet identifier
|
||||
* @param[out] topic_name returned string - the MQTT topic in the publish
|
||||
* @param[out] topic_len returned integer - the length of the MQTT topic
|
||||
* @param[out] payload returned byte buffer - the MQTT publish payload
|
||||
* @param[out] payload_len returned integer - the length of the MQTT payload
|
||||
* @return @see MQTTPacketErrCode
|
||||
*/
|
||||
int mqtt_publish_packet_deserialize(uint8_t* buf, int buf_len, MQTTPublishFlags* flags, uint16_t* packet_id,
|
||||
char** topic_name, int* topic_len, uint8_t** payload, int* payload_len)
|
||||
{
|
||||
SHORT_BUFFER_CHECK(buf_len, MIN_MQTT_FIXED_HEADER_LEN);
|
||||
|
||||
uint8_t* ptr = buf;
|
||||
uint8_t* ptr_remain = NULL;
|
||||
int rc = 0;
|
||||
int len = 0;
|
||||
|
||||
rc = _mqtt_packet_type_check(PUBLISH, flags, &ptr);
|
||||
if (rc) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
rc = _mqtt_remaining_length_deserialize(buf_len, &len, &ptr);
|
||||
if (rc) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ptr_remain = ptr;
|
||||
|
||||
rc = _read_string(topic_name, &ptr);
|
||||
if (rc <= 0) {
|
||||
rc = MQTT_ERR_INVALID_PACKET_TYPE;
|
||||
goto exit;
|
||||
}
|
||||
*topic_len = rc;
|
||||
|
||||
if (flags->qos > 0) {
|
||||
*packet_id = _read_int(&ptr);
|
||||
}
|
||||
|
||||
*payload_len = len - (ptr - ptr_remain);
|
||||
*payload = ptr;
|
||||
|
||||
rc = MQTT_RET_PACKET_OK;
|
||||
exit:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deserialize the supplied (wire) buffer into an ack. See 3.4.
|
||||
*
|
||||
* @param[in] buf the raw buffer data, of the correct length determined by the remaining length field
|
||||
* @param[in] buf_len the length in bytes of the data in the supplied buffer
|
||||
* @param[out] packet_id returned integer - the MQTT packet identifier
|
||||
* @return @see MQTTPacketErrCode
|
||||
*/
|
||||
int mqtt_puback_packet_deserialize(uint8_t* buf, int buf_len, uint16_t* packet_id)
|
||||
{
|
||||
SHORT_BUFFER_CHECK(buf_len, MIN_MQTT_FIXED_HEADER_LEN);
|
||||
|
||||
uint8_t* ptr = buf;
|
||||
int rc = 0;
|
||||
|
||||
rc = _mqtt_packet_type_check(PUBACK, 0, &ptr);
|
||||
if (rc) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
rc = _mqtt_remaining_length_deserialize(buf_len, NULL, &ptr);
|
||||
if (rc) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
*packet_id = _read_int(&ptr);
|
||||
exit:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deserialize the supplied (wire) buffer into suback data. See 3.9.
|
||||
*
|
||||
* @param[in] buf the raw buffer data, of the correct length determined by the remaining length field
|
||||
* @param[in] buf_len the length in bytes of the data in the supplied buffer
|
||||
* @param[in] maxcount - the maximum number of members allowed in the grantedQoSs array
|
||||
* @param[out] count returned integer - number of members in the grantedQoSs array
|
||||
* @param[out] packet_id returned integer - the MQTT packet identifier
|
||||
* @param[out] granted_qos returned array of integers - the granted qualities of service
|
||||
* @return @see MQTTPacketErrCode
|
||||
*/
|
||||
int mqtt_suback_packet_deserialize(uint8_t* buf, int buf_len, int maxcount, int* count, uint16_t* packet_id,
|
||||
int granted_qos[])
|
||||
{
|
||||
SHORT_BUFFER_CHECK(buf_len, MIN_MQTT_FIXED_HEADER_LEN);
|
||||
|
||||
uint8_t* ptr = buf;
|
||||
int rc = 0;
|
||||
int len = 0;
|
||||
|
||||
rc = _mqtt_packet_type_check(SUBACK, NULL, &ptr);
|
||||
if (rc) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
rc = _mqtt_remaining_length_deserialize(buf_len, &len, &ptr);
|
||||
if (rc) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
*packet_id = _read_int(&ptr);
|
||||
len -= 2;
|
||||
|
||||
*count = 0;
|
||||
while (len--) {
|
||||
if (*count > maxcount) {
|
||||
rc = MQTT_ERR_SUB_COUNT_EXCEED;
|
||||
goto exit;
|
||||
}
|
||||
granted_qos[(*count)++] = _read_char(&ptr);
|
||||
}
|
||||
|
||||
exit:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deserialize the supplied (wire) buffer into unsuback data. See 3.11.
|
||||
*
|
||||
* @param[in] buf the raw buffer data, of the correct length determined by the remaining length field
|
||||
* @param[in] buf_len the length in bytes of the data in the supplied buffer
|
||||
* @param[out] packet_id returned integer - the MQTT packet identifier
|
||||
* @return @see MQTTPacketErrCode
|
||||
*/
|
||||
int mqtt_unsuback_packet_deserialize(uint8_t* buf, int buf_len, uint16_t* packet_id)
|
||||
{
|
||||
SHORT_BUFFER_CHECK(buf_len, MIN_MQTT_FIXED_HEADER_LEN);
|
||||
|
||||
uint8_t* ptr = buf;
|
||||
int rc = 0;
|
||||
|
||||
rc = _mqtt_packet_type_check(UNSUBACK, NULL, &ptr);
|
||||
if (rc) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
rc = _mqtt_remaining_length_deserialize(buf_len, NULL, &ptr);
|
||||
if (rc) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
*packet_id = _read_int(&ptr);
|
||||
exit:
|
||||
return rc;
|
||||
}
|
@@ -0,0 +1,458 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 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.
|
||||
*
|
||||
* @file mqtt_packet_serialize.c
|
||||
* @brief implements mqtt packet serialize. Reference paho.mqtt.embedded-c &
|
||||
* http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/mqtt-v3.1.1.pdf
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-05-21
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-05-21 <td>1.0 <td>fancyxu <td>first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include "mqtt_packet.h"
|
||||
|
||||
/**
|
||||
* @brief Writes one character to an output buffer. See 1.5.1.
|
||||
*
|
||||
* @param[in] c the character to write
|
||||
* @param[out] pptr pointer to the output buffer - incremented by the number of bytes used & returned
|
||||
*/
|
||||
static inline void _write_char(char c, uint8_t** pptr)
|
||||
{
|
||||
**pptr = c;
|
||||
(*pptr)++;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Writes an integer as 2 bytes to an output buffer. See 1.5.2.
|
||||
*
|
||||
* @param[in] value the integer to write.
|
||||
* @param[out] pptr pointer to the output buffer - incremented by the number of bytes used & returned.
|
||||
*/
|
||||
static inline void _write_int(int value, uint8_t** pptr)
|
||||
{
|
||||
**pptr = (uint8_t)(value / 256);
|
||||
(*pptr)++;
|
||||
**pptr = (uint8_t)(value % 256);
|
||||
(*pptr)++;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Writes a UTF-8 encoded string to an output buffer. Converts C string to
|
||||
* length-delimited. See 1.5.3.
|
||||
*
|
||||
* @param[in] string the C string to write
|
||||
* @param[out] pptr pointer to the output buffer - incremented by the number of bytes used & returned
|
||||
*/
|
||||
static inline void _write_string(const char* string, uint8_t** pptr)
|
||||
{
|
||||
int len = strlen(string);
|
||||
_write_int(len, pptr);
|
||||
memcpy(*pptr, string, len);
|
||||
*pptr += len;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Serialize mqtt fixed header except remaining length. See 2.2.
|
||||
*
|
||||
* @param[in] packet_type type of mqtt control packet, See 2.2.1 MQTT Control Packet type
|
||||
* @param[in] pflags pointer to the flags of mqtt control packet fixed header. See 2.2.2 Flags
|
||||
* @param[out] pptr pointer to the output buffer - incremented by the number of bytes used & returned
|
||||
*/
|
||||
static void _mqtt_header_serialize(MQTTPacketType packet_type, const MQTTPublishFlags* pflags, uint8_t** pptr)
|
||||
{
|
||||
MQTTHeader header = {0};
|
||||
|
||||
switch (packet_type) {
|
||||
case PUBLISH:
|
||||
header.bits.dup = pflags->dup;
|
||||
header.bits.qos = pflags->qos;
|
||||
header.bits.retain = pflags->retain;
|
||||
break;
|
||||
case SUBSCRIBE:
|
||||
case UNSUBSCRIBE:
|
||||
header.byte = 0x02;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
header.bits.type |= packet_type;
|
||||
_write_char(header.byte, pptr); /* write header */
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Serialize remaining length. See 2.2.3.
|
||||
*
|
||||
* @param[in] length the length to be encoded to remaining length
|
||||
* @param[out] pptr pointer to the output buffer - incremented by the number of bytes used & returned
|
||||
*/
|
||||
static void _mqtt_remaining_length_serialize(int length, uint8_t** pptr)
|
||||
{
|
||||
char d;
|
||||
do {
|
||||
d = length % 128;
|
||||
length /= 128;
|
||||
|
||||
// if there are more digits to encode, set the top bit of this digit
|
||||
if (length > 0) {
|
||||
d |= 0x80;
|
||||
}
|
||||
|
||||
_write_char(d, pptr);
|
||||
} while (length > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Serialize the remaining length of the MQTT connect packet that would be produced using the
|
||||
* supplied connect options. See 3.1.1.
|
||||
*
|
||||
* @param[in] options the options to be used to build the connect packet
|
||||
* @param[in] buf_len the length in bytes of the supplied buffer
|
||||
* @param[out] pptr pointer to the output buffer - incremented by the number of bytes used & returned
|
||||
*/
|
||||
static int _mqtt_connect_remaining_length_serialize(const MQTTPacketConnectOption* options, int buf_len, uint8_t** pptr)
|
||||
{
|
||||
if (options->mqtt_version != 3 && options->mqtt_version != 4) {
|
||||
return MQTT_ERR_VERSION_UNSUPPORTED;
|
||||
}
|
||||
|
||||
int len = 0;
|
||||
|
||||
len = options->mqtt_version == 3 ? 12 : 10;
|
||||
|
||||
len += strlen(options->client_id) + 2;
|
||||
|
||||
if (options->will_flag) {
|
||||
len += strlen(options->will.topic_name) + 2 + strlen(options->will.message) + 2;
|
||||
}
|
||||
|
||||
if (options->username) {
|
||||
len += strlen(options->username) + 2;
|
||||
}
|
||||
|
||||
if (options->password) {
|
||||
len += strlen(options->password) + 2;
|
||||
}
|
||||
|
||||
SHORT_BUFFER_CHECK(buf_len, len);
|
||||
|
||||
_mqtt_remaining_length_serialize(len, pptr);
|
||||
|
||||
return MQTT_RET_PACKET_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Serialize the mqtt connect packet variable header. See 3.1.2.
|
||||
*
|
||||
* @param[in] options the options to be used to build the connect packet
|
||||
* @param[out] pptr pointer to the output buffer - incremented by the number of bytes used & returned
|
||||
*/
|
||||
static int _mqtt_connect_variable_header_serialize(const MQTTPacketConnectOption* options, uint8_t** pptr)
|
||||
{
|
||||
if (options->password && !options->username) {
|
||||
return MQTT_ERR_INVALID_PACKET_TYPE;
|
||||
}
|
||||
|
||||
// 3.1.2.1 protocol name & 3.1.2.2 protocol level
|
||||
switch (options->mqtt_version) {
|
||||
case 3:
|
||||
_write_string("MQIsdp", pptr);
|
||||
_write_char((char)3, pptr);
|
||||
break;
|
||||
case 4:
|
||||
_write_string("MQTT", pptr);
|
||||
_write_char((char)4, pptr);
|
||||
break;
|
||||
}
|
||||
|
||||
// 3.1.2.3 Connect Flags
|
||||
MQTTConnectFlags flags = {
|
||||
.bits.clean_session = options->clean_session,
|
||||
.bits.will = options->will_flag,
|
||||
.bits.will_qos = options->will_flag ? options->will.qos : 0,
|
||||
.bits.will_retain = options->will_flag ? options->will.retained : 0,
|
||||
.bits.username = options->username ? 1 : 0,
|
||||
.bits.password = options->password ? 1 : 0,
|
||||
};
|
||||
_write_char(flags.all, pptr);
|
||||
|
||||
return MQTT_RET_PACKET_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Serialize the remaining length of the MQTT publish packet that would be produced using the supplied
|
||||
* parameters. See 3.3.2 & 3.3.3.
|
||||
*
|
||||
* @param[in] qos the MQTT QoS of the publish (packet id is omitted for QoS 0)
|
||||
* @param[in] topic_name the topic name to be used in the publish
|
||||
* @param[in] payload_len the length of the payload to be sent
|
||||
* @param[in] buf_len the length in bytes of the supplied buffer
|
||||
* @param[out] pptr pointer to the output buffer - incremented by the number of bytes used & returned
|
||||
*/
|
||||
static int _mqtt_publish_remaining_length_serialize(uint8_t qos, const char* topic_name, int payload_len, int buf_len,
|
||||
uint8_t** pptr)
|
||||
{
|
||||
int len = 0;
|
||||
|
||||
len += 2 + strlen(topic_name) + payload_len;
|
||||
if (qos > 0) {
|
||||
len += 2; /* packet id */
|
||||
}
|
||||
|
||||
SHORT_BUFFER_CHECK(buf_len, len + MAX_MQTT_FIXED_HEADER_LEN);
|
||||
|
||||
_mqtt_remaining_length_serialize(len, pptr);
|
||||
return MQTT_RET_PACKET_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Serialize the length of the MQTT subscribe/unsubscribe packet that would be produced using the
|
||||
* supplied parameters. See 3.8.1 & 3.10.1.
|
||||
*
|
||||
* @param[in] count the number of topic filter strings in topicFilters
|
||||
* @param[in] topic_filters the array of topic filter strings to be used in the publish
|
||||
* @param[in] buf_len the length in bytes of the supplied buffer
|
||||
* @param[out] pptr pointer to the output buffer - incremented by the number of bytes used & returned
|
||||
*/
|
||||
static int _mqtt_sub_unsub_remaining_length_serialize(uint32_t count, char* topic_filters[], int buf_len,
|
||||
uint8_t** pptr, uint8_t is_sub)
|
||||
{
|
||||
int i = 0;
|
||||
int len = 2; /* packet id */
|
||||
|
||||
for (i = 0; i < count; ++i) {
|
||||
len += 2 + strlen(topic_filters[i]) + is_sub; /* length + topic */
|
||||
}
|
||||
|
||||
SHORT_BUFFER_CHECK(buf_len, len + MAX_MQTT_FIXED_HEADER_LEN);
|
||||
|
||||
_mqtt_remaining_length_serialize(len, pptr);
|
||||
return MQTT_RET_PACKET_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Serialize the connect options into the buffer. See 3.1.
|
||||
*
|
||||
* @param[out] buf the buffer into which the packet will be serialized
|
||||
* @param[in] buf_len the length in bytes of the supplied buffer
|
||||
* @param[in] options the options to be used to build the connect packet
|
||||
* @return serialized length, or error if <= 0
|
||||
*/
|
||||
int mqtt_connect_packet_serialize(uint8_t* buf, int buf_len, const MQTTPacketConnectOption* options)
|
||||
{
|
||||
SHORT_BUFFER_CHECK(buf_len, MIN_MQTT_FIXED_HEADER_LEN);
|
||||
int rc;
|
||||
uint8_t* ptr = buf;
|
||||
|
||||
// 3.1.1 Fixed header
|
||||
_mqtt_header_serialize(CONNECT, NULL, &ptr);
|
||||
|
||||
rc = _mqtt_connect_remaining_length_serialize(options, buf_len, &ptr);
|
||||
if (rc) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
// 3.1.2 Variable header
|
||||
rc = _mqtt_connect_variable_header_serialize(options, &ptr);
|
||||
if (rc) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
// 3.1.3 Payload
|
||||
_write_int(options->keep_alive_interval, &ptr);
|
||||
_write_string(options->client_id, &ptr);
|
||||
|
||||
if (options->will_flag) {
|
||||
_write_string(options->will.topic_name, &ptr);
|
||||
_write_string(options->will.message, &ptr);
|
||||
}
|
||||
|
||||
if (options->username) {
|
||||
_write_string(options->username, &ptr);
|
||||
}
|
||||
|
||||
if (options->password) {
|
||||
_write_string(options->password, &ptr);
|
||||
}
|
||||
|
||||
return ptr - buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Serialize a disconnect packet into the supplied buffer, ready for writing to a socket. See 3.14.
|
||||
*
|
||||
* @param[out] buf the buffer into which the packet will be serialized
|
||||
* @param[in] buf_len the length in bytes of the supplied buffer, to avoid overruns
|
||||
* @return serialized length, or error if <= 0
|
||||
*/
|
||||
int mqtt_disconnect_packet_serialize(uint8_t* buf, int buf_len)
|
||||
{
|
||||
SHORT_BUFFER_CHECK(buf_len, MIN_MQTT_FIXED_HEADER_LEN);
|
||||
|
||||
uint8_t* ptr = buf;
|
||||
_mqtt_header_serialize(DISCONNECT, 0, &ptr);
|
||||
_mqtt_remaining_length_serialize(0, &ptr);
|
||||
return ptr - buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Serialize a disconnect packet into the supplied buffer, ready for writing to a socket. See 3.12.
|
||||
*
|
||||
* @param[out] buf the buffer into which the packet will be serialized
|
||||
* @param[in] buf_len the length in bytes of the supplied buffer, to avoid overruns
|
||||
* @return serialized length, or error if <= 0
|
||||
*/
|
||||
int mqtt_pingreq_packet_serialize(uint8_t* buf, int buf_len)
|
||||
{
|
||||
SHORT_BUFFER_CHECK(buf_len, MIN_MQTT_FIXED_HEADER_LEN);
|
||||
|
||||
uint8_t* ptr = buf;
|
||||
_mqtt_header_serialize(PINGREQ, 0, &ptr);
|
||||
_mqtt_remaining_length_serialize(0, &ptr);
|
||||
return ptr - buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Serialize the supplied publish data into the supplied buffer, ready for sending. See 3.3.
|
||||
*
|
||||
* @param[out] buf the buffer into which the packet will be serialized
|
||||
* @param[in] buf_len the length in bytes of the supplied buffer
|
||||
* @param[in] flags the MQTT dup, qos, retained flag
|
||||
* @param[in] packet_id integer - the MQTT packet identifier
|
||||
* @param[in] topic_name char * - the MQTT topic in the publish
|
||||
* @param[in] payload byte buffer - the MQTT publish payload
|
||||
* @param[in] payload_len integer - the length of the MQTT payload
|
||||
* @return serialized length, or error if <= 0
|
||||
*/
|
||||
int mqtt_publish_packet_serialize(uint8_t* buf, int buf_len, const MQTTPublishFlags* flags, uint16_t packet_id,
|
||||
const char* topic_name, const uint8_t* payload, int payload_len)
|
||||
{
|
||||
SHORT_BUFFER_CHECK(buf_len, MIN_MQTT_FIXED_HEADER_LEN);
|
||||
|
||||
int rc;
|
||||
uint8_t* ptr = buf;
|
||||
|
||||
_mqtt_header_serialize(PUBLISH, flags, &ptr);
|
||||
|
||||
rc = _mqtt_publish_remaining_length_serialize(flags->qos, topic_name, payload_len, buf_len, &ptr);
|
||||
if (rc) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
_write_string(topic_name, &ptr);
|
||||
|
||||
if (flags->qos > 0) {
|
||||
_write_int(packet_id, &ptr);
|
||||
}
|
||||
|
||||
memcpy(ptr, payload, payload_len);
|
||||
|
||||
ptr += payload_len;
|
||||
return ptr - buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Serialize a puback packet into the supplied buffer. See 3.4.
|
||||
*
|
||||
* @param[out] buf the buffer into which the packet will be serialized
|
||||
* @param[in] buf_len the length in bytes of the supplied buffer
|
||||
* @param[in] packet_id integer - the MQTT packet identifier
|
||||
* @return serialized length, or error if <= 0
|
||||
*/
|
||||
int mqtt_puback_packet_serialize(uint8_t* buf, int buf_len, uint16_t packet_id)
|
||||
{
|
||||
SHORT_BUFFER_CHECK(buf_len, MIN_MQTT_FIXED_HEADER_LEN + 2);
|
||||
|
||||
uint8_t* ptr = buf;
|
||||
|
||||
_mqtt_header_serialize(PUBACK, 0, &ptr);
|
||||
_mqtt_remaining_length_serialize(2, &ptr);
|
||||
_write_int(packet_id, &ptr);
|
||||
return ptr - buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Serialize the supplied subscribe data into the supplied buffer, ready for sending. See 3.8.
|
||||
*
|
||||
* @param[out] buf the buffer into which the packet will be serialized
|
||||
* @param[in] buf_len the length in bytes of the supplied bufferr
|
||||
* @param[in] packet_id integer - the MQTT packet identifier
|
||||
* @param[in] count - number of members in the topicFilters and reqQos arrays
|
||||
* @param[in] topic_filters - array of topic filter names
|
||||
* @param[in] requested_qos - array of requested QoS
|
||||
* @return serialized length, or error if <= 0
|
||||
*/
|
||||
int mqtt_subscribe_packet_serialize(uint8_t* buf, int buf_len, uint16_t packet_id, uint32_t count,
|
||||
char* topic_filters[], const int requested_qos[])
|
||||
{
|
||||
SHORT_BUFFER_CHECK(buf_len, MIN_MQTT_FIXED_HEADER_LEN);
|
||||
|
||||
int rc, i = 0;
|
||||
uint8_t* ptr = buf;
|
||||
|
||||
_mqtt_header_serialize(SUBSCRIBE, 0, &ptr);
|
||||
rc = _mqtt_sub_unsub_remaining_length_serialize(count, topic_filters, buf_len, &ptr, 1);
|
||||
if (rc) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
_write_int(packet_id, &ptr);
|
||||
|
||||
for (i = 0; i < count; ++i) {
|
||||
_write_string(topic_filters[i], &ptr);
|
||||
_write_char(requested_qos[i], &ptr);
|
||||
}
|
||||
|
||||
return ptr - buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Serialize the supplied unsubscribe data into the supplied buffer, ready for sending. See 3.10.
|
||||
*
|
||||
* @param[out] buf the raw buffer data, of the correct length determined by the remaining length field
|
||||
* @param[in] buf_len the length in bytes of the data in the supplied buffer
|
||||
* @param[in] packet_id integer - the MQTT packet identifier
|
||||
* @param[in] count - number of members in the topicFilters array
|
||||
* @param[in] topic_filters - array of topic filter names
|
||||
* @return serialized length, or error if <= 0
|
||||
*/
|
||||
int mqtt_unsubscribe_packet_serialize(uint8_t* buf, int buf_len, uint16_t packet_id, int count, char* topic_filters[])
|
||||
{
|
||||
SHORT_BUFFER_CHECK(buf_len, MIN_MQTT_FIXED_HEADER_LEN);
|
||||
|
||||
int rc, i = 0;
|
||||
uint8_t* ptr = buf;
|
||||
|
||||
_mqtt_header_serialize(UNSUBSCRIBE, 0, &ptr);
|
||||
rc = _mqtt_sub_unsub_remaining_length_serialize(count, topic_filters, buf_len, &ptr, 0);
|
||||
if (rc) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
_write_int(packet_id, &ptr);
|
||||
|
||||
for (i = 0; i < count; ++i) {
|
||||
_write_string(topic_filters[i], &ptr);
|
||||
}
|
||||
|
||||
return ptr - buf;
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
Language: Cpp
|
||||
BasedOnStyle: Google
|
||||
ColumnLimit: 120
|
||||
DerivePointerAlignment: true
|
||||
PointerAlignment: Left
|
||||
SortIncludes: true
|
||||
IncludeBlocks: Preserve
|
||||
IndentPPDirectives: AfterHash
|
@@ -0,0 +1,307 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 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.
|
||||
*
|
||||
* @file test_mqtt_packet.cc
|
||||
* @brief unit test for mqtt packet
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-06-01
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-06-01 <td>1.0 <td>fancyxu <td>first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "mqtt_packet.h"
|
||||
|
||||
namespace mqtt_packet_unittest {
|
||||
/**
|
||||
* @brief Test connect packet.
|
||||
*
|
||||
*/
|
||||
TEST(MQTTPacketTest, CONNECT) {
|
||||
uint8_t test_packet[] = {
|
||||
0x10, 0x77, 0x00, 0x04, 0x4d, 0x51, 0x54, 0x54, 0x04, 0xc2, 0x00, 0xf0, 0x00, 0x0e, 0x4a, 0x4e, 0x4c, 0x52,
|
||||
0x57, 0x4f, 0x33, 0x54, 0x35, 0x39, 0x74, 0x65, 0x73, 0x74, 0x00, 0x28, 0x4a, 0x4e, 0x4c, 0x52, 0x57, 0x4f,
|
||||
0x33, 0x54, 0x35, 0x39, 0x74, 0x65, 0x73, 0x74, 0x3b, 0x32, 0x31, 0x30, 0x31, 0x30, 0x34, 0x30, 0x36, 0x3b,
|
||||
0x49, 0x41, 0x5a, 0x5a, 0x4a, 0x3b, 0x32, 0x31, 0x34, 0x37, 0x34, 0x38, 0x33, 0x36, 0x34, 0x37, 0x00, 0x31,
|
||||
0x66, 0x62, 0x63, 0x39, 0x65, 0x34, 0x36, 0x33, 0x61, 0x32, 0x30, 0x64, 0x66, 0x30, 0x34, 0x35, 0x65, 0x62,
|
||||
0x66, 0x34, 0x38, 0x34, 0x35, 0x61, 0x35, 0x39, 0x62, 0x32, 0x61, 0x38, 0x35, 0x66, 0x65, 0x62, 0x34, 0x31,
|
||||
0x34, 0x31, 0x30, 0x30, 0x3b, 0x68, 0x6d, 0x61, 0x63, 0x73, 0x68, 0x61, 0x31,
|
||||
};
|
||||
|
||||
uint8_t packet_buf[2048];
|
||||
std::string user_name = "JNLRWO3T59test;21010406;IAZZJ;2147483647";
|
||||
std::string password = "fbc9e463a20df045ebf4845a59b2a85feb414100;hmacsha1";
|
||||
|
||||
MQTTPacketConnectOption options = {
|
||||
.mqtt_version = 4,
|
||||
.client_id = "JNLRWO3T59test",
|
||||
.keep_alive_interval = 240,
|
||||
.clean_session = 1,
|
||||
.will_flag = 0,
|
||||
.will = {0},
|
||||
.username = reinterpret_cast<char *>(const_cast<char *>(user_name.c_str())),
|
||||
.password = reinterpret_cast<char *>(const_cast<char *>(password.c_str())),
|
||||
};
|
||||
|
||||
ASSERT_EQ(mqtt_connect_packet_serialize(packet_buf, sizeof(packet_buf), &options), sizeof(test_packet));
|
||||
ASSERT_EQ(memcmp(packet_buf, test_packet, sizeof(test_packet)), 0);
|
||||
|
||||
ASSERT_EQ(mqtt_connect_packet_serialize(packet_buf, 1, &options), MQTT_ERR_SHORT_BUFFER);
|
||||
|
||||
options.username = NULL;
|
||||
ASSERT_EQ(mqtt_connect_packet_serialize(packet_buf, sizeof(packet_buf), &options), MQTT_ERR_INVALID_PACKET_TYPE);
|
||||
|
||||
options.password = NULL;
|
||||
ASSERT_EQ(mqtt_connect_packet_serialize(packet_buf, sizeof(packet_buf), &options),
|
||||
sizeof(test_packet) - user_name.length() - password.length() - 4);
|
||||
|
||||
options.mqtt_version = 5;
|
||||
ASSERT_EQ(mqtt_connect_packet_serialize(packet_buf, sizeof(packet_buf), &options), MQTT_ERR_VERSION_UNSUPPORTED);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Test connack packet.
|
||||
*
|
||||
*/
|
||||
TEST(MQTTPacketTest, CONNACK) {
|
||||
uint8_t test_packet[] = {0x20, 0x02, 0x00, 0x00};
|
||||
uint8_t session_present, connack_rc;
|
||||
|
||||
for (uint8_t i = CONNACK_CONNECTION_ACCEPTED; i <= CONNACK_NOT_AUTHORIZED_ERROR; i++) {
|
||||
test_packet[3] = i;
|
||||
ASSERT_EQ(mqtt_connack_packet_deserialize(test_packet, sizeof(test_packet), &session_present, &connack_rc),
|
||||
MQTT_RET_PACKET_OK);
|
||||
ASSERT_EQ(connack_rc, i);
|
||||
}
|
||||
|
||||
ASSERT_EQ(mqtt_connack_packet_deserialize(test_packet, 2, &session_present, &connack_rc), MQTT_ERR_SHORT_BUFFER);
|
||||
|
||||
test_packet[0] = SUBACK;
|
||||
ASSERT_EQ(mqtt_connack_packet_deserialize(test_packet, sizeof(test_packet), &session_present, &connack_rc),
|
||||
MQTT_ERR_INVALID_PACKET_TYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Test publish packet.
|
||||
*
|
||||
*/
|
||||
TEST(MQTTPacketTest, PUBLISH) {
|
||||
uint8_t test_packet[] = {
|
||||
0x32, 0x40, 0x00, 0x14, 0x4a, 0x4e, 0x4c, 0x52, 0x57, 0x4f, 0x33, 0x54, 0x35, 0x39, 0x2f, 0x74, 0x65,
|
||||
0x73, 0x74, 0x2f, 0x64, 0x61, 0x74, 0x61, 0x70, 0xaf, 0x7b, 0x22, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e,
|
||||
0x22, 0x3a, 0x20, 0x22, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x5f, 0x74, 0x65, 0x73, 0x74, 0x22,
|
||||
0x2c, 0x20, 0x22, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x3a, 0x20, 0x22, 0x30, 0x22, 0x7d,
|
||||
};
|
||||
|
||||
uint8_t packet_buf[128];
|
||||
|
||||
/**
|
||||
* @brief QoS1
|
||||
*
|
||||
*/
|
||||
char serialize_topic_name[] = "JNLRWO3T59/test/data";
|
||||
int serialize_topic_len = strlen(serialize_topic_name);
|
||||
uint16_t serialize_packet_id = 28847;
|
||||
uint8_t serialize_payload[] = "{\"action\": \"publish_test\", \"count\": \"0\"}";
|
||||
int serialize_payload_len = strlen((const char *)serialize_payload);
|
||||
MQTTPublishFlags serialize_flags = {0, 1, 0};
|
||||
|
||||
ASSERT_EQ(mqtt_publish_packet_serialize(packet_buf, sizeof(packet_buf), &serialize_flags, serialize_packet_id,
|
||||
serialize_topic_name, serialize_payload, serialize_payload_len),
|
||||
sizeof(test_packet));
|
||||
ASSERT_EQ(memcmp(packet_buf, test_packet, sizeof(test_packet)), 0);
|
||||
|
||||
char *deserialize_topic_name;
|
||||
int deserialize_topic_len;
|
||||
uint16_t deserialize_packet_id = 0;
|
||||
uint8_t *deserialize_payload;
|
||||
int deserialize_payload_len;
|
||||
MQTTPublishFlags deserialize_flags;
|
||||
ASSERT_EQ(mqtt_publish_packet_deserialize(test_packet, sizeof(test_packet), &deserialize_flags,
|
||||
&deserialize_packet_id, &deserialize_topic_name, &deserialize_topic_len,
|
||||
&deserialize_payload, &deserialize_payload_len),
|
||||
0);
|
||||
|
||||
ASSERT_EQ(deserialize_packet_id, serialize_packet_id);
|
||||
ASSERT_EQ(deserialize_topic_len, serialize_topic_len);
|
||||
ASSERT_EQ(deserialize_payload_len, serialize_payload_len);
|
||||
|
||||
ASSERT_EQ(memcmp(&deserialize_flags, &serialize_flags, sizeof(deserialize_flags)), 0);
|
||||
ASSERT_EQ(memcmp(deserialize_topic_name, serialize_topic_name, deserialize_topic_len), 0);
|
||||
ASSERT_EQ(memcmp(deserialize_payload, serialize_payload, deserialize_payload_len), 0);
|
||||
|
||||
/**
|
||||
* @brief QoS0
|
||||
*
|
||||
*/
|
||||
serialize_flags.qos = 0;
|
||||
deserialize_packet_id = 0;
|
||||
ASSERT_EQ(
|
||||
mqtt_publish_packet_serialize(packet_buf, sizeof(packet_buf), &serialize_flags, serialize_packet_id,
|
||||
serialize_topic_name, serialize_payload, strlen((const char *)serialize_payload)),
|
||||
sizeof(test_packet) - 2);
|
||||
ASSERT_EQ(mqtt_publish_packet_deserialize(packet_buf, sizeof(packet_buf), &deserialize_flags, &deserialize_packet_id,
|
||||
&deserialize_topic_name, &deserialize_topic_len, &deserialize_payload,
|
||||
&deserialize_payload_len),
|
||||
MQTT_RET_PACKET_OK);
|
||||
ASSERT_NE(deserialize_packet_id, serialize_packet_id);
|
||||
|
||||
/**
|
||||
* @brief bad packet
|
||||
*
|
||||
*/
|
||||
packet_buf[0] = PUBACK;
|
||||
ASSERT_EQ(mqtt_publish_packet_deserialize(packet_buf, sizeof(packet_buf), &deserialize_flags, &deserialize_packet_id,
|
||||
&deserialize_topic_name, &deserialize_topic_len, &deserialize_payload,
|
||||
&deserialize_payload_len),
|
||||
MQTT_ERR_INVALID_PACKET_TYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Test puback packet.
|
||||
*
|
||||
*/
|
||||
TEST(MQTTPacketTest, PUBACK) {
|
||||
uint8_t test_packet[] = {0x40, 0x02, 0x70, 0xaf};
|
||||
|
||||
uint8_t packet_buf[128];
|
||||
|
||||
uint16_t serialize_packet_id = 28847;
|
||||
ASSERT_EQ(mqtt_puback_packet_serialize(packet_buf, sizeof(packet_buf), serialize_packet_id), 4);
|
||||
ASSERT_EQ(memcmp(packet_buf, test_packet, sizeof(test_packet)), 0);
|
||||
|
||||
uint16_t deserialize_packet_id;
|
||||
ASSERT_EQ(mqtt_puback_packet_deserialize(test_packet, sizeof(test_packet), &deserialize_packet_id),
|
||||
MQTT_RET_PACKET_OK);
|
||||
ASSERT_EQ(deserialize_packet_id, serialize_packet_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Test subscribe packet.
|
||||
*
|
||||
*/
|
||||
TEST(MQTTPacketTest, SUBSCRIBE) {
|
||||
uint8_t test_packet[] = {
|
||||
0x82, 0x19, 0x70, 0xae, 0x00, 0x14, 0x4a, 0x4e, 0x4c, 0x52, 0x57, 0x4f, 0x33, 0x54,
|
||||
0x35, 0x39, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x64, 0x61, 0x74, 0x61, 0x00,
|
||||
};
|
||||
|
||||
uint8_t packet_buf[128];
|
||||
|
||||
std::string topic = "JNLRWO3T59/test/data";
|
||||
|
||||
uint16_t serialize_packet_id = 28846;
|
||||
char *serialize_topic_name = reinterpret_cast<char *>(const_cast<char *>(topic.c_str()));
|
||||
int serialize_qos = 0;
|
||||
|
||||
ASSERT_EQ(mqtt_subscribe_packet_serialize(packet_buf, sizeof(packet_buf), serialize_packet_id, 1,
|
||||
&serialize_topic_name, &serialize_qos),
|
||||
sizeof(test_packet));
|
||||
ASSERT_EQ(memcmp(packet_buf, test_packet, sizeof(test_packet)), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Test suback packet.
|
||||
*
|
||||
*/
|
||||
TEST(MQTTPacketTest, SUBACK) {
|
||||
uint8_t test_packet[] = {0x90, 0x03, 0x70, 0xae, 0x00};
|
||||
|
||||
int sub_count = 0;
|
||||
uint16_t packet_id;
|
||||
int granted_qos[1];
|
||||
ASSERT_EQ(mqtt_suback_packet_deserialize(test_packet, sizeof(test_packet), 1, &sub_count, &packet_id, granted_qos),
|
||||
MQTT_RET_PACKET_OK);
|
||||
ASSERT_EQ(sub_count, 1);
|
||||
ASSERT_EQ(packet_id, 28846);
|
||||
ASSERT_EQ(granted_qos[0], 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Test unsubscribe packet.
|
||||
*
|
||||
*/
|
||||
TEST(MQTTPacketTest, UNSUBSCRIBE) {
|
||||
uint8_t test_packet[] = {
|
||||
0xa2, 0x18, 0x35, 0x74, 0x00, 0x14, 0x4a, 0x4e, 0x4c, 0x52, 0x57, 0x4f, 0x33,
|
||||
0x54, 0x35, 0x39, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x64, 0x61, 0x74, 0x61,
|
||||
};
|
||||
|
||||
uint8_t packet_buf[128];
|
||||
uint16_t packet_id = 13684;
|
||||
std::string topic = "JNLRWO3T59/test/data";
|
||||
char *topic_name = reinterpret_cast<char *>(const_cast<char *>(topic.c_str()));
|
||||
|
||||
ASSERT_EQ(mqtt_unsubscribe_packet_serialize(packet_buf, sizeof(packet_buf), packet_id, 1, &topic_name),
|
||||
sizeof(test_packet));
|
||||
ASSERT_EQ(memcmp(packet_buf, test_packet, sizeof(test_packet)), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Test connack packet.
|
||||
*
|
||||
*/
|
||||
TEST(MQTTPacketTest, UNSUBACK) {
|
||||
uint8_t test_packet[] = {0xb0, 0x02, 0x92, 0x6b};
|
||||
|
||||
uint16_t packet_id;
|
||||
|
||||
ASSERT_EQ(mqtt_unsuback_packet_deserialize(test_packet, sizeof(test_packet), &packet_id), MQTT_RET_PACKET_OK);
|
||||
ASSERT_EQ(packet_id, 37483);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Test pingreq packet.
|
||||
*
|
||||
*/
|
||||
TEST(MQTTPacketTest, PINGREQ) {
|
||||
uint8_t test_packet[] = {0xc0, 0x00};
|
||||
|
||||
uint8_t packet_buf[128];
|
||||
ASSERT_EQ(mqtt_pingreq_packet_serialize(packet_buf, sizeof(packet_buf)), sizeof(test_packet));
|
||||
ASSERT_EQ(memcmp(packet_buf, test_packet, sizeof(test_packet)), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Test pingresp packet.
|
||||
*
|
||||
*/
|
||||
TEST(MQTTPacketTest, PINGRESP) {
|
||||
uint8_t test_packet[] = {0xd0, 0x00};
|
||||
|
||||
ASSERT_EQ(mqtt_pingresp_packet_deserialize(test_packet, sizeof(test_packet)), MQTT_RET_PACKET_OK);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Test disconnect packet.
|
||||
*
|
||||
*/
|
||||
TEST(MQTTPacketTest, DISCONNECT) {
|
||||
uint8_t test_packet[] = {0xe0, 0x00};
|
||||
|
||||
uint8_t packet_buf[128];
|
||||
ASSERT_EQ(mqtt_disconnect_packet_serialize(packet_buf, sizeof(packet_buf)), sizeof(test_packet));
|
||||
ASSERT_EQ(memcmp(packet_buf, test_packet, sizeof(test_packet)), 0);
|
||||
}
|
||||
|
||||
} // namespace mqtt_packet_unittest
|
@@ -0,0 +1,10 @@
|
||||
file(GLOB src ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c)
|
||||
set(inc ${CMAKE_CURRENT_SOURCE_DIR}/inc/)
|
||||
|
||||
set(src_common ${src_common} ${src} PARENT_SCOPE)
|
||||
set(inc_common ${inc_common} ${inc} PARENT_SCOPE)
|
||||
|
||||
if( ${CONFIG_IOT_TEST} STREQUAL "ON")
|
||||
file(GLOB src_unit_test ${CMAKE_CURRENT_SOURCE_DIR}/test/*.cc)
|
||||
set(src_test ${src_test} ${src_unit_test} PARENT_SCOPE)
|
||||
endif()
|
@@ -0,0 +1,104 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 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.
|
||||
*
|
||||
* @file utils_downloader.h
|
||||
* @brief
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-10-20
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-10-20 <td>1.0 <td>fancyxu <td>first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#ifndef IOT_HUB_DEVICE_C_SDK_COMMON_UTILS_INC_UTILS_DOWNLOADER_H_
|
||||
#define IOT_HUB_DEVICE_C_SDK_COMMON_UTILS_INC_UTILS_DOWNLOADER_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
/**
|
||||
* @brief Downloader status.
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
UTILS_DOWNLOADER_STATUS_SUCCESS = 0,
|
||||
UTILS_DOWNLOADER_STATUS_BREAK_POINT_FAILED,
|
||||
UTILS_DOWNLOADER_STATUS_DATA_DOWNLOAD_FAILED,
|
||||
UTILS_DOWNLOADER_STATUS_NETWORK_FAILED
|
||||
} UtilsDownloaderStatus;
|
||||
|
||||
/**
|
||||
* @brief Downloader function.
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
// memory
|
||||
void* (*downloader_malloc)(size_t len); /**< user malloc */
|
||||
void (*downloader_free)(void* val); /**< user free */
|
||||
|
||||
// break point
|
||||
int (*break_point_init)(void* usr_data); /**< init break point, read from flash or file */
|
||||
void (*break_point_deinit)(void* usr_data); /**< deinit break point */
|
||||
int (*break_point_set)(void* usr_data); /**< set break point structure */
|
||||
int (*break_point_save)(void* usr_data); /**< save break point in flash or file */
|
||||
int (*break_point_check)(void* usr_data); /**< check break point valid */
|
||||
int (*break_point_restore)(void* usr_data); /**< restore break point */
|
||||
|
||||
// data download
|
||||
int (*data_download_init)(void* usr_data); /**< init data download, such as http connect */
|
||||
void (*data_download_deinit)(void* usr_data); /**< deinit data download, such as http disconnect */
|
||||
|
||||
int (*data_download_is_over)(void* usr_data); /**< check if download finish */
|
||||
int (*data_download_recv)(void* usr_data); /**< recv data, such as http recv */
|
||||
int (*data_download_save)(void* usr_data); /**< save data, such as write firmware to flash */
|
||||
int (*data_download_finish)(void* usr_data, UtilsDownloaderStatus status); /**< process result */
|
||||
} UtilsDownloaderFunction;
|
||||
|
||||
/**
|
||||
* @brief Init downloader.
|
||||
*
|
||||
* @param[in] func download function should implement
|
||||
* @param[in] usr_data user data using in function
|
||||
* @return pointer to downloader
|
||||
*/
|
||||
void* utils_downloader_init(UtilsDownloaderFunction func, void* usr_data);
|
||||
|
||||
/**
|
||||
* @brief Process download using function.
|
||||
*
|
||||
* @param[in,out] handle pointer to downloader
|
||||
* @return -1 for fail, others see data_download_finish
|
||||
*/
|
||||
int utils_downloader_process(void* handle);
|
||||
|
||||
/**
|
||||
* @brief Deinit downloader.
|
||||
*
|
||||
* @param[in,out] handle pointer to downloader
|
||||
*/
|
||||
void utils_downloader_deinit(void* handle);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // IOT_HUB_DEVICE_C_SDK_COMMON_UTILS_INC_UTILS_DOWNLOADER_H_
|
@@ -0,0 +1,146 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 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.
|
||||
*
|
||||
* @file utils_json.h
|
||||
* @brief
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-07-24
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-07-24 <td>1.0 <td>fancyxu <td>first commit
|
||||
* <tr><td>2021-07-29 <td>1.1 <td>fancyxu <td>fix bug and add utils_json_value_data_get
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#ifndef IOT_HUB_DEVICE_C_SDK_COMMON_UTILS_INC_UTILS_JSON_H_
|
||||
#define IOT_HUB_DEVICE_C_SDK_COMMON_UTILS_INC_UTILS_JSON_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
/**
|
||||
* @brief Json value type
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
UTILS_JSON_VALUE_TYPE_INT32 = 0,
|
||||
UTILS_JSON_VALUE_TYPE_INT64,
|
||||
UTILS_JSON_VALUE_TYPE_UINT32,
|
||||
UTILS_JSON_VALUE_TYPE_UINT64,
|
||||
UTILS_JSON_VALUE_TYPE_FLOAT,
|
||||
UTILS_JSON_VALUE_TYPE_DOUBLE,
|
||||
UTILS_JSON_VALUE_TYPE_BOOLEAN,
|
||||
} UtilsJsonValueType;
|
||||
|
||||
/**
|
||||
* @brief Json array result
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
UTILS_JSON_ARRAY_ITER_CONTINUE,
|
||||
UTILS_JSON_ARRAY_ITER_STOP,
|
||||
} UtilsJsonArrayIterResult;
|
||||
|
||||
/**
|
||||
* @brief Json value
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
const char *value;
|
||||
int value_len;
|
||||
} UtilsJsonValue;
|
||||
|
||||
/**
|
||||
* @brief Get value from json string. Not strict, just for iot scene, we suppose all the string is valid json.
|
||||
*
|
||||
* @param[in] key key in json, support nesting with '.'
|
||||
* @param[in] key_len key len
|
||||
* @param[in] src json string
|
||||
* @param[in] src_len src length
|
||||
* @param[out] value value
|
||||
* @return 0 for success
|
||||
*/
|
||||
int utils_json_value_get(const char *key, int key_len, const char *src, int src_len, UtilsJsonValue *value);
|
||||
|
||||
/**
|
||||
* @brief Get data of value with type.
|
||||
*
|
||||
* @param[in] value @see UtilsJsonValue
|
||||
* @param[in] type value type, string can use value directly @see UtilsJsonValueType
|
||||
* @param[out] data data pointer, user should match the type
|
||||
* @return 0 for success
|
||||
*/
|
||||
int utils_json_value_data_get(UtilsJsonValue value, UtilsJsonValueType type, void *data);
|
||||
|
||||
/**
|
||||
* @brief Return unsigned int value of key in json.
|
||||
*
|
||||
* @param[in] key key in json, support nesting with '.'
|
||||
* @param[in] key_len key len
|
||||
* @param[in] src json string
|
||||
* @param[in] src_len src length
|
||||
* @param[out] data data value
|
||||
* @return 0 for success
|
||||
*/
|
||||
int utils_json_get_uint32(const char *key, int key_len, const char *src, int src_len, uint32_t *data);
|
||||
|
||||
/**
|
||||
* @brief Return int value of key in json.
|
||||
*
|
||||
* @param[in] key key in json, support nesting with '.'
|
||||
* @param[in] key_len key len
|
||||
* @param[in] src json string
|
||||
* @param[in] src_len src length
|
||||
* @param[out] data data value
|
||||
* @return 0 for success
|
||||
*/
|
||||
int utils_json_get_int32(const char *key, int key_len, const char *src, int src_len, int32_t *data);
|
||||
|
||||
/**
|
||||
* @brief Remove '\\' in json string.
|
||||
*
|
||||
* @param[in,out] src string to transfer
|
||||
* @param[in] src_len string len
|
||||
* @return length after transfer
|
||||
*/
|
||||
int utils_json_strip_transfer(char *src, int src_len);
|
||||
|
||||
/**
|
||||
* @brief Parse array object, assume array json is legal, src should be like "[12, 456]", this function will split array
|
||||
* according to array_elem_type, obj_cb will be called for each elements.
|
||||
*
|
||||
* @param[in] src array string
|
||||
* @param[in] src_len length of src
|
||||
* @param[in] obj_cb callback to deal with array element
|
||||
* @param[in] arg argument passed to the obj_cb
|
||||
*/
|
||||
void utils_json_array_parse(const char *src, int src_len, UtilsJsonArrayIterResult (*obj_cb)(const char *, int, void *),
|
||||
void *arg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // IOT_HUB_DEVICE_C_SDK_COMMON_UTILS_INC_UTILS_JSON_H_
|
@@ -0,0 +1,156 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 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.
|
||||
*
|
||||
* @file utils_list.h
|
||||
* @brief header file for utils list
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-05-25
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-05-25 <td>1.0 <td>fancyxu <td>first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#ifndef IOT_HUB_DEVICE_C_SDK_COMMON_UTILS_INC_UTILS_LIST_H_
|
||||
#define IOT_HUB_DEVICE_C_SDK_COMMON_UTILS_INC_UTILS_LIST_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* @brief ListNode iterator direction.
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
LIST_HEAD,
|
||||
LIST_TAIL,
|
||||
} UtilsListDirection;
|
||||
|
||||
/**
|
||||
* @brief ListNode process result of OnNodeProcessHandle.
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
LIST_TRAVERSE_CONTINUE,
|
||||
LIST_TRAVERSE_BREAK,
|
||||
} UtilsListResult;
|
||||
|
||||
/**
|
||||
* @brief Utils list function.
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
void *(*list_malloc)(size_t len);
|
||||
void (*list_free)(void *val);
|
||||
|
||||
void *(*list_lock_init)(void);
|
||||
void (*list_lock)(void *lock);
|
||||
void (*list_unlock)(void *lock);
|
||||
void (*list_lock_deinit)(void *lock);
|
||||
} UtilsListFunc;
|
||||
|
||||
/**
|
||||
* @brief Default list func
|
||||
*
|
||||
*/
|
||||
#define DEFAULT_LIST_FUNCS \
|
||||
{ \
|
||||
HAL_Malloc, HAL_Free, HAL_MutexCreate, HAL_MutexLock, HAL_MutexUnlock, HAL_MutexDestroy \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Node process handle called by utils_list_process.
|
||||
*
|
||||
*/
|
||||
typedef UtilsListResult (*OnNodeProcessHandle)(void *list, void *node, void *val, void *usr_data);
|
||||
|
||||
/**
|
||||
* @brief Create list with max len, return NULL if fail.
|
||||
*
|
||||
* @param[in] func function needed by list
|
||||
* @param[in] max_len max_len of list
|
||||
* @return pointer to list, NULL for failed
|
||||
*/
|
||||
void *utils_list_create(UtilsListFunc func, int max_len);
|
||||
|
||||
/**
|
||||
* @brief Destroy list.
|
||||
*
|
||||
* @param[in] list pointer to list
|
||||
*/
|
||||
void utils_list_destroy(void *list);
|
||||
|
||||
/**
|
||||
* @brief Clear the list
|
||||
*
|
||||
* @param list
|
||||
*/
|
||||
void utils_list_clear(void *list);
|
||||
|
||||
/**
|
||||
* @brief Get list len.
|
||||
*
|
||||
* @param[in] list pointer to list
|
||||
* @return len of list
|
||||
*/
|
||||
int utils_list_len_get(void *list);
|
||||
|
||||
/**
|
||||
* @brief Push the node to list tail, return NULL if node invalid.
|
||||
*
|
||||
* @param[in] list pointer to list
|
||||
* @param[in] val value needed to push to list
|
||||
* @return pointer to node, NULL for failed
|
||||
*/
|
||||
void *utils_list_push(void *list, void *val);
|
||||
|
||||
/**
|
||||
* @brief Pop the val from list head, return NULL if list empty.
|
||||
*
|
||||
* @param[in] list pointer to list
|
||||
* @return val in the head node
|
||||
*/
|
||||
void *utils_list_pop(void *list);
|
||||
|
||||
/**
|
||||
* @brief Delete the node in list and release the resource.
|
||||
*
|
||||
* @param[in] list pointer to list
|
||||
* @param[in] node pointer to node needed remove
|
||||
*/
|
||||
void utils_list_remove(void *list, void *node);
|
||||
|
||||
/**
|
||||
* @brief Process list using handle function.
|
||||
*
|
||||
* @param[in] list pointer to list
|
||||
* @param[in] direction direction to traverse @see UtilsListDirection
|
||||
* @param[in] handle process function @see OnNodeProcessHandle
|
||||
* @param[in,out] usr_data usr data to pass to OnNodeProcessHandle
|
||||
*/
|
||||
void utils_list_process(void *list, UtilsListDirection direction, OnNodeProcessHandle handle, void *usr_data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // IOT_HUB_DEVICE_C_SDK_COMMON_UTILS_INC_UTILS_LIST_H_
|
@@ -0,0 +1,131 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 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.
|
||||
*
|
||||
* @file utils_log.h
|
||||
* @brief header file for utils log
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-05-28
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-05-28 <td>1.0 <td>fancyxu <td>first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#ifndef IOT_HUB_DEVICE_C_SDK_COMMON_UTILS_INC_UTILS_LOG_H_
|
||||
#define IOT_HUB_DEVICE_C_SDK_COMMON_UTILS_INC_UTILS_LOG_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* @brief SDK log print/upload level.
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
LOG_LEVEL_DISABLE = 0, /**< disable log print/upload */
|
||||
LOG_LEVEL_ERROR = 1, /**< error log level */
|
||||
LOG_LEVEL_WARN = 2, /**< warning log level */
|
||||
LOG_LEVEL_INFO = 3, /**< info log level */
|
||||
LOG_LEVEL_DEBUG = 4, /**< debug log level */
|
||||
} LogLevel;
|
||||
|
||||
/**
|
||||
* @brief User's self defined log handler callback.
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
void *(*log_malloc)(size_t len);
|
||||
void (*log_free)(void *val);
|
||||
void (*log_handle)(const char *message);
|
||||
void (*log_upload)(LogLevel log_level, const char *message);
|
||||
void (*log_printf)(const char *fmt, ...);
|
||||
char *(*log_get_current_time_str)(void);
|
||||
void *(*log_mutex_create)(void);
|
||||
void (*log_mutex_lock)(void *mutex);
|
||||
void (*log_mutex_unlock)(void *mutex);
|
||||
void (*log_mutex_destroy)(void *mutex);
|
||||
} LogHandleFunc;
|
||||
|
||||
/**
|
||||
* @brief Default log func
|
||||
*
|
||||
*/
|
||||
#define DEFAULT_LOG_HANDLE_FUNCS \
|
||||
{ \
|
||||
HAL_Malloc, HAL_Free, NULL, NULL, HAL_Printf, HAL_Timer_Current, HAL_MutexCreate, HAL_MutexLock, \
|
||||
HAL_MutexUnlock, HAL_MutexDestroy \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Init log with func, log level, max log size.
|
||||
*
|
||||
* @param[in] func function should be implement for utils log
|
||||
* @param[in] log_level @see LogLevel
|
||||
* @param[in] max_log_size max size of log to print
|
||||
* @return 0 for success
|
||||
*/
|
||||
int utils_log_init(LogHandleFunc func, LogLevel log_level, int max_log_size);
|
||||
|
||||
/**
|
||||
* @brief Deinit log.
|
||||
*
|
||||
*/
|
||||
void utils_log_deinit(void);
|
||||
|
||||
/**
|
||||
* @brief Set log level.
|
||||
*
|
||||
* @param log_level @see LogLevel
|
||||
*/
|
||||
void utils_log_set_level(LogLevel log_level);
|
||||
|
||||
/**
|
||||
* @brief Get log level.
|
||||
*
|
||||
* @return @see LogLevel
|
||||
*/
|
||||
LogLevel utils_log_get_level(void);
|
||||
|
||||
/**
|
||||
* @brief Generate log if level higher than set.
|
||||
*
|
||||
* @param[in] file file path
|
||||
* @param[in] func function where generate log
|
||||
* @param[in] line line of source file where genertate log
|
||||
* @param[in] level @see LogLevel
|
||||
* @param[in] fmt format of log content
|
||||
*/
|
||||
void utils_log_gen(const char *file, const char *func, const int line, const int level, const char *fmt, ...);
|
||||
|
||||
// Simple APIs for log generation in different level
|
||||
#define Log_d(fmt, ...) utils_log_gen(__FILE__, __FUNCTION__, __LINE__, LOG_LEVEL_DEBUG, fmt, ##__VA_ARGS__)
|
||||
#define Log_i(fmt, ...) utils_log_gen(__FILE__, __FUNCTION__, __LINE__, LOG_LEVEL_INFO, fmt, ##__VA_ARGS__)
|
||||
#define Log_w(fmt, ...) utils_log_gen(__FILE__, __FUNCTION__, __LINE__, LOG_LEVEL_WARN, fmt, ##__VA_ARGS__)
|
||||
#define Log_e(fmt, ...) utils_log_gen(__FILE__, __FUNCTION__, __LINE__, LOG_LEVEL_ERROR, fmt, ##__VA_ARGS__)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // IOT_HUB_DEVICE_C_SDK_COMMON_UTILS_INC_UTILS_LOG_H_
|
@@ -0,0 +1,142 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 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.
|
||||
*
|
||||
* @file utils_downloader.c
|
||||
* @brief
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-10-18
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-10-18 <td>1.0 <td>fancyxu <td>first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include "utils_downloader.h"
|
||||
|
||||
/**
|
||||
* @brief Downloader.
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
UtilsDownloaderFunction func;
|
||||
void* usr_data;
|
||||
} UtilsDownloader;
|
||||
|
||||
/**
|
||||
* @brief Init downloader.
|
||||
*
|
||||
* @param[in] func download function should implement
|
||||
* @param[in] usr_data user data using in function
|
||||
* @return pointer to downloader
|
||||
*/
|
||||
void* utils_downloader_init(UtilsDownloaderFunction func, void* usr_data)
|
||||
{
|
||||
if (!func.downloader_malloc) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
UtilsDownloader* handle = func.downloader_malloc(sizeof(UtilsDownloader));
|
||||
|
||||
if (!handle) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
handle->func = func;
|
||||
handle->usr_data = usr_data;
|
||||
return handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Process download using function.
|
||||
*
|
||||
* @param[in,out] handle pointer to downloader
|
||||
* @return -1 for fail, others see data_download_finish
|
||||
*/
|
||||
int utils_downloader_process(void* handle)
|
||||
{
|
||||
int rc = -1;
|
||||
|
||||
UtilsDownloader* downloader = handle;
|
||||
UtilsDownloaderStatus status = UTILS_DOWNLOADER_STATUS_SUCCESS;
|
||||
|
||||
if (!handle) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = downloader->func.break_point_init(downloader->usr_data);
|
||||
if (rc) {
|
||||
status = UTILS_DOWNLOADER_STATUS_BREAK_POINT_FAILED;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// if check ok, restore from break point, otherwise set break point
|
||||
rc = downloader->func.break_point_check(downloader->usr_data)
|
||||
? downloader->func.break_point_set(downloader->usr_data)
|
||||
: downloader->func.break_point_restore(downloader->usr_data);
|
||||
if (rc) {
|
||||
status = UTILS_DOWNLOADER_STATUS_BREAK_POINT_FAILED;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
rc = downloader->func.data_download_init(downloader->usr_data);
|
||||
if (rc) {
|
||||
status = UTILS_DOWNLOADER_STATUS_DATA_DOWNLOAD_FAILED;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
while (!downloader->func.data_download_is_over(downloader->usr_data)) {
|
||||
rc = downloader->func.data_download_recv(downloader->usr_data);
|
||||
if (rc < 0) {
|
||||
status = UTILS_DOWNLOADER_STATUS_NETWORK_FAILED;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
rc = downloader->func.data_download_save(downloader->usr_data);
|
||||
if (rc) {
|
||||
status = UTILS_DOWNLOADER_STATUS_DATA_DOWNLOAD_FAILED;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
rc = downloader->func.break_point_save(downloader->usr_data);
|
||||
if (rc) {
|
||||
status = UTILS_DOWNLOADER_STATUS_BREAK_POINT_FAILED;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
rc = downloader->func.data_download_finish(downloader->usr_data, status);
|
||||
downloader->func.break_point_deinit(downloader->usr_data);
|
||||
downloader->func.data_download_deinit(downloader->usr_data);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deinit downloader.
|
||||
*
|
||||
* @param[in,out] handle pointer to downloader
|
||||
*/
|
||||
void utils_downloader_deinit(void* handle)
|
||||
{
|
||||
UtilsDownloader* downloader = handle;
|
||||
|
||||
if (!handle) {
|
||||
return;
|
||||
}
|
||||
downloader->func.downloader_free(handle);
|
||||
}
|
@@ -0,0 +1,684 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 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.
|
||||
*
|
||||
* @file utils_json.c
|
||||
* @brief
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-07-25
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-07-25 <td>1.0 <td>fancyxu <td>first commit
|
||||
* <tr><td>2021-07-29 <td>1.1 <td>fancyxu <td>fix bug and add utils_json_value_data_get
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include "utils_json.h"
|
||||
|
||||
/**
|
||||
* @brief Delimiter of json
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
JSON_DELIMITER_ARRAY_BEGIN = '[',
|
||||
JSON_DELIMITER_ARRAY_END = ']',
|
||||
JSON_DELIMITER_OBJECT_BEGIN = '{',
|
||||
JSON_DELIMITER_OBJECT_END = '}',
|
||||
JSON_DELIMITER_KEY = '"',
|
||||
JSON_DELIMITER_VALUE = ':',
|
||||
JSON_DELIMITER_ELEMENT_END = ',',
|
||||
JSON_DELIMITER_TYPE_STRING = '"',
|
||||
JSON_DELIMITER_TYPE_BOOLEAN_TRUE_UPPER = 'T',
|
||||
JSON_DELIMITER_TYPE_BOOLEAN_TRUE_LOWER = 't',
|
||||
JSON_DELIMITER_TYPE_BOOLEAN_FALSE_UPPER = 'F',
|
||||
JSON_DELIMITER_TYPE_BOOLEAN_FALSE_LOWER = 'f',
|
||||
JSON_DELIMITER_TYPE_NULL_UPPER = 'N',
|
||||
JSON_DELIMITER_TYPE_NULL_LOWER = 'n',
|
||||
JSON_DELIMITER_SPACE = ' ',
|
||||
JSON_DELIMITER_NONE = 0,
|
||||
} JsonDelimiter;
|
||||
|
||||
/**
|
||||
* @brief Element type support
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
JSON_ELEMENT_ARRAY = 0,
|
||||
JSON_ELEMENT_OBJECT,
|
||||
JSON_ELEMENT_VALUE_STRING,
|
||||
JSON_ELEMENT_VALUE_NUMBER,
|
||||
JSON_ELEMENT_VALUE_BOOLEAN,
|
||||
JSON_ELEMENT_VALUE_NULL,
|
||||
JSON_ELEMENT_KEY,
|
||||
} JsonElementType;
|
||||
|
||||
/**
|
||||
* @brief Json Element
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
JsonElementType type;
|
||||
char *pos_begin;
|
||||
char *pos_end;
|
||||
int element_len;
|
||||
} JsonElement;
|
||||
|
||||
#define json_key JsonElement
|
||||
#define json_value JsonElement
|
||||
|
||||
/**
|
||||
* @brief Is delimiter begin or end matched
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
int (*is_begin_matched)(char *str);
|
||||
int (*is_end_matched)(char *str, int *offset);
|
||||
} IsDelimiterMatched;
|
||||
|
||||
/**
|
||||
* @brief For array type, when meets '[', means depth++
|
||||
*
|
||||
* @param[in] str pointer to char
|
||||
* @return true for matched
|
||||
*/
|
||||
static int _is_array_begin_matched(char *str)
|
||||
{
|
||||
return *str == JSON_DELIMITER_ARRAY_BEGIN;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief For array type, when meets ']', means depth--
|
||||
*
|
||||
* @param[in] str pointer to char
|
||||
* @param[out] offset offset for pointer to move
|
||||
* @return true for matched
|
||||
*/
|
||||
static int _is_array_end_matched(char *str, int *offset)
|
||||
{
|
||||
return *str == JSON_DELIMITER_ARRAY_END;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief For object type, when meets '{', means depth++
|
||||
*
|
||||
* @param[in] str pointer to char
|
||||
* @return true for matched
|
||||
*/
|
||||
static int _is_object_begin_matched(char *str)
|
||||
{
|
||||
return *str == JSON_DELIMITER_OBJECT_BEGIN;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief For object type, when meets '}', means depth--
|
||||
*
|
||||
* @param[in] str pointer to char
|
||||
* @param[out] offset offset for pointer to move
|
||||
* @return true for matched
|
||||
*/
|
||||
static int _is_object_end_matched(char *str, int *offset)
|
||||
{
|
||||
return *str == JSON_DELIMITER_OBJECT_END;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief For string type, when meets '"', but not "\"", means the end
|
||||
*
|
||||
* @param[in] str pointer to char
|
||||
* @param[out] offset offset for pointer to move
|
||||
* @return true for matched
|
||||
*/
|
||||
static int _is_string_end_matched(char *str, int *offset)
|
||||
{
|
||||
return *str == '"' && *(str - 1) != '\\';
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief For number type, when meets not '0~9', '.' or 'f' for float, means the end
|
||||
*
|
||||
* @param[in] str pointer to char
|
||||
* @param[out] offset offset for pointer to move
|
||||
* @return true for matched
|
||||
*/
|
||||
static int _is_number_end_matched(char *str, int *offset)
|
||||
{
|
||||
return (*str < '0' || *str > '9') && *str != '.' && *str != 'f' && *str != '-';
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief For boolen type, when meets "true", "True", "false" or "False", means the end
|
||||
*
|
||||
* @param[in] str pointer to char
|
||||
* @param[out] offset offset for pointer to move, -1 means error
|
||||
* @return true for matched
|
||||
*/
|
||||
static int _is_boolean_end_matched(char *str, int *offset)
|
||||
{
|
||||
*offset = !strncmp(str, "true", 4) || !strncmp(str, "TRUE", 4) ? 4 : *offset;
|
||||
*offset = !strncmp(str, "false", 5) || !strncmp(str, "FALSE", 5) ? 5 : *offset;
|
||||
if (*offset == 0) {
|
||||
*offset = -1; // -1 means error
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief For boolen type, when meets "null", "NULL", means the end
|
||||
*
|
||||
* @param[in] str pointer to char
|
||||
* @param[out] offset offset for pointer to move, -1 means error
|
||||
* @return true for matched
|
||||
*/
|
||||
static int _is_null_end_matched(char *str, int *offset)
|
||||
{
|
||||
*offset = !strncmp(str, "null", 4) || !strncmp(str, "NULL", 4) ? 4 : 0;
|
||||
if (*offset == 0) {
|
||||
*offset = -1; // -1 means error
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Table of matched function of elements
|
||||
*
|
||||
*/
|
||||
static IsDelimiterMatched sg_element_matched_func[] = {
|
||||
{_is_array_begin_matched, _is_array_end_matched}, // JSON_ELEMENT_ARRAY
|
||||
{_is_object_begin_matched, _is_object_end_matched}, // JSON_ELEMENT_OBJECT
|
||||
{NULL, _is_string_end_matched}, // JSON_ELEMENT_VALUE_STRING
|
||||
{NULL, _is_number_end_matched}, // JSON_ELEMENT_VALUE_NUMBER
|
||||
{NULL, _is_boolean_end_matched}, // JSON_ELEMENT_VALUE_BOOLEAN
|
||||
{NULL, _is_null_end_matched}, // JSON_ELEMENT_VALUE_NULL
|
||||
{NULL, NULL}, // JSON_ELEMENT_KEY
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Increase pos and decrease remain len
|
||||
*
|
||||
* @param[in,out] pos current pos
|
||||
* @param[in,out] len current len
|
||||
*/
|
||||
static void _increase_pos(char **pos, int *len)
|
||||
{
|
||||
(*pos)++;
|
||||
(*len)--;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Find delimiter and return pos
|
||||
*
|
||||
* @param[in] ch delimiter character
|
||||
* @param[in] spilt_char if 0 then spilt nothing but delimiter
|
||||
* @param[in] str input string
|
||||
* @param[in,out] remain_len remaining length
|
||||
* @return NULL for failed
|
||||
*/
|
||||
static char *_find_json_delimiter(JsonDelimiter ch, char spilt_char, const char *str, int *remain_len)
|
||||
{
|
||||
char *pos = NULL;
|
||||
char *src = (char *)str;
|
||||
|
||||
while ((*remain_len) > 0 && src && *src) {
|
||||
if ((!spilt_char && *src != ch) || (spilt_char && *src == spilt_char) || (*src == '\n')) {
|
||||
_increase_pos(&src, remain_len);
|
||||
continue;
|
||||
}
|
||||
|
||||
pos = (*src == ch) ? src : 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Find element end
|
||||
*
|
||||
* @param[in] is_begin_matched using in nesting
|
||||
* @param[in] is_end_matched match end
|
||||
* @param[in] str input string
|
||||
* @param[in,out] remain_len remaining length
|
||||
* @return NULL for failed
|
||||
*/
|
||||
static char *_find_json_value_element_end(int (*is_begin_matched)(char *), int (*is_end_matched)(char *, int *),
|
||||
char *str, int *remain_len)
|
||||
{
|
||||
int offset = 0;
|
||||
int depth = 0;
|
||||
char *pos = NULL;
|
||||
|
||||
while (str && *str) {
|
||||
offset = 0;
|
||||
|
||||
if (*remain_len <= 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (is_begin_matched) {
|
||||
if (is_begin_matched(str)) {
|
||||
depth++;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_end_matched(str, &offset)) {
|
||||
if (!depth) {
|
||||
if (offset < 0) {
|
||||
break; // failed
|
||||
}
|
||||
pos = str + offset;
|
||||
(*remain_len) -= offset;
|
||||
break;
|
||||
}
|
||||
|
||||
if (depth > 0) {
|
||||
depth--;
|
||||
}
|
||||
}
|
||||
|
||||
str++;
|
||||
(*remain_len)--;
|
||||
}
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get json key from string
|
||||
*
|
||||
* @param[in] node_begin string begin
|
||||
* @param[in,out] remain_len remaining length
|
||||
* @param[out] key key node
|
||||
* @return 0 for success
|
||||
*/
|
||||
static int _get_json_key(char *node_begin, int *remain_len, json_key *key)
|
||||
{
|
||||
char *key_begin = _find_json_delimiter(JSON_DELIMITER_KEY, JSON_DELIMITER_SPACE, node_begin, remain_len);
|
||||
if (!key_begin) {
|
||||
return -1;
|
||||
}
|
||||
_increase_pos(&key_begin, remain_len);
|
||||
|
||||
char *key_end = _find_json_delimiter(JSON_DELIMITER_KEY, JSON_DELIMITER_NONE, key_begin, remain_len);
|
||||
if (!key_end) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
key->type = JSON_ELEMENT_KEY;
|
||||
key->pos_begin = key_begin;
|
||||
key->pos_end = key_end;
|
||||
key->element_len = key_end - key_begin;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get json value from key end
|
||||
*
|
||||
* @param[in] key_end end of key
|
||||
* @param[in,out] remain_len remaining length
|
||||
* @param[out] value value node
|
||||
* @return 0 for success
|
||||
*/
|
||||
static int _get_json_value(char *key_end, int *remain_len, json_value *value)
|
||||
{
|
||||
char *value_begin = _find_json_delimiter(JSON_DELIMITER_VALUE, JSON_DELIMITER_SPACE, key_end, remain_len);
|
||||
if (!value_begin) {
|
||||
return -1;
|
||||
}
|
||||
_increase_pos(&value_begin, remain_len);
|
||||
|
||||
// filter all the space
|
||||
while (*value_begin == ' ') {
|
||||
value_begin++;
|
||||
(*remain_len)--;
|
||||
if (*remain_len <= 0) {
|
||||
return -1;
|
||||
}
|
||||
};
|
||||
|
||||
JsonElementType type;
|
||||
|
||||
char *value_end = NULL;
|
||||
|
||||
switch (*value_begin) {
|
||||
case JSON_DELIMITER_ARRAY_BEGIN:
|
||||
// should + 1 for escape first [
|
||||
_increase_pos(&value_begin, remain_len);
|
||||
type = JSON_ELEMENT_ARRAY;
|
||||
break;
|
||||
case JSON_DELIMITER_OBJECT_BEGIN:
|
||||
// should + 1 for escape first {
|
||||
_increase_pos(&value_begin, remain_len);
|
||||
type = JSON_ELEMENT_OBJECT;
|
||||
break;
|
||||
case JSON_DELIMITER_TYPE_STRING:
|
||||
// should + 1 for escape first "
|
||||
_increase_pos(&value_begin, remain_len);
|
||||
type = JSON_ELEMENT_VALUE_STRING;
|
||||
break;
|
||||
case JSON_DELIMITER_TYPE_BOOLEAN_TRUE_UPPER:
|
||||
case JSON_DELIMITER_TYPE_BOOLEAN_TRUE_LOWER:
|
||||
case JSON_DELIMITER_TYPE_BOOLEAN_FALSE_UPPER:
|
||||
case JSON_DELIMITER_TYPE_BOOLEAN_FALSE_LOWER:
|
||||
type = JSON_ELEMENT_VALUE_BOOLEAN;
|
||||
break;
|
||||
case JSON_DELIMITER_TYPE_NULL_UPPER:
|
||||
case JSON_DELIMITER_TYPE_NULL_LOWER:
|
||||
type = JSON_ELEMENT_VALUE_NULL;
|
||||
break;
|
||||
default:
|
||||
type = JSON_ELEMENT_VALUE_NUMBER;
|
||||
break;
|
||||
}
|
||||
|
||||
value_end = _find_json_value_element_end(sg_element_matched_func[type].is_begin_matched,
|
||||
sg_element_matched_func[type].is_end_matched, value_begin, remain_len);
|
||||
if (!value_end) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int is_object_or_array = (type == JSON_ELEMENT_ARRAY || type == JSON_ELEMENT_OBJECT);
|
||||
int is_object_or_array_or_string = (is_object_or_array || type == JSON_ELEMENT_VALUE_STRING);
|
||||
|
||||
value->type = type;
|
||||
value->pos_begin = value_begin - is_object_or_array; // include '[' or '{'
|
||||
value->element_len = value_end - value->pos_begin + is_object_or_array; // include ']' or '}'
|
||||
value->pos_end = value_end + is_object_or_array_or_string; // filter '"' , ']' or '}'
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get value by key from json string
|
||||
*
|
||||
* @param[in] str json string
|
||||
* @param[in] str_len strign length
|
||||
* @param[in] key key
|
||||
* @param[in] key_len key length
|
||||
* @param[out] value value
|
||||
* @return 0 for success
|
||||
*/
|
||||
int json_get_value_by_key(const char *str, int str_len, const char *key, int key_len, json_value *value)
|
||||
{
|
||||
int rc = 0;
|
||||
char *object_begin = NULL;
|
||||
json_key key_found = {JSON_ELEMENT_KEY, 0, 0, 0};
|
||||
|
||||
object_begin = _find_json_delimiter(JSON_DELIMITER_OBJECT_BEGIN, JSON_DELIMITER_SPACE, (char *)str, &str_len);
|
||||
if (!object_begin) {
|
||||
return -1;
|
||||
}
|
||||
_increase_pos(&object_begin, &str_len);
|
||||
|
||||
do {
|
||||
rc = _get_json_key(object_begin, &str_len, &key_found);
|
||||
if (rc) {
|
||||
break;
|
||||
}
|
||||
_increase_pos(&key_found.pos_end, &str_len);
|
||||
|
||||
rc = _get_json_value(key_found.pos_end, &str_len, value);
|
||||
if (rc) {
|
||||
break;
|
||||
}
|
||||
|
||||
// NULL of object begin can be ok for reach the end of str
|
||||
object_begin = _find_json_delimiter(JSON_DELIMITER_ELEMENT_END, JSON_DELIMITER_SPACE, value->pos_end, &str_len);
|
||||
if (object_begin) {
|
||||
_increase_pos(&object_begin, &str_len);
|
||||
}
|
||||
} while (key_len != key_found.element_len || strncmp(key, key_found.pos_begin, key_found.element_len));
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get value from json string. Not strict, just for iot scene, we suppose all the string is valid json.
|
||||
*
|
||||
* @param[in] key key in json, support nesting with '.'
|
||||
* @param[in] key_len key len
|
||||
* @param[in] src json string
|
||||
* @param[in] src_len src length
|
||||
* @param[out] value value
|
||||
* @return 0 for success
|
||||
*/
|
||||
int utils_json_value_get(const char *key, int key_len, const char *src, int src_len, UtilsJsonValue *value)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
char *delim = NULL;
|
||||
char *src_iter = (char *)src;
|
||||
int src_iter_len = src_len;
|
||||
char *key_iter = (char *)key;
|
||||
char *key_next = NULL;
|
||||
int key_next_len = 0;
|
||||
|
||||
json_value value_tmp;
|
||||
|
||||
// key can be separated by '.', such as: outer_key.(.......).inner_key
|
||||
while ((delim = strchr(key_iter, '.')) != NULL) {
|
||||
key_next = key_iter;
|
||||
key_next_len = delim - key_iter;
|
||||
|
||||
rc = json_get_value_by_key(src_iter, src_iter_len, key_next, key_next_len, &value_tmp);
|
||||
if (rc) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
src_iter = value_tmp.pos_begin;
|
||||
src_iter_len = value_tmp.element_len;
|
||||
key_iter = delim + 1;
|
||||
}
|
||||
|
||||
// found inner key and get value
|
||||
rc = json_get_value_by_key(src_iter, src_iter_len, key_iter, key_len + key - key_iter, &value_tmp);
|
||||
if (rc) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
value->value = value_tmp.pos_begin;
|
||||
value->value_len = value_tmp.element_len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get data of value with type.
|
||||
*
|
||||
* @param[in] value @see UtilsJsonValue
|
||||
* @param[in] type value type, string can use value directly. @see UtilsJsonValueType
|
||||
* @param[out] data data pointer, user should
|
||||
* @return 0 for success
|
||||
*/
|
||||
int utils_json_value_data_get(UtilsJsonValue value, UtilsJsonValueType type, void *data)
|
||||
{
|
||||
char value_tmp[32] = {0};
|
||||
|
||||
if (value.value_len > sizeof(value_tmp) || !value.value_len) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
strncpy(value_tmp, value.value, value.value_len);
|
||||
|
||||
switch (type) {
|
||||
case UTILS_JSON_VALUE_TYPE_INT32:
|
||||
return !(sscanf(value_tmp, "%" SCNi32, (int32_t *)data) == 1);
|
||||
case UTILS_JSON_VALUE_TYPE_INT64:
|
||||
return !(sscanf(value_tmp, "%" SCNi64, (int64_t *)data) == 1);
|
||||
case UTILS_JSON_VALUE_TYPE_UINT32:
|
||||
return !(sscanf(value_tmp, "%" SCNu32, (uint32_t *)data) == 1);
|
||||
case UTILS_JSON_VALUE_TYPE_UINT64:
|
||||
return !(sscanf(value_tmp, "%" SCNu64, (uint64_t *)data) == 1);
|
||||
case UTILS_JSON_VALUE_TYPE_FLOAT:
|
||||
return !(sscanf(value_tmp, "%f", (float *)data) == 1);
|
||||
case UTILS_JSON_VALUE_TYPE_DOUBLE:
|
||||
return !(sscanf(value_tmp, "%lf", (double *)data) == 1);
|
||||
case UTILS_JSON_VALUE_TYPE_BOOLEAN:
|
||||
*(int *)data = strcmp(value_tmp, "false");
|
||||
return 0;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return unsigned int value of key in json.
|
||||
*
|
||||
* @param[in] key key in json, support nesting with '.'
|
||||
* @param[in] key_len key len
|
||||
* @param[in] src json string
|
||||
* @param[in] src_len src length
|
||||
* @param[out] data data value
|
||||
* @return 0 for success
|
||||
*/
|
||||
int utils_json_get_uint32(const char *key, int key_len, const char *src, int src_len, uint32_t *data)
|
||||
{
|
||||
int rc;
|
||||
UtilsJsonValue tmp;
|
||||
|
||||
rc = utils_json_value_get(key, key_len, src, src_len, &tmp);
|
||||
if (rc) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = utils_json_value_data_get(tmp, UTILS_JSON_VALUE_TYPE_UINT32, data);
|
||||
if (rc) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return int value of key in json.
|
||||
*
|
||||
* @param[in] key key in json, support nesting with '.'
|
||||
* @param[in] key_len key len
|
||||
* @param[in] src json string
|
||||
* @param[in] src_len src length
|
||||
* @param[out] data data value
|
||||
* @return 0 for success
|
||||
*/
|
||||
int utils_json_get_int32(const char *key, int key_len, const char *src, int src_len, int32_t *data)
|
||||
{
|
||||
int rc;
|
||||
UtilsJsonValue tmp;
|
||||
|
||||
rc = utils_json_value_get(key, key_len, src, src_len, &tmp);
|
||||
if (rc) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = utils_json_value_data_get(tmp, UTILS_JSON_VALUE_TYPE_INT32, data);
|
||||
if (rc) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Remove '\\' in json string.
|
||||
*
|
||||
* @param[in,out] src string to transfer
|
||||
* @param[in] src_len string len
|
||||
* @return length after transfer
|
||||
*/
|
||||
int utils_json_strip_transfer(char *src, int src_len)
|
||||
{
|
||||
char *src_tmp = src;
|
||||
char *end = src + src_len;
|
||||
|
||||
while (src_tmp < end) {
|
||||
if (*src_tmp == '\\') {
|
||||
memmove(src_tmp, src_tmp + 1, end - src_tmp);
|
||||
end--;
|
||||
}
|
||||
src_tmp++;
|
||||
}
|
||||
return end - src;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Parse array object, assume array json is legal, src should be like "[12, 456]", this function will split array
|
||||
* according to array_elem_type, obj_cb will be called for each elements.
|
||||
*
|
||||
* @param[in] src array string
|
||||
* @param[in] src_len length of src
|
||||
* @param[in] obj_cb callback to deal with array element
|
||||
* @param[in] arg argument passed to the obj_cb
|
||||
*/
|
||||
void utils_json_array_parse(const char *src, int src_len, UtilsJsonArrayIterResult (*obj_cb)(const char *, int, void *),
|
||||
void *arg)
|
||||
{
|
||||
#define SKIP_SPACE(str) while (*str == ' ' && (str = str + 1))
|
||||
|
||||
if (!obj_cb) {
|
||||
return;
|
||||
}
|
||||
|
||||
const char *start = src + 1, *end = src + 1;
|
||||
|
||||
char left_delimiter, right_delimiter;
|
||||
SKIP_SPACE(start);
|
||||
switch (*start) {
|
||||
case JSON_DELIMITER_ARRAY_BEGIN:
|
||||
left_delimiter = JSON_DELIMITER_ARRAY_BEGIN;
|
||||
right_delimiter = JSON_DELIMITER_ARRAY_END;
|
||||
break;
|
||||
case JSON_DELIMITER_OBJECT_BEGIN:
|
||||
left_delimiter = JSON_DELIMITER_OBJECT_BEGIN;
|
||||
right_delimiter = JSON_DELIMITER_OBJECT_END;
|
||||
break;
|
||||
case JSON_DELIMITER_TYPE_STRING:
|
||||
left_delimiter = JSON_DELIMITER_TYPE_STRING;
|
||||
right_delimiter = JSON_DELIMITER_TYPE_STRING;
|
||||
break;
|
||||
default:
|
||||
left_delimiter = 0;
|
||||
right_delimiter = JSON_DELIMITER_ELEMENT_END;
|
||||
break;
|
||||
}
|
||||
|
||||
int remain_length = 0;
|
||||
while (end && end <= (src + src_len)) {
|
||||
SKIP_SPACE(start);
|
||||
if (left_delimiter) {
|
||||
remain_length = src + src_len - start;
|
||||
start = _find_json_delimiter(left_delimiter, JSON_DELIMITER_NONE, start, &remain_length);
|
||||
if (!start) {
|
||||
break;
|
||||
}
|
||||
if (JSON_DELIMITER_TYPE_STRING == left_delimiter) {
|
||||
start++;
|
||||
}
|
||||
}
|
||||
|
||||
remain_length = src + src_len - start;
|
||||
end = _find_json_delimiter(right_delimiter, JSON_DELIMITER_NONE, start, &remain_length);
|
||||
if (!end) {
|
||||
if (!left_delimiter) {
|
||||
obj_cb(start, src + src_len - start - 1, arg);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (JSON_DELIMITER_ARRAY_END == right_delimiter || JSON_DELIMITER_OBJECT_END == right_delimiter) {
|
||||
end++;
|
||||
}
|
||||
|
||||
if (UTILS_JSON_ARRAY_ITER_STOP == obj_cb(start, end - start, arg)) {
|
||||
return;
|
||||
}
|
||||
start = end + 1;
|
||||
}
|
||||
}
|
@@ -0,0 +1,338 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 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.
|
||||
*
|
||||
* @file utils_list.c
|
||||
* @brief utils list operation
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-05-25
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-05-28 <td>1.0 <td>fancyxu <td>first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include "utils_list.h"
|
||||
|
||||
/**
|
||||
* @brief Define list node.
|
||||
*
|
||||
*/
|
||||
typedef struct ListNode {
|
||||
struct ListNode *prev;
|
||||
struct ListNode *next;
|
||||
void *val;
|
||||
} ListNode;
|
||||
|
||||
/**
|
||||
* @brief Double Linked List.
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
UtilsListFunc func;
|
||||
ListNode *head;
|
||||
ListNode *tail;
|
||||
void *lock;
|
||||
int len;
|
||||
int max_len;
|
||||
} List;
|
||||
|
||||
/**
|
||||
* @brief List iterator.
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
List *list;
|
||||
ListNode *next;
|
||||
UtilsListDirection direction;
|
||||
} ListIterator;
|
||||
|
||||
/**
|
||||
* @brief Lock list.
|
||||
*
|
||||
* @param[in] list pointer to list
|
||||
*/
|
||||
static inline void _list_lock(List *list)
|
||||
{
|
||||
if (list->lock) {
|
||||
list->func.list_lock(list->lock);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unlock list.
|
||||
*
|
||||
* @param[in] list pointer to list
|
||||
*/
|
||||
static inline void _list_unlock(List *list)
|
||||
{
|
||||
if (list->lock) {
|
||||
list->func.list_unlock(list->lock);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Delete the node in list and release the resource.
|
||||
*
|
||||
* @param[in] list pointer to list
|
||||
* @param[in] node pointer to node needed remove
|
||||
*/
|
||||
static void _list_remove(void *list, void *node)
|
||||
{
|
||||
List *self = (List *)list;
|
||||
ListNode *list_node = (ListNode *)node;
|
||||
|
||||
list_node->prev ? (list_node->prev->next = list_node->next) : (self->head = list_node->next);
|
||||
|
||||
list_node->next ? (list_node->next->prev = list_node->prev) : (self->tail = list_node->prev);
|
||||
|
||||
self->func.list_free(list_node->val);
|
||||
self->func.list_free(list_node);
|
||||
|
||||
if (self->len) {
|
||||
--self->len;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create list with max len, return NULL if fail.
|
||||
*
|
||||
* @param[in] func function needed by list
|
||||
* @param[in] max_len max_len of list
|
||||
* @return pointer to list, NULL for failed
|
||||
*/
|
||||
void *utils_list_create(UtilsListFunc func, int max_len)
|
||||
{
|
||||
List *self;
|
||||
|
||||
if (max_len <= 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
self = (List *)func.list_malloc(sizeof(List));
|
||||
if (!self) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(self, 0, sizeof(List));
|
||||
|
||||
if (func.list_lock_init) {
|
||||
self->lock = func.list_lock_init();
|
||||
if (!self->lock) {
|
||||
func.list_free(self);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
self->func = func;
|
||||
self->max_len = max_len;
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Destroy list.
|
||||
*
|
||||
* @param[in] list pointer to list
|
||||
*/
|
||||
void utils_list_destroy(void *list)
|
||||
{
|
||||
utils_list_clear(list);
|
||||
List *self = (List *)list;
|
||||
if (self->lock) {
|
||||
self->func.list_lock_deinit(self->lock);
|
||||
}
|
||||
self->func.list_free(self);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clear the list.
|
||||
*
|
||||
* @param[in] list pointer to list
|
||||
*/
|
||||
void utils_list_clear(void *list)
|
||||
{
|
||||
if (!list) {
|
||||
return;
|
||||
}
|
||||
|
||||
List *self = (List *)list;
|
||||
|
||||
_list_lock(self);
|
||||
|
||||
ListNode *next;
|
||||
ListNode *curr = self->head;
|
||||
|
||||
while (self->len--) {
|
||||
next = curr->next;
|
||||
self->func.list_free(curr->val);
|
||||
self->func.list_free(curr);
|
||||
curr = next;
|
||||
}
|
||||
|
||||
_list_unlock(self);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get list len.
|
||||
*
|
||||
* @param[in] list pointer to list
|
||||
* @return len of list
|
||||
*/
|
||||
int utils_list_len_get(void *list)
|
||||
{
|
||||
List *self = (List *)list;
|
||||
return self->len;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Push the node to list tail, return NULL if node invalid.
|
||||
*
|
||||
* @param[in] list pointer to list
|
||||
* @param[in] val value needed to push to list
|
||||
* @return pointer to node, NULL for failed
|
||||
*/
|
||||
void *utils_list_push(void *list, void *val)
|
||||
{
|
||||
List *self = (List *)list;
|
||||
|
||||
_list_lock(self);
|
||||
|
||||
if (!val || self->len >= self->max_len) {
|
||||
_list_unlock(self);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ListNode *node;
|
||||
node = self->func.list_malloc(sizeof(ListNode));
|
||||
if (!node) {
|
||||
_list_unlock(self);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
node->prev = NULL;
|
||||
node->next = NULL;
|
||||
node->val = val;
|
||||
|
||||
if (self->len) {
|
||||
node->prev = self->tail;
|
||||
node->next = NULL;
|
||||
self->tail->next = node;
|
||||
self->tail = node;
|
||||
} else {
|
||||
self->head = self->tail = node;
|
||||
node->prev = node->next = NULL;
|
||||
}
|
||||
|
||||
++self->len;
|
||||
|
||||
_list_unlock(self);
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Pop the value from list head, return NULL if list empty.
|
||||
*
|
||||
* @param[in] list pointer to list
|
||||
* @return value in the head node
|
||||
*/
|
||||
void *utils_list_pop(void *list)
|
||||
{
|
||||
List *self = (List *)list;
|
||||
ListNode *node = NULL;
|
||||
|
||||
_list_lock(self);
|
||||
|
||||
if (!self->len) {
|
||||
_list_unlock(self);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
node = self->head;
|
||||
|
||||
if (--self->len) {
|
||||
(self->head = node->next)->prev = NULL;
|
||||
} else {
|
||||
self->head = self->tail = NULL;
|
||||
}
|
||||
|
||||
node->next = node->prev = NULL;
|
||||
|
||||
_list_unlock(self);
|
||||
|
||||
void *val = node->val;
|
||||
self->func.list_free(node);
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Delete the node in list and release the resource.
|
||||
*
|
||||
* @param[in] list pointer to list
|
||||
* @param[in] node pointer to node needed remove
|
||||
*/
|
||||
void utils_list_remove(void *list, void *node)
|
||||
{
|
||||
List *self = (List *)list;
|
||||
_list_lock(self);
|
||||
_list_remove(self, node);
|
||||
_list_unlock(self);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Process list using handle function.
|
||||
*
|
||||
* @param[in] list pointer to list
|
||||
* @param[in] direction direction to traverse
|
||||
* @param[in] handle process function @see OnNodeProcessHandle
|
||||
* @param[in,out] usr_data usr data to pass to OnNodeProcessHandle
|
||||
*/
|
||||
void utils_list_process(void *list, UtilsListDirection direction, OnNodeProcessHandle handle, void *usr_data)
|
||||
{
|
||||
int rc;
|
||||
ListNode *node = NULL;
|
||||
List *self = (List *)list;
|
||||
|
||||
_list_lock(self);
|
||||
|
||||
if (!utils_list_len_get(list)) {
|
||||
_list_unlock(self);
|
||||
return;
|
||||
}
|
||||
|
||||
ListIterator iterator = {
|
||||
.direction = direction,
|
||||
.list = self,
|
||||
.next = direction == LIST_HEAD ? self->head : self->tail,
|
||||
};
|
||||
|
||||
// traverse list to process
|
||||
while ((node = iterator.next) != NULL) {
|
||||
iterator.next = iterator.direction == LIST_HEAD ? node->next : node->prev;
|
||||
if (!node->val) {
|
||||
_list_remove(list, node);
|
||||
continue;
|
||||
}
|
||||
// process node and val
|
||||
if (handle) {
|
||||
rc = handle(list, node, node->val, usr_data);
|
||||
if (rc) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
_list_unlock(list);
|
||||
}
|
@@ -0,0 +1,178 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 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.
|
||||
*
|
||||
* @file utils_log.c
|
||||
* @brief different level log generator
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-05-28
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-05-28 <td>1.0 <td>fancyxu <td>first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include "utils_log.h"
|
||||
|
||||
static const char *LEVEL_STR[] = {"DIS", "ERR", "WRN", "INF", "DBG"};
|
||||
static LogHandleFunc sg_log_handle_func;
|
||||
static char *sg_log_buffer;
|
||||
static int sg_log_max_size;
|
||||
static void *sg_log_mutex;
|
||||
static LogLevel sg_log_print_level = LOG_LEVEL_DEBUG;
|
||||
extern LogLevel sg_log_upload_level;
|
||||
|
||||
/**
|
||||
* @brief Get file name form path.
|
||||
*
|
||||
* @param[in] path file path
|
||||
* @return file name
|
||||
*/
|
||||
static const char *_get_filename(const char *path)
|
||||
{
|
||||
#ifdef WIN32
|
||||
char ch = '\\';
|
||||
#else
|
||||
char ch = '/';
|
||||
#endif
|
||||
const char *q = strrchr(path, ch);
|
||||
if (!q) {
|
||||
q = path;
|
||||
} else {
|
||||
q++;
|
||||
}
|
||||
return q;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Init log with func, log level, max log size.
|
||||
*
|
||||
* @param[in] func function should be implement for utils log
|
||||
* @param[in] log_level @see LogLevel
|
||||
* @param[in] max_log_size max size of log to print
|
||||
* @return 0 for success
|
||||
*/
|
||||
int utils_log_init(LogHandleFunc func, LogLevel log_level, int max_log_size)
|
||||
{
|
||||
sg_log_handle_func = func;
|
||||
sg_log_print_level = log_level;
|
||||
sg_log_max_size = max_log_size;
|
||||
if (func.log_mutex_create) {
|
||||
sg_log_mutex = func.log_mutex_create();
|
||||
if (!sg_log_mutex) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (func.log_malloc) {
|
||||
sg_log_buffer = func.log_malloc(max_log_size);
|
||||
if (!sg_log_buffer) {
|
||||
if (sg_log_mutex) {
|
||||
func.log_mutex_destroy(sg_log_mutex);
|
||||
}
|
||||
}
|
||||
}
|
||||
return !sg_log_buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deinit log.
|
||||
*
|
||||
*/
|
||||
void utils_log_deinit(void)
|
||||
{
|
||||
sg_log_handle_func.log_free(sg_log_buffer);
|
||||
if (sg_log_mutex) {
|
||||
sg_log_handle_func.log_mutex_destroy(sg_log_mutex);
|
||||
}
|
||||
sg_log_mutex = NULL;
|
||||
sg_log_buffer = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set log level.
|
||||
*
|
||||
* @param log_level @see LogLevel
|
||||
*/
|
||||
void utils_log_set_level(LogLevel log_level)
|
||||
{
|
||||
sg_log_print_level = log_level;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get log level.
|
||||
*
|
||||
* @return @see LogLevel
|
||||
*/
|
||||
LogLevel utils_log_get_level(void)
|
||||
{
|
||||
return sg_log_print_level;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Generate log if level higher than set.
|
||||
*
|
||||
* @param[in] file file path
|
||||
* @param[in] func function where generate log
|
||||
* @param[in] line line of source file where genertate log
|
||||
* @param[in] level @see LogLevel
|
||||
* @param[in] fmt format of log content
|
||||
*/
|
||||
void utils_log_gen(const char *file, const char *func, const int line, const int level, const char *fmt, ...)
|
||||
{
|
||||
if (level > sg_log_print_level) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (sg_log_mutex) {
|
||||
sg_log_handle_func.log_mutex_lock(sg_log_mutex);
|
||||
}
|
||||
|
||||
/* format log content */
|
||||
const char *file_name = _get_filename(file);
|
||||
char *o = sg_log_buffer;
|
||||
|
||||
memset(sg_log_buffer, 0, sg_log_max_size);
|
||||
|
||||
o += snprintf(sg_log_buffer, sg_log_max_size, "%s|%s|%s|%s(%d): ", LEVEL_STR[level],
|
||||
sg_log_handle_func.log_get_current_time_str(), file_name, func, line);
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(o, sg_log_max_size - 2 - strlen(sg_log_buffer), fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
strncat(sg_log_buffer, "\r\n", sg_log_max_size - strlen(sg_log_buffer) - 1);
|
||||
|
||||
if (level <= sg_log_print_level) {
|
||||
if (sg_log_handle_func.log_handle) {
|
||||
sg_log_handle_func.log_handle(sg_log_buffer);
|
||||
}
|
||||
sg_log_handle_func.log_printf("%s", sg_log_buffer);
|
||||
}
|
||||
|
||||
/* append to upload buffer */
|
||||
if (sg_log_handle_func.log_upload) {
|
||||
sg_log_handle_func.log_upload(level, sg_log_buffer);
|
||||
}
|
||||
|
||||
if (sg_log_mutex) {
|
||||
sg_log_handle_func.log_mutex_unlock(sg_log_mutex);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
Language: Cpp
|
||||
BasedOnStyle: Google
|
||||
ColumnLimit: 120
|
||||
DerivePointerAlignment: true
|
||||
PointerAlignment: Left
|
||||
SortIncludes: true
|
||||
IncludeBlocks: Preserve
|
||||
IndentPPDirectives: AfterHash
|
@@ -0,0 +1,214 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 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.
|
||||
*
|
||||
* @file test_utils.cc
|
||||
* @brief unittest for utils
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-07-07
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-07-07 <td>1.0 <td>fancyxu <td>first commit
|
||||
* <tr><td>2021-07-27 <td>1.1 <td>fancyxu <td>support utils json
|
||||
* <tr><td>2021-07-29 <td>1.1 <td>fancyxu <td>rename HAL_Timer and add utils json test
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "qcloud_iot_platform.h"
|
||||
#include "utils_json.h"
|
||||
#include "utils_list.h"
|
||||
#include "utils_log.h"
|
||||
|
||||
namespace utils_unittest {
|
||||
|
||||
/**
|
||||
* @brief test fixture of utils list
|
||||
*
|
||||
*/
|
||||
class UtilsListTest : public testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
UtilsListFunc func = DEFAULT_LIST_FUNCS;
|
||||
|
||||
self_list = utils_list_create(func, 10);
|
||||
ASSERT_NE(self_list, nullptr);
|
||||
|
||||
for (int i = 0; i < 10;) {
|
||||
int *val = reinterpret_cast<int *>(HAL_Malloc(sizeof(int)));
|
||||
*val = i++;
|
||||
ASSERT_NE(utils_list_push(self_list, reinterpret_cast<void *>(val)), nullptr);
|
||||
ASSERT_EQ(utils_list_len_get(self_list), i);
|
||||
}
|
||||
}
|
||||
|
||||
void TearDown() override { utils_list_destroy(self_list); }
|
||||
|
||||
void *self_list;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Test list.
|
||||
*
|
||||
*/
|
||||
TEST_F(UtilsListTest, list) {
|
||||
ASSERT_EQ(utils_list_push(self_list, reinterpret_cast<void *>(1)), nullptr);
|
||||
for (int i = 0; i < 10; i++) {
|
||||
ASSERT_EQ(utils_list_len_get(self_list), 10 - i);
|
||||
int *val = reinterpret_cast<int *>(utils_list_pop(self_list));
|
||||
ASSERT_EQ(*val, i);
|
||||
HAL_Free(val);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Test remove list node
|
||||
*
|
||||
* @param[in,out] list pointer to list
|
||||
* @param[in,out] node pointer to node
|
||||
* @param[in,out] val pointer to val
|
||||
* @param[in,out] usr_data pointer to usr data
|
||||
* @return @see UtilsListResult
|
||||
*/
|
||||
static UtilsListResult list_process_remove(void *list, void *node, void *val, void *usr_data) {
|
||||
static int i = 0;
|
||||
if (*reinterpret_cast<int *>(val) != i++) {
|
||||
return LIST_TRAVERSE_BREAK;
|
||||
}
|
||||
utils_list_remove(list, node);
|
||||
return LIST_TRAVERSE_CONTINUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Test list process.
|
||||
*
|
||||
*/
|
||||
TEST_F(UtilsListTest, list_process) {
|
||||
utils_list_process(self_list, LIST_HEAD, list_process_remove, NULL);
|
||||
ASSERT_EQ(utils_list_len_get(self_list), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Test log.
|
||||
*
|
||||
*/
|
||||
TEST(UtilsLogTest, log) {
|
||||
LogHandleFunc func = DEFAULT_LOG_HANDLE_FUNCS;
|
||||
ASSERT_EQ(utils_log_init(func, LOG_LEVEL_DEBUG, 2048), 0);
|
||||
Log_d("Here is a debug level log test!");
|
||||
Log_i("Here is a info level log test!");
|
||||
Log_w("Here is a warning level log test!");
|
||||
Log_e("Here is a error level log test!");
|
||||
utils_log_deinit();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Test json.
|
||||
*
|
||||
*/
|
||||
TEST(UtilsJsonTest, json) {
|
||||
char test_json[] =
|
||||
"{\"str_test\":\"test\",\"int_test\":100,\"float_test\":1.210f,\"bool_test\":true,"
|
||||
"\"bool_false_test\":false,\"null_test\":null}";
|
||||
|
||||
UtilsJsonValue value;
|
||||
|
||||
int data_int = 0;
|
||||
ASSERT_EQ(utils_json_value_get("int_test", strlen("int_test"), test_json, strlen(test_json), &value), 0);
|
||||
ASSERT_EQ(utils_json_value_data_get(value, UTILS_JSON_VALUE_TYPE_INT32, &data_int), 0);
|
||||
ASSERT_EQ(data_int, 100);
|
||||
|
||||
float data_float = 0.0;
|
||||
ASSERT_EQ(utils_json_value_get("float_test", strlen("float_test"), test_json, strlen(test_json), &value), 0);
|
||||
ASSERT_EQ(utils_json_value_data_get(value, UTILS_JSON_VALUE_TYPE_FLOAT, &data_float), 0);
|
||||
ASSERT_EQ(data_float, 1.210f);
|
||||
|
||||
ASSERT_EQ(utils_json_value_get("bool_test", strlen("bool_test"), test_json, strlen(test_json), &value), 0);
|
||||
ASSERT_EQ(strncmp(value.value, "true", value.value_len), 0);
|
||||
|
||||
ASSERT_EQ(utils_json_value_get("bool_false_test", strlen("bool_false_test"), test_json, strlen(test_json), &value),
|
||||
0);
|
||||
ASSERT_EQ(strncmp(value.value, "false", value.value_len), 0);
|
||||
|
||||
ASSERT_EQ(utils_json_value_get("null_test", strlen("null_test"), test_json, strlen(test_json), &value), 0);
|
||||
ASSERT_EQ(strncmp(value.value, "null", value.value_len), 0);
|
||||
|
||||
ASSERT_EQ(utils_json_value_get("str_test", strlen("str_test"), test_json, strlen(test_json), &value), 0);
|
||||
ASSERT_EQ(strncmp(value.value, "test", value.value_len), 0);
|
||||
|
||||
char test_json_depth[] = "{\"depth_test\": {\"test\":\"test1\"}}";
|
||||
ASSERT_EQ(utils_json_value_get("depth_test.test", strlen("depth_test.test"), test_json_depth, strlen(test_json_depth),
|
||||
&value),
|
||||
0);
|
||||
ASSERT_EQ(strncmp(value.value, "test1", value.value_len), 0);
|
||||
|
||||
char test_json_before_strip[] =
|
||||
"{\\\"str_test\":\\\"test\\\",\\\"int_test\\\":100,\\\"float_test\\\":1.210f,\\\"bool_test\\\":true,"
|
||||
"\\\"bool_false_test\\\":false,\\\"null_test\\\":null}";
|
||||
ASSERT_EQ(utils_json_strip_transfer(test_json_before_strip, strlen(test_json_before_strip)), strlen(test_json));
|
||||
ASSERT_EQ(strncmp(test_json_before_strip, test_json, strlen(test_json_before_strip)), 0);
|
||||
|
||||
char test_json_break[] = "{\n\"str_test\":\"test\"\n}";
|
||||
ASSERT_EQ(utils_json_value_get("str_test", strlen("str_test"), test_json_break, strlen(test_json_break), &value), 0);
|
||||
ASSERT_EQ(strncmp(value.value, "test", value.value_len), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Test json array.
|
||||
*
|
||||
*/
|
||||
static const char *sg_dst_value[12];
|
||||
|
||||
static UtilsJsonArrayIterResult _array_handle(const char *value, int value_len, void *arg) {
|
||||
static int i = 0;
|
||||
EXPECT_EQ(0, strncmp(sg_dst_value[i++], value, value_len));
|
||||
return UTILS_JSON_ARRAY_ITER_CONTINUE;
|
||||
}
|
||||
|
||||
TEST(UtilsJsonTest, json_array) {
|
||||
int i = 0;
|
||||
char test_array_string[] = "[\"test0\",\"test1\"]";
|
||||
sg_dst_value[i++] = "test0";
|
||||
sg_dst_value[i++] = "test1";
|
||||
char test_array_int[] = "[1,2]";
|
||||
sg_dst_value[i++] = "1";
|
||||
sg_dst_value[i++] = "2";
|
||||
char test_array_float[] = "[1.20f,1.21f]";
|
||||
sg_dst_value[i++] = "1.20f";
|
||||
sg_dst_value[i++] = "1.21f";
|
||||
char test_array_bool[] = "[false,true]";
|
||||
sg_dst_value[i++] = "false";
|
||||
sg_dst_value[i++] = "true";
|
||||
char test_array_null[] = "[null,null]";
|
||||
sg_dst_value[i++] = "null";
|
||||
sg_dst_value[i++] = "null";
|
||||
char test_array_obj[] = "[{\"test\":0},{\"test\":1}]";
|
||||
sg_dst_value[i++] = "{\"test\":0}";
|
||||
sg_dst_value[i++] = "{\"test\":1}";
|
||||
|
||||
utils_json_array_parse(test_array_string, sizeof(test_array_string) - 1, _array_handle, NULL);
|
||||
utils_json_array_parse(test_array_int, sizeof(test_array_int) - 1, _array_handle, NULL);
|
||||
utils_json_array_parse(test_array_float, sizeof(test_array_float) - 1, _array_handle, NULL);
|
||||
utils_json_array_parse(test_array_bool, sizeof(test_array_bool) - 1, _array_handle, NULL);
|
||||
utils_json_array_parse(test_array_null, sizeof(test_array_null) - 1, _array_handle, NULL);
|
||||
utils_json_array_parse(test_array_obj, sizeof(test_array_obj) - 1, _array_handle, NULL);
|
||||
}
|
||||
|
||||
} // namespace utils_unittest
|
Reference in New Issue
Block a user