mbed 5.4 with sleep mode
Revision 39:4f3f7463e55f, committed 2017-05-25
- Comitter:
- ranaumarnaeem
- Date:
- Thu May 25 11:53:45 2017 +0000
- Parent:
- 38:f1bc6e867fcf
- Commit message:
- mbed 5.4 with sleep mode
Changed in this revision
--- a/Coap/coap_client.c Wed May 24 07:51:33 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1021 +0,0 @@ -/* - * Copyright (c) 2015 Keith Cullen. - * All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * @file coap_client.c - * - * @brief Source file for the FreeCoAP client library - */ - -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <stdint.h> -#include <errno.h> - -#include "coap_client.h" -#include "main.h" - -#define COAP_CLIENT_ACK_TIMEOUT_SEC 2 /**< Minimum delay to wait before retransmitting a confirmable message */ -#define COAP_CLIENT_MAX_RETRANSMIT 4 /**< Maximum number of times a confirmable message can be retransmitted */ -#define COAP_CLIENT_RESP_TIMEOUT_SEC 30 /**< Maximum amount of time to wait for a response */ - -/**************************************************************************************************** - * coap_client * - ****************************************************************************************************/ -void coap_client_destroy(coap_client_t *client) -{ - memset(client, 0, sizeof(coap_client_t)); -} - -/** - * @brief Send a message to the server - * - * @param[in,out] client Pointer to a client structure - * @param[in] msg Pointer to a message structure - * - * @returns Number of bytes sent or error code - * @retval >0 Number of bytes sent - * @retval <0 Error - */ -static int coap_client_send(coap_client_t *client, coap_msg_t *msg) -{ - int num = 0; - char buf[COAP_MSG_MAX_BUF_LEN] = {0}; - - num = coap_msg_format(msg, buf, sizeof(buf)); - if (num < 0) - { - return num; - } - num = send(client->sd, buf, num); - if (num < 0) - { - printf("send error\n"); - return -errno; - } - - return num; -} - -/** - * @brief Handle a format error in a received message - * - * Special handling for the case where a received - * message could not be parsed due to a format error. - * Extract enough information from the received message - * to form a reset message. - * - * @param[in,out] client Pointer to a client structure - * @param[in] buf Buffer containing the message - * @param[in] len length of the buffer - */ -static void coap_client_handle_format_error(coap_client_t *client, char *buf, size_t len) -{ - coap_msg_t msg = {0}; - unsigned msg_id = 0; - unsigned type = 0; - int ret = 0; - - ret = coap_msg_parse_type_msg_id(buf, len, &type, &msg_id); - if ((ret == 0) && (type == COAP_MSG_CON)) - { - coap_msg_create(&msg); - ret = coap_msg_set_type(&msg, COAP_MSG_RST); - if (ret < 0) - { - coap_msg_destroy(&msg); - return; - } - ret = coap_msg_set_msg_id(&msg, msg_id); - if (ret < 0) - { - coap_msg_destroy(&msg); - return; - } - coap_client_send(client, &msg); - coap_msg_destroy(&msg); - } -} - -/** - * @brief Receive a message from the server - * - * @param[in,out] client Pointer to a client structure - * @param[in] msg Pointer to a message structure - * - * @returns Number of bytes received or error code - * @retval >0 Number of bytes received - * @retval <0 Error - */ -static int coap_client_recv(coap_client_t *client, coap_msg_t *msg) -{ - int num = 0; - int ret = 0; - char buf[COAP_MSG_MAX_BUF_LEN] = {0}; - - num = recv(client->sd, buf, sizeof(buf)); - if (num < 0) - { - return -errno; - } - - ret = coap_msg_parse(msg, buf, num); - if (ret < 0) - { - if (ret == -EBADMSG) - { - coap_client_handle_format_error(client, buf, num); - } - return ret; - } - return num; -} - -/** - * @brief Reject a received confirmable message - * - * Send a reset message to the server. - * - * @param[in,out] client Pointer to a client structure - * @param[in] msg Pointer to a message structure - * - * @returns Operation status - * @retval 0 Success - * @retval <0 Error - */ -static int coap_client_reject_con(coap_client_t *client, coap_msg_t *msg) -{ - coap_msg_t rej = {0}; - int num = 0; - int ret = 0; - - printf("Rejecting confirmable message from host %s and port %s\n", client->server_host, client->server_port); - coap_msg_create(&rej); - ret = coap_msg_set_type(&rej, COAP_MSG_RST); - if (ret < 0) - { - coap_msg_destroy(&rej); - return ret; - } - ret = coap_msg_set_msg_id(&rej, coap_msg_get_msg_id(msg)); - if (ret < 0) - { - coap_msg_destroy(&rej); - return ret; - } - num = coap_client_send(client, &rej); - coap_msg_destroy(&rej); - if (num < 0) - { - return num; - } - return 0; -} - -/** - * @brief Reject a received non-confirmable message - * - * @param[in] client Pointer to a client structure - * @param[in] msg Pointer to a message structure - * - * @returns Operation status - * @retval 0 Success - */ -static int coap_client_reject_non(coap_client_t *client, coap_msg_t *msg) -{ - printf("Rejecting non-confirmable message from host %s and port %s\n", client->server_host, client->server_port); - return 0; -} - -/** - * @brief Reject a received acknowledgement message - * - * @param[in] client Pointer to a client structure - * @param[in] msg Pointer to a message structure - * - * @returns Operation status - * @retval 0 Success - */ -static int coap_client_reject_ack(coap_client_t *client, coap_msg_t *msg) -{ - printf("Rejecting acknowledgement message from host %s and port %s\n", client->server_host, client->server_port); - return 0; -} - -/** - * @brief Reject a received reset message - * - * @param[in] client Pointer to a client structure - * @param[in] msg Pointer to a message structure - * - * @returns Operation status - * @retval 0 Success - */ -static int coap_client_reject_reset(coap_client_t *client, coap_msg_t *msg) -{ - printf("Rejecting reset message from host %s and port %s\n", client->server_host, client->server_port); - return 0; -} - -/** - * @brief Reject a received message - * - * @param[in,out] client Pointer to a client structure - * @param[in] msg Pointer to a message structure - * - * @returns Operation status - * @retval 0 Success - * @retval <0 Error - */ -static int coap_client_reject(coap_client_t *client, coap_msg_t *msg) -{ - if (coap_msg_get_type(msg) == COAP_MSG_CON) - { - return coap_client_reject_con(client, msg); - } - else if (coap_msg_get_type(msg) == COAP_MSG_NON) - { - return coap_client_reject_non(client, msg); - } - else if (coap_msg_get_type(msg) == COAP_MSG_ACK) - { - return coap_client_reject_ack(client, msg); - } - else if (coap_msg_get_type(msg) == COAP_MSG_RST) - { - return coap_client_reject_reset(client, msg); - } - return 0; /* should never arrive here */ -} - -/** - * @brief Send an acknowledgement message to the server - * - * @param[in,out] client Pointer to a client structure - * @param[in] msg Pointer to a message structure - * - * @returns Operation status - * @retval 0 Success - * @retval <0 Error - */ -static int coap_client_send_ack(coap_client_t *client, coap_msg_t *msg) -{ - coap_msg_t ack = {0}; - int num = 0; - int ret = 0; - - printf("Acknowledging confirmable message from host %s and port %s\n", client->server_host, client->server_port); - coap_msg_create(&ack); - ret = coap_msg_set_type(&ack, COAP_MSG_ACK); - if (ret < 0) - { - coap_msg_destroy(&ack); - return ret; - } - ret = coap_msg_set_msg_id(&ack, coap_msg_get_msg_id(msg)); - if (ret < 0) - { - coap_msg_destroy(&ack); - return ret; - } - num = coap_client_send(client, &ack); - coap_msg_destroy(&ack); - if (num < 0) - { - return num; - } - return 0; -} - -/** - * @brief Compare the token values in a request message and a response message - * - * @param[in] req Pointer to the request message - * @param[in] resp Pointer to the response message - * - * @returns Comparison value - * @retval 0 the tokens are not equal - * @retval 1 the tokens are equal - */ -static int coap_client_match_token(coap_msg_t *req, coap_msg_t *resp) -{ - return ((coap_msg_get_token_len(resp) == coap_msg_get_token_len(req)) - && (memcmp(coap_msg_get_token(resp), coap_msg_get_token(req), coap_msg_get_token_len(req)) == 0)); -} - -/** - * @brief Check that all of the options in a message are acceptable - * - * For a proxy, options are acceptable if they are safe to forward or recognized or both. - * For a server, options are acceptable if they are elective or recognized or both. - * - * @param[in] msg Pointer to message structure - * - * @returns Operation status or bad option number - * @retval 0 Success - * @retval >0 Bad option number - */ -static unsigned coap_client_check_options(coap_msg_t *msg) -{ -#ifdef COAP_PROXY - return coap_msg_check_unsafe_ops(msg); -#else /* !COAP_PROXY */ - return coap_msg_check_critical_ops(msg); -#endif /* COAP_PROXY */ -} - -/** - * @brief Handle a received piggy-backed response message - * - * An acknowledgement has been received that contains - * the same token as the request. Check the response - * contained within it. - * - * @param[in,out] client Pointer to a client structure - * @param[in] resp Pointer to the response message - * - * @returns Operation status - * @retval 0 Success - * @retval <0 Error - */ -static int coap_client_handle_piggybacked_response(coap_client_t *client, coap_msg_t *resp) -{ - unsigned op_num = 0; - - op_num = coap_client_check_options(resp); - if (op_num != 0) - { - printf("Found bad option number %u in message from host %s and port %s\n", op_num, client->server_host, client->server_port); - coap_client_reject(client, resp); - return -EBADMSG; - } - printf("Received acknowledgement and response from Server\n"); - return 0; -} - -/** - * @brief Handle a received separate response message - * - * A separate response has been received that contains - * the same token as the request. Check the response - * and send an acknowledgement if necessary. - * - * @param[in,out] client Pointer to a client structure - * @param[in] resp Pointer to the response message - * - * @returns Operation status - * @retval 0 Success - * @retval <0 Error - */ -static int coap_client_handle_sep_response(coap_client_t *client, coap_msg_t *resp) -{ - unsigned op_num = 0; - - if (coap_msg_get_type(resp) == COAP_MSG_CON) - { - printf("Received confirmable response from host %s and port %s\n", client->server_host, client->server_port); - op_num = coap_client_check_options(resp); - if (op_num != 0) - { - printf("Found bad option number %u in message from host %s and port %s\n", op_num, client->server_host, client->server_port); - coap_client_reject(client, resp); - return -EBADMSG; - } - return coap_client_send_ack(client, resp); - } - else if (coap_msg_get_type(resp) == COAP_MSG_NON) - { - printf("Received non-confirmable response from host %s and port %s\n", client->server_host, client->server_port); - op_num = coap_client_check_options(resp); - if (op_num != 0) - { - printf("Found bad option number %u in message from host %s and port %s\n", op_num, client->server_host, client->server_port); - coap_client_reject(client, resp); - return -EBADMSG; - } - return 0; - } - coap_client_reject(client, resp); - return -EBADMSG; -} - -/** - * @brief Handle a separate response to a confirmable request - * - * An acknowledgement has been received. Receive the - * response and send an acknowledgement back to the server. - * - * @param[in,out] client Pointer to a client structure - * @param[in] req Pointer to the request message - * @param[out] resp Pointer to the response message - * - * @returns Operation status - * @retval 0 Success - * @retval <0 Error - */ -static int coap_client_exchange_sep(coap_client_t *client, coap_msg_t *req, coap_msg_t *resp) -{ - int num = 0; - int ret = 0; - int starttimeout,endtimeout; - - /* wait for a separate response to a confirmable request */ - printf("Expecting response from host %s and port %s\n", client->server_host, client->server_port); - while (1) - { - starttimeout = readseconds(); - while(1) - { - num = coap_client_recv(client, resp); - if (num < 0) - { - return num; - } - else if(num > 0) - { - break; - } - endtimeout = readseconds(); - if((endtimeout - starttimeout) > COAP_CLIENT_RESP_TIMEOUT_SEC) - { - printf("Timeout no data received\n"); - return -1; - } - } - - if (coap_msg_get_msg_id(resp) == coap_msg_get_msg_id(req)) - { - if (coap_msg_get_type(resp) == COAP_MSG_ACK) - { - /* message deduplication */ - printf("Received duplicate acknowledgement from host %s and port %s\n", client->server_host, client->server_port); - continue; - } - else if (coap_msg_get_type(resp) == COAP_MSG_RST) - { - return -ECONNRESET; - } - coap_client_reject(client, resp); - return -EBADMSG; - } - if (coap_client_match_token(req, resp)) - { - return coap_client_handle_sep_response(client, resp); - } - /* message deduplication */ - /* we might have received a duplicate message that was already received from the same server */ - /* reject the message and continue listening */ - ret = coap_client_reject(client, resp); - if (ret < 0 ) - { - return ret; - } - } - return 0; -} - -/** - * @brief Handle the response to a confirmable request - * - * A confirmable request has been sent to the server. - * Receive the acknowledgement and response. Send an - * acknowledgement if necessary. - * - * @param[in,out] client Pointer to a client structure - * @param[in] req Pointer to the request message - * @param[out] resp Pointer to the response message - * - * @returns Operation status - * @retval 0 Success - * @retval <0 Error - */ -static int coap_client_exchange_con(coap_client_t *client, coap_msg_t *req, coap_msg_t *resp) -{ - int num = 0; - int ret = 0; - int starttimeout,endtimeout; - /* wait for piggy-backed response in ack message - * or ack message and separate response message - */ - //printf("Expecting acknowledgement from server\r\n"); - client->num_retrans = 0; - while (1) - { - int doubletimeout = COAP_CLIENT_ACK_TIMEOUT_SEC; - starttimeout = readseconds(); - while(1) - { - num = coap_client_recv(client, resp); - if (num < 0) - { - //return num; - } - else if(num > 0) - { - break; - } - endtimeout = readseconds(); - if((endtimeout - starttimeout) > doubletimeout) - { - printf("Timeout no data received after %d seconds\n",endtimeout - starttimeout); - - if (client->num_retrans >= COAP_CLIENT_MAX_RETRANSMIT) - { - printf("Maximum retries reached no ack from server!\r\n"); - return -ETIMEDOUT; - } - - doubletimeout = 2 * doubletimeout; - starttimeout = readseconds(); - client->num_retrans++; - - printf("Retransmitting to server...\r\n"); - num = coap_client_send(client, req); - if (num < 0) - { - printf("Resending Failed to server...\r\n"); - return num; - } - } - } - - if (coap_msg_get_msg_id(resp) == coap_msg_get_msg_id(req)) - { - if (coap_msg_get_type(resp) == COAP_MSG_ACK) - { - if (coap_msg_is_empty(resp)) - { - /* received ack message, wait for separate response message */ - printf("Received acknowledgement from host %s and port %s\n", client->server_host, client->server_port); - return coap_client_exchange_sep(client, req, resp); - } - else if (coap_client_match_token(req, resp)) - { - return coap_client_handle_piggybacked_response(client, resp); - } - } - else if (coap_msg_get_type(resp) == COAP_MSG_RST) - { - return -ECONNRESET; - } - coap_client_reject(client, resp); - return -EBADMSG; - } - else if (coap_client_match_token(req, resp)) - { - /* RFC7252 - * as the underlying datagram transport may not be sequence-preserving, - * the Confirmable message carrying the response may actually arrive - * before or after the Acknowledgement message for the request; for - * the purposes of terminating the retransmission sequence, this also - * serves as an acknowledgement. - */ - return coap_client_handle_sep_response(client, resp); - } - /* message deduplication */ - /* we might have received a duplicate message that was already received from the same server */ - /* reject the message and continue listening */ - ret = coap_client_reject(client, resp); - if (ret < 0 ) - { - return ret; - } - } - return 0; -} - -/** - * @brief Handle the response to a non-confirmable request - * - * A non-confirmable request has been sent to the server. - * Receive the response. - * - * @param[in,out] client Pointer to a client structure - * @param[in] req Pointer to the request message - * @param[out] resp Pointer to the response message - * - * @returns Operation status - * @retval 0 Success - * @retval <0 Error - **/ -static int coap_client_exchange_non(coap_client_t *client, coap_msg_t *req, coap_msg_t *resp) -{ - int num = 0; - int ret = 0; - int starttimeout,endtimeout; - - printf("Expecting response from host %s and port %s\n", client->server_host, client->server_port); - while (1) - { - starttimeout = readseconds(); - while(1) - { - num = coap_client_recv(client, resp); - if (num < 0) - { - return num; - } - else if(num > 0) - { - break; - } - endtimeout = readseconds(); - if((endtimeout - starttimeout) > COAP_CLIENT_RESP_TIMEOUT_SEC) - { - printf("Timeout no data received\n"); - return -1; - } - } - - if (coap_msg_get_msg_id(resp) == coap_msg_get_msg_id(req)) - { - if (coap_msg_get_type(resp) == COAP_MSG_RST) - { - return -ECONNRESET; - } - coap_client_reject(client, resp); - return -EBADMSG; - } - if (coap_client_match_token(req, resp)) - { - return coap_client_handle_sep_response(client, resp); - } - /* message deduplication */ - /* we might have received a duplicate message that was already received from the same server */ - /* reject the message and continue listening */ - ret = coap_client_reject(client, resp); - if (ret < 0 ) - { - return ret; - } - } - return 0; -} - -int coap_client_exchange(coap_client_t *client, coap_msg_t *req, coap_msg_t *resp) -{ - unsigned char msg_id_buf[2] = {0}; - unsigned msg_id = 0; - int num = 0; - char token[4] = {0}; - int ret = 0; - - /* check for a valid request */ - if ((coap_msg_get_type(req) == COAP_MSG_ACK) - || (coap_msg_get_type(req) == COAP_MSG_RST) - || (coap_msg_get_code_class(req) != COAP_MSG_REQ)) - { - return -EINVAL; - } - - /* generate the message ID */ - coap_msg_gen_rand_str((char *)msg_id_buf, sizeof(msg_id_buf)); - msg_id = (((unsigned)msg_id_buf[1]) << 8) | (unsigned)msg_id_buf[0]; - ret = coap_msg_set_msg_id(req, msg_id); - if (ret < 0) - { - return ret; - } - - /* generate the token */ - coap_msg_gen_rand_str(token, sizeof(token)); - ret = coap_msg_set_token(req, token, sizeof(token)); - if (ret < 0) - { - return ret; - } - - num = coap_client_send(client, req); - if (num < 0) - { - return num; - } - - if (coap_msg_get_type(req) == COAP_MSG_CON) - { - return coap_client_exchange_con(client, req, resp); - } - else if (coap_msg_get_type(req) == COAP_MSG_NON) - { - return coap_client_exchange_non(client, req, resp); - } - return -EINVAL; -} -//---------------------------------------------------------------------------------------------------------------------- -//---------------------------------------------------------------------------------------------------------------------- -/** - * @brief Test an exchange with the server - * - * @param[in] data Pointer to a client test data structure - * - * @returns Test result - */ -test_result_t test_exchange_func(char* buf,int buf_len) -{ - printf("----------------------------------------\n"); - test_result_t result = PASS; - coap_client_t client = {0}; - coap_msg_t resp = {0}; - coap_msg_t req = {0}; - int ret = 0; - - test_coap_client_msg_op_t req_ops; - req_ops.num = COAP_MSG_URI_PATH; - req_ops.len = 8; - req_ops.val = (char*)"resource"; - - test_coap_client_msg_op_t resp_ops; - resp_ops.num = 0; - resp_ops.len = 0; - resp_ops.val = 0; - - test_coap_client_msg_t test1_req; - test1_req.type = COAP_MSG_CON; - test1_req.code_class = COAP_MSG_REQ; - test1_req.code_detail = COAP_MSG_GET; - test1_req.ops = req_ops; - test1_req.num_ops = 1; - test1_req.payload = buf; - test1_req.payload_len = buf_len; - - test_coap_client_msg_t test1_resp; - test1_resp.type = COAP_MSG_ACK; - test1_resp.code_class = COAP_MSG_SUCCESS; - test1_resp.code_detail = COAP_MSG_CONTENT; - test1_resp.ops = resp_ops; - test1_resp.num_ops = 0; - test1_resp.payload = (char*)"Hello Client!"; - test1_resp.payload_len = 13; - - test_coap_client_data_t test1_data; - test1_data.desc = "Send a confirmable request to server and expect a piggy-backed response"; - test1_data.host = HOST; - test1_data.port = PORT; - test1_data.common_name = (char*)"dummy/server"; - test1_data.test_req = test1_req; - test1_data.test_resp = test1_resp; - test1_data.num_msg = 1; - - //printf("%s\n", test1_data.desc); - - if (ret < 0) - { - printf("Error : %s\n",strerror(-ret)); - return FAIL; - } - - coap_msg_create(&req); - coap_msg_create(&resp); - - ret = exchange(&client, &test1_data.test_req, &req, &resp); - //printf("exchange function\n"); - if (ret != PASS) - { - printf("exchange function fail\n"); - coap_msg_destroy(&resp); - coap_msg_destroy(&req); - coap_client_destroy(&client); - return FAIL; - } - - ret = compare_ver_token(&req, &resp); - if (ret != PASS) - { - coap_msg_destroy(&resp); - coap_msg_destroy(&req); - coap_client_destroy(&client); - return FAIL; - } - - ret = check_resp(&test1_data.test_resp, &resp); - if (ret != PASS) - { - coap_msg_destroy(&resp); - coap_msg_destroy(&req); - coap_client_destroy(&client); - return FAIL; - } - - coap_msg_destroy(&resp); - coap_msg_destroy(&req); - coap_client_destroy(&client); - - return result; -} -//---------------------------------------------------------------------------------------------------------------------- -//---------------------------------------------------------------------------------------------------------------------- -/** - * @brief Print a CoAP message - * - * @param[in] str String to be printed before the message - * @param[in] msg Pointer to a message structure - */ -void print_coap_msg(const char *str, coap_msg_t *msg) -{ - //coap_msg_op_t *op = NULL; - //unsigned num = 0; - //unsigned len = 0; - unsigned i = 0; - //unsigned j = 0; - char *payload = NULL; - char *token = NULL; - //char *val = NULL; - - printf("%s\n", str); - printf("ver: 0x%02x\n", coap_msg_get_ver(msg)); - printf("type: 0x%02x\n", coap_msg_get_type(msg)); - printf("token_len: %d\n", coap_msg_get_token_len(msg)); - printf("code_class: %d\n", coap_msg_get_code_class(msg)); - printf("code_detail: %d\n", coap_msg_get_code_detail(msg)); - printf("msg_id: 0x%04x\n", coap_msg_get_msg_id(msg)); - printf("token: "); - token = coap_msg_get_token(msg); - for (i = 0; i < coap_msg_get_token_len(msg); i++) - { - printf(" 0x%02x", (unsigned char)token[i]); - } - printf("\n"); - /*op = coap_msg_get_first_op(msg); - while (op != NULL) - { - num = coap_msg_op_get_num(op); - len = coap_msg_op_get_len(op); - val = coap_msg_op_get_val(op); - printf("op[%u].num: %u\n", j, num); - printf("op[%u].len: %u\n", j, len); - printf("op[%u].val: ", j); - for (i = 0; i < len; i++) - { - printf(" 0x%02x", (unsigned char)val[i]); - } - printf("\n"); - op = coap_msg_op_get_next(op); - j++; - }*/ - printf("payload: "); - payload = coap_msg_get_payload(msg); - for (i = 0; i < coap_msg_get_payload_len(msg); i++) - { - printf("%c", payload[i]); - //ucReturnCode[i] = payload[i]; - } - //ucReturnCode[i] = '\0'; - printf("\n"); - printf("payload_len: %zu\n", coap_msg_get_payload_len(msg)); -} -//---------------------------------------------------------------------------------------------------------------------- -//---------------------------------------------------------------------------------------------------------------------- -/** - * @brief Populate a request message with details from a test request message structure - * - * @param[in] test_req Pointer to a test request message structure - * @param[out] req Pointer to a request message structure - * - * @returns Test result - */ -test_result_t populate_req(test_coap_client_msg_t *test_req, coap_msg_t *req) -{ - int ret = 0; - - ret = coap_msg_set_type(req, test_req->type); - if (ret < 0) - { - printf("Error : %s\n",strerror(-ret)); - return FAIL; - } - ret = coap_msg_set_code(req, test_req->code_class, test_req->code_detail); - if (ret < 0) - { - printf("Error : %s\n",strerror(-ret)); - return FAIL; - } - ret = coap_msg_add_op(req, test_req->ops.num, test_req->ops.len, test_req->ops.val); - if (ret < 0) - { - printf("Error : %s\n",strerror(-ret)); - return FAIL; - } - if (test_req->payload) - { - ret = coap_msg_set_payload(req, test_req->payload, test_req->payload_len); - if (ret < 0) - { - printf("Error : %s\n",strerror(-ret)); - return FAIL; - } - } - return PASS; -} -//---------------------------------------------------------------------------------------------------------------------- -//---------------------------------------------------------------------------------------------------------------------- -/** - * @brief Send a request to the server and receive the response - * - * @param[in,out] client Pointer to a client structure - * @param[in] test_req Pointer to a test request message structure - * @param[out] req Pointer to a request message structure - * @param[out] resp Pointer to a response message structure - * - * @returns Test result - */ -test_result_t exchange(coap_client_t *client, test_coap_client_msg_t *test_req, coap_msg_t *req, coap_msg_t *resp) -{ - test_result_t result = PASS; - int ret = 0; - - result = populate_req(test_req, req); - if (result != PASS) - { - printf("populate_req FAIL\n"); - return result; - } - ret = coap_client_exchange(client, req, resp); - if (ret < 0) - { - printf("coap_client_exchange FAIL\n"); - printf("Error : %s\n",strerror(-ret)); - return FAIL; - } - //printf("coap_client_exchange PASS\n"); - print_coap_msg("Sent:", req); - print_coap_msg("Received:", resp); - - return PASS; -} -//---------------------------------------------------------------------------------------------------------------------- -//---------------------------------------------------------------------------------------------------------------------- -/** - * @brief Compare the version and token fields in a request message and a response message - * - * @param[out] req Pointer to a request message structure - * @param[out] resp Pointer to a response message structure - * - * @returns Test result - */ -test_result_t compare_ver_token(coap_msg_t *req, coap_msg_t *resp) -{ - if (coap_msg_get_ver(req) != coap_msg_get_ver(resp)) - { - return FAIL; - } - if (coap_msg_get_token_len(req) != coap_msg_get_token_len(resp)) - { - return FAIL; - } - else if (memcmp(coap_msg_get_token(req), coap_msg_get_token(resp), coap_msg_get_token_len(req)) != 0) - { - return FAIL; - } - return PASS; -} -//---------------------------------------------------------------------------------------------------------------------- -//---------------------------------------------------------------------------------------------------------------------- -/** - * @brief Check the fields in a response message against the expected values - * - * @param[out] test_resp Pointer to a test response message structure - * @param[out] resp Pointer to a response message structure - * - * @returns Test result - */ -test_result_t check_resp(test_coap_client_msg_t *test_resp, coap_msg_t *resp) -{ - if (test_resp->type != coap_msg_get_type(resp)) - { - return FAIL; - } - if (test_resp->code_class != coap_msg_get_code_class(resp)) - { - return FAIL; - } - if (test_resp->code_detail != coap_msg_get_code_detail(resp)) - { - return FAIL; - } - return PASS; -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Coap/coap_client.cpp Thu May 25 11:53:45 2017 +0000 @@ -0,0 +1,1026 @@ +/* + * Copyright (c) 2015 Keith Cullen. + * All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file coap_client.c + * + * @brief Source file for the FreeCoAP client library + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <stdint.h> +#include <errno.h> + +#include "coap_client.h" +#include "main.h" + +#define COAP_CLIENT_ACK_TIMEOUT_SEC 2 /**< Minimum delay to wait before retransmitting a confirmable message */ +#define COAP_CLIENT_MAX_RETRANSMIT 4 /**< Maximum number of times a confirmable message can be retransmitted */ +#define COAP_CLIENT_RESP_TIMEOUT_SEC 30 /**< Maximum amount of time to wait for a response */ + +/**************************************************************************************************** + * coap_client * + ****************************************************************************************************/ +void coap_client_destroy(coap_client_t *client) +{ + memset(client, 0, sizeof(coap_client_t)); +} + +/** + * @brief Send a message to the server + * + * @param[in,out] client Pointer to a client structure + * @param[in] msg Pointer to a message structure + * + * @returns Number of bytes sent or error code + * @retval >0 Number of bytes sent + * @retval <0 Error + */ +static int coap_client_send(coap_client_t *client, coap_msg_t *msg) +{ + int num = 0; + char buf[COAP_MSG_MAX_BUF_LEN] = {0}; + + num = coap_msg_format(msg, buf, sizeof(buf)); + if (num < 0) + { + return num; + } + num = send(client->sd, buf, num); + if (num < 0) + { + printf("send error\n"); + return -errno; + } + + return num; +} + +/** + * @brief Handle a format error in a received message + * + * Special handling for the case where a received + * message could not be parsed due to a format error. + * Extract enough information from the received message + * to form a reset message. + * + * @param[in,out] client Pointer to a client structure + * @param[in] buf Buffer containing the message + * @param[in] len length of the buffer + */ +static void coap_client_handle_format_error(coap_client_t *client, char *buf, size_t len) +{ + coap_msg_t msg = {0}; + unsigned msg_id = 0; + unsigned type = 0; + int ret = 0; + + ret = coap_msg_parse_type_msg_id(buf, len, &type, &msg_id); + if ((ret == 0) && (type == COAP_MSG_CON)) + { + coap_msg_create(&msg); + ret = coap_msg_set_type(&msg, COAP_MSG_RST); + if (ret < 0) + { + coap_msg_destroy(&msg); + return; + } + ret = coap_msg_set_msg_id(&msg, msg_id); + if (ret < 0) + { + coap_msg_destroy(&msg); + return; + } + coap_client_send(client, &msg); + coap_msg_destroy(&msg); + } +} + +/** + * @brief Receive a message from the server + * + * @param[in,out] client Pointer to a client structure + * @param[in] msg Pointer to a message structure + * + * @returns Number of bytes received or error code + * @retval >0 Number of bytes received + * @retval <0 Error + */ +static int coap_client_recv(coap_client_t *client, coap_msg_t *msg) +{ + int num = 0; + int ret = 0; + char buf[COAP_MSG_MAX_BUF_LEN] = {0}; + + num = recv(client->sd, buf, sizeof(buf)); + if (num < 0) + { + return -errno; + } + + ret = coap_msg_parse(msg, buf, num); + if (ret < 0) + { + if (ret == -EBADMSG) + { + coap_client_handle_format_error(client, buf, num); + } + return ret; + } + return num; +} + +/** + * @brief Reject a received confirmable message + * + * Send a reset message to the server. + * + * @param[in,out] client Pointer to a client structure + * @param[in] msg Pointer to a message structure + * + * @returns Operation status + * @retval 0 Success + * @retval <0 Error + */ +static int coap_client_reject_con(coap_client_t *client, coap_msg_t *msg) +{ + coap_msg_t rej = {0}; + int num = 0; + int ret = 0; + + printf("Rejecting confirmable message from host %s and port %s\n", client->server_host, client->server_port); + coap_msg_create(&rej); + ret = coap_msg_set_type(&rej, COAP_MSG_RST); + if (ret < 0) + { + coap_msg_destroy(&rej); + return ret; + } + ret = coap_msg_set_msg_id(&rej, coap_msg_get_msg_id(msg)); + if (ret < 0) + { + coap_msg_destroy(&rej); + return ret; + } + num = coap_client_send(client, &rej); + coap_msg_destroy(&rej); + if (num < 0) + { + return num; + } + return 0; +} + +/** + * @brief Reject a received non-confirmable message + * + * @param[in] client Pointer to a client structure + * @param[in] msg Pointer to a message structure + * + * @returns Operation status + * @retval 0 Success + */ +static int coap_client_reject_non(coap_client_t *client, coap_msg_t *msg) +{ + printf("Rejecting non-confirmable message from host %s and port %s\n", client->server_host, client->server_port); + return 0; +} + +/** + * @brief Reject a received acknowledgement message + * + * @param[in] client Pointer to a client structure + * @param[in] msg Pointer to a message structure + * + * @returns Operation status + * @retval 0 Success + */ +static int coap_client_reject_ack(coap_client_t *client, coap_msg_t *msg) +{ + printf("Rejecting acknowledgement message from host %s and port %s\n", client->server_host, client->server_port); + return 0; +} + +/** + * @brief Reject a received reset message + * + * @param[in] client Pointer to a client structure + * @param[in] msg Pointer to a message structure + * + * @returns Operation status + * @retval 0 Success + */ +static int coap_client_reject_reset(coap_client_t *client, coap_msg_t *msg) +{ + printf("Rejecting reset message from host %s and port %s\n", client->server_host, client->server_port); + return 0; +} + +/** + * @brief Reject a received message + * + * @param[in,out] client Pointer to a client structure + * @param[in] msg Pointer to a message structure + * + * @returns Operation status + * @retval 0 Success + * @retval <0 Error + */ +static int coap_client_reject(coap_client_t *client, coap_msg_t *msg) +{ + if (coap_msg_get_type(msg) == COAP_MSG_CON) + { + return coap_client_reject_con(client, msg); + } + else if (coap_msg_get_type(msg) == COAP_MSG_NON) + { + return coap_client_reject_non(client, msg); + } + else if (coap_msg_get_type(msg) == COAP_MSG_ACK) + { + return coap_client_reject_ack(client, msg); + } + else if (coap_msg_get_type(msg) == COAP_MSG_RST) + { + return coap_client_reject_reset(client, msg); + } + return 0; /* should never arrive here */ +} + +/** + * @brief Send an acknowledgement message to the server + * + * @param[in,out] client Pointer to a client structure + * @param[in] msg Pointer to a message structure + * + * @returns Operation status + * @retval 0 Success + * @retval <0 Error + */ +static int coap_client_send_ack(coap_client_t *client, coap_msg_t *msg) +{ + coap_msg_t ack = {0}; + int num = 0; + int ret = 0; + + printf("Acknowledging confirmable message from host %s and port %s\n", client->server_host, client->server_port); + coap_msg_create(&ack); + ret = coap_msg_set_type(&ack, COAP_MSG_ACK); + if (ret < 0) + { + coap_msg_destroy(&ack); + return ret; + } + ret = coap_msg_set_msg_id(&ack, coap_msg_get_msg_id(msg)); + if (ret < 0) + { + coap_msg_destroy(&ack); + return ret; + } + num = coap_client_send(client, &ack); + coap_msg_destroy(&ack); + if (num < 0) + { + return num; + } + return 0; +} + +/** + * @brief Compare the token values in a request message and a response message + * + * @param[in] req Pointer to the request message + * @param[in] resp Pointer to the response message + * + * @returns Comparison value + * @retval 0 the tokens are not equal + * @retval 1 the tokens are equal + */ +static int coap_client_match_token(coap_msg_t *req, coap_msg_t *resp) +{ + return ((coap_msg_get_token_len(resp) == coap_msg_get_token_len(req)) + && (memcmp(coap_msg_get_token(resp), coap_msg_get_token(req), coap_msg_get_token_len(req)) == 0)); +} + +/** + * @brief Check that all of the options in a message are acceptable + * + * For a proxy, options are acceptable if they are safe to forward or recognized or both. + * For a server, options are acceptable if they are elective or recognized or both. + * + * @param[in] msg Pointer to message structure + * + * @returns Operation status or bad option number + * @retval 0 Success + * @retval >0 Bad option number + */ +static unsigned coap_client_check_options(coap_msg_t *msg) +{ +#ifdef COAP_PROXY + return coap_msg_check_unsafe_ops(msg); +#else /* !COAP_PROXY */ + return coap_msg_check_critical_ops(msg); +#endif /* COAP_PROXY */ +} + +/** + * @brief Handle a received piggy-backed response message + * + * An acknowledgement has been received that contains + * the same token as the request. Check the response + * contained within it. + * + * @param[in,out] client Pointer to a client structure + * @param[in] resp Pointer to the response message + * + * @returns Operation status + * @retval 0 Success + * @retval <0 Error + */ +static int coap_client_handle_piggybacked_response(coap_client_t *client, coap_msg_t *resp) +{ + unsigned op_num = 0; + + op_num = coap_client_check_options(resp); + if (op_num != 0) + { + printf("Found bad option number %u in message from host %s and port %s\n", op_num, client->server_host, client->server_port); + coap_client_reject(client, resp); + return -EBADMSG; + } + printf("Received acknowledgement and response from Server\n"); + return 0; +} + +/** + * @brief Handle a received separate response message + * + * A separate response has been received that contains + * the same token as the request. Check the response + * and send an acknowledgement if necessary. + * + * @param[in,out] client Pointer to a client structure + * @param[in] resp Pointer to the response message + * + * @returns Operation status + * @retval 0 Success + * @retval <0 Error + */ +static int coap_client_handle_sep_response(coap_client_t *client, coap_msg_t *resp) +{ + unsigned op_num = 0; + + if (coap_msg_get_type(resp) == COAP_MSG_CON) + { + printf("Received confirmable response from host %s and port %s\n", client->server_host, client->server_port); + op_num = coap_client_check_options(resp); + if (op_num != 0) + { + printf("Found bad option number %u in message from host %s and port %s\n", op_num, client->server_host, client->server_port); + coap_client_reject(client, resp); + return -EBADMSG; + } + return coap_client_send_ack(client, resp); + } + else if (coap_msg_get_type(resp) == COAP_MSG_NON) + { + printf("Received non-confirmable response from host %s and port %s\n", client->server_host, client->server_port); + op_num = coap_client_check_options(resp); + if (op_num != 0) + { + printf("Found bad option number %u in message from host %s and port %s\n", op_num, client->server_host, client->server_port); + coap_client_reject(client, resp); + return -EBADMSG; + } + return 0; + } + coap_client_reject(client, resp); + return -EBADMSG; +} + +/** + * @brief Handle a separate response to a confirmable request + * + * An acknowledgement has been received. Receive the + * response and send an acknowledgement back to the server. + * + * @param[in,out] client Pointer to a client structure + * @param[in] req Pointer to the request message + * @param[out] resp Pointer to the response message + * + * @returns Operation status + * @retval 0 Success + * @retval <0 Error + */ +static int coap_client_exchange_sep(coap_client_t *client, coap_msg_t *req, coap_msg_t *resp) +{ + int num = 0; + int ret = 0; + int starttimeout,endtimeout; + + /* wait for a separate response to a confirmable request */ + printf("Expecting response from host %s and port %s\n", client->server_host, client->server_port); + while (1) + { + starttimeout = readseconds(); + while(1) + { + num = coap_client_recv(client, resp); + if (num < 0) + { + return num; + } + else if(num > 0) + { + break; + } + endtimeout = readseconds(); + if((endtimeout - starttimeout) > COAP_CLIENT_RESP_TIMEOUT_SEC) + { + printf("Timeout no data received\n"); + return -1; + } + } + + if (coap_msg_get_msg_id(resp) == coap_msg_get_msg_id(req)) + { + if (coap_msg_get_type(resp) == COAP_MSG_ACK) + { + /* message deduplication */ + printf("Received duplicate acknowledgement from host %s and port %s\n", client->server_host, client->server_port); + continue; + } + else if (coap_msg_get_type(resp) == COAP_MSG_RST) + { + return -ECONNRESET; + } + coap_client_reject(client, resp); + return -EBADMSG; + } + if (coap_client_match_token(req, resp)) + { + return coap_client_handle_sep_response(client, resp); + } + /* message deduplication */ + /* we might have received a duplicate message that was already received from the same server */ + /* reject the message and continue listening */ + ret = coap_client_reject(client, resp); + if (ret < 0 ) + { + return ret; + } + } + return 0; +} + +/** + * @brief Handle the response to a confirmable request + * + * A confirmable request has been sent to the server. + * Receive the acknowledgement and response. Send an + * acknowledgement if necessary. + * + * @param[in,out] client Pointer to a client structure + * @param[in] req Pointer to the request message + * @param[out] resp Pointer to the response message + * + * @returns Operation status + * @retval 0 Success + * @retval <0 Error + */ +static int coap_client_exchange_con(coap_client_t *client, coap_msg_t *req, coap_msg_t *resp) +{ + int num = 0; + int ret = 0; + int starttimeout,endtimeout; + /* wait for piggy-backed response in ack message + * or ack message and separate response message + */ + //printf("Expecting acknowledgement from server\r\n"); + client->num_retrans = 0; + while (1) + { + int doubletimeout = COAP_CLIENT_ACK_TIMEOUT_SEC; + starttimeout = readseconds(); + while(1) + { + num = coap_client_recv(client, resp); + if (num < 0) + { + //return num; + } + else if(num > 0) + { + break; + } + endtimeout = readseconds(); + if((endtimeout - starttimeout) > doubletimeout) + { + printf("Timeout no data received after %d seconds\n",endtimeout - starttimeout); + + if (client->num_retrans >= COAP_CLIENT_MAX_RETRANSMIT) + { + printf("Maximum retries reached no ack from server!\r\n"); + return -ETIMEDOUT; + } + + doubletimeout = 2 * doubletimeout; + starttimeout = readseconds(); + client->num_retrans++; + + printf("Retransmitting to server...\r\n"); + num = coap_client_send(client, req); + if (num < 0) + { + printf("Resending Failed to server...\r\n"); + return num; + } + } + } + + if (coap_msg_get_msg_id(resp) == coap_msg_get_msg_id(req)) + { + if (coap_msg_get_type(resp) == COAP_MSG_ACK) + { + if (coap_msg_is_empty(resp)) + { + /* received ack message, wait for separate response message */ + printf("Received acknowledgement from host %s and port %s\n", client->server_host, client->server_port); + return coap_client_exchange_sep(client, req, resp); + } + else if (coap_client_match_token(req, resp)) + { + return coap_client_handle_piggybacked_response(client, resp); + } + } + else if (coap_msg_get_type(resp) == COAP_MSG_RST) + { + return -ECONNRESET; + } + coap_client_reject(client, resp); + return -EBADMSG; + } + else if (coap_client_match_token(req, resp)) + { + /* RFC7252 + * as the underlying datagram transport may not be sequence-preserving, + * the Confirmable message carrying the response may actually arrive + * before or after the Acknowledgement message for the request; for + * the purposes of terminating the retransmission sequence, this also + * serves as an acknowledgement. + */ + return coap_client_handle_sep_response(client, resp); + } + /* message deduplication */ + /* we might have received a duplicate message that was already received from the same server */ + /* reject the message and continue listening */ + ret = coap_client_reject(client, resp); + if (ret < 0 ) + { + return ret; + } + } + return 0; +} + +/** + * @brief Handle the response to a non-confirmable request + * + * A non-confirmable request has been sent to the server. + * Receive the response. + * + * @param[in,out] client Pointer to a client structure + * @param[in] req Pointer to the request message + * @param[out] resp Pointer to the response message + * + * @returns Operation status + * @retval 0 Success + * @retval <0 Error + **/ +static int coap_client_exchange_non(coap_client_t *client, coap_msg_t *req, coap_msg_t *resp) +{ + int num = 0; + int ret = 0; + int starttimeout,endtimeout; + + printf("Expecting response from host %s and port %s\n", client->server_host, client->server_port); + while (1) + { + starttimeout = readseconds(); + while(1) + { + num = coap_client_recv(client, resp); + if (num < 0) + { + return num; + } + else if(num > 0) + { + break; + } + endtimeout = readseconds(); + if((endtimeout - starttimeout) > COAP_CLIENT_RESP_TIMEOUT_SEC) + { + printf("Timeout no data received\n"); + return -1; + } + } + + if (coap_msg_get_msg_id(resp) == coap_msg_get_msg_id(req)) + { + if (coap_msg_get_type(resp) == COAP_MSG_RST) + { + return -ECONNRESET; + } + coap_client_reject(client, resp); + return -EBADMSG; + } + if (coap_client_match_token(req, resp)) + { + return coap_client_handle_sep_response(client, resp); + } + /* message deduplication */ + /* we might have received a duplicate message that was already received from the same server */ + /* reject the message and continue listening */ + ret = coap_client_reject(client, resp); + if (ret < 0 ) + { + return ret; + } + } + return 0; +} + +int coap_client_exchange(coap_client_t *client, coap_msg_t *req, coap_msg_t *resp) +{ + unsigned char msg_id_buf[2] = {0}; + unsigned msg_id = 0; + int num = 0; + char token[4] = {0}; + int ret = 0; + + /* check for a valid request */ + if ((coap_msg_get_type(req) == COAP_MSG_ACK) + || (coap_msg_get_type(req) == COAP_MSG_RST) + || (coap_msg_get_code_class(req) != COAP_MSG_REQ)) + { + return -EINVAL; + } + + /* generate the message ID */ + coap_msg_gen_rand_str((char *)msg_id_buf, sizeof(msg_id_buf)); + msg_id = (((unsigned)msg_id_buf[1]) << 8) | (unsigned)msg_id_buf[0]; + ret = coap_msg_set_msg_id(req, msg_id); + if (ret < 0) + { + return ret; + } + + /* generate the token */ + coap_msg_gen_rand_str(token, sizeof(token)); + ret = coap_msg_set_token(req, token, sizeof(token)); + if (ret < 0) + { + return ret; + } + + num = coap_client_send(client, req); + if (num < 0) + { + return num; + } + + if (coap_msg_get_type(req) == COAP_MSG_CON) + { + return coap_client_exchange_con(client, req, resp); + } + else if (coap_msg_get_type(req) == COAP_MSG_NON) + { + return coap_client_exchange_non(client, req, resp); + } + return -EINVAL; +} +//---------------------------------------------------------------------------------------------------------------------- +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Test an exchange with the server + * + * @param[in] data Pointer to a client test data structure + * + * @returns Test result + */ +test_result_t test_exchange_func(char* buf,int buf_len,char* returncode) +{ + printf("----------------------------------------\n"); + test_result_t result = PASS; + coap_client_t client = {0}; + coap_msg_t resp = {0}; + coap_msg_t req = {0}; + int ret = 0; + + test_coap_client_msg_op_t req_ops; + req_ops.num = COAP_MSG_URI_PATH; + req_ops.len = 8; + req_ops.val = (char*)"resource"; + + test_coap_client_msg_op_t resp_ops; + resp_ops.num = 0; + resp_ops.len = 0; + resp_ops.val = 0; + + test_coap_client_msg_t test1_req; + test1_req.type = COAP_MSG_CON; + test1_req.code_class = COAP_MSG_REQ; + test1_req.code_detail = COAP_MSG_GET; + test1_req.ops = req_ops; + test1_req.num_ops = 1; + test1_req.payload = buf; + test1_req.payload_len = buf_len; + + test_coap_client_msg_t test1_resp; + test1_resp.type = COAP_MSG_ACK; + test1_resp.code_class = COAP_MSG_SUCCESS; + test1_resp.code_detail = COAP_MSG_CONTENT; + test1_resp.ops = resp_ops; + test1_resp.num_ops = 0; + test1_resp.payload = (char*)"Hello Client!"; + test1_resp.payload_len = 13; + + test_coap_client_data_t test1_data; + test1_data.desc = "Send a confirmable request to server and expect a piggy-backed response"; + test1_data.host = HOST; + test1_data.port = PORT; + test1_data.common_name = (char*)"dummy/server"; + test1_data.test_req = test1_req; + test1_data.test_resp = test1_resp; + test1_data.num_msg = 1; + + //printf("%s\n", test1_data.desc); + + if (ret < 0) + { + printf("Error : %s\n",strerror(-ret)); + return FAIL; + } + + coap_msg_create(&req); + coap_msg_create(&resp); + + ret = exchange(&client, &test1_data.test_req, &req, &resp,returncode); + //printf("exchange function\n"); + if (ret != PASS) + { + printf("exchange function fail\n"); + coap_msg_destroy(&resp); + coap_msg_destroy(&req); + coap_client_destroy(&client); + return FAIL; + } + + ret = compare_ver_token(&req, &resp); + if (ret != PASS) + { + coap_msg_destroy(&resp); + coap_msg_destroy(&req); + coap_client_destroy(&client); + return FAIL; + } + + ret = check_resp(&test1_data.test_resp, &resp); + if (ret != PASS) + { + coap_msg_destroy(&resp); + coap_msg_destroy(&req); + coap_client_destroy(&client); + return FAIL; + } + + coap_msg_destroy(&resp); + coap_msg_destroy(&req); + coap_client_destroy(&client); + + return result; +} +//---------------------------------------------------------------------------------------------------------------------- +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Print a CoAP message + * + * @param[in] str String to be printed before the message + * @param[in] msg Pointer to a message structure + */ +void print_coap_msg(const char *str, coap_msg_t *msg,char* returncode) +{ + //coap_msg_op_t *op = NULL; + //unsigned num = 0; + //unsigned len = 0; + unsigned i = 0; + //unsigned j = 0; + char *payload = NULL; + char *token = NULL; + //char *val = NULL; + + printf("%s\n", str); + printf("ver: 0x%02x\n", coap_msg_get_ver(msg)); + printf("type: 0x%02x\n", coap_msg_get_type(msg)); + printf("token_len: %d\n", coap_msg_get_token_len(msg)); + printf("code_class: %d\n", coap_msg_get_code_class(msg)); + printf("code_detail: %d\n", coap_msg_get_code_detail(msg)); + printf("msg_id: 0x%04x\n", coap_msg_get_msg_id(msg)); + printf("token: "); + token = coap_msg_get_token(msg); + for (i = 0; i < coap_msg_get_token_len(msg); i++) + { + printf(" 0x%02x", (unsigned char)token[i]); + } + printf("\n"); + /*op = coap_msg_get_first_op(msg); + while (op != NULL) + { + num = coap_msg_op_get_num(op); + len = coap_msg_op_get_len(op); + val = coap_msg_op_get_val(op); + printf("op[%u].num: %u\n", j, num); + printf("op[%u].len: %u\n", j, len); + printf("op[%u].val: ", j); + for (i = 0; i < len; i++) + { + printf(" 0x%02x", (unsigned char)val[i]); + } + printf("\n"); + op = coap_msg_op_get_next(op); + j++; + }*/ + printf("payload: "); + payload = coap_msg_get_payload(msg); + + if(strcmp(str,"Received:") == 0) + { + for (i = 0; i < coap_msg_get_payload_len(msg); i++) + { + printf("%c", payload[i]); + returncode[i] = payload[i]; + } + returncode[i] = '\0'; + } + printf("\n"); + + printf("payload_len: %zu\n", coap_msg_get_payload_len(msg)); +} +//---------------------------------------------------------------------------------------------------------------------- +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Populate a request message with details from a test request message structure + * + * @param[in] test_req Pointer to a test request message structure + * @param[out] req Pointer to a request message structure + * + * @returns Test result + */ +test_result_t populate_req(test_coap_client_msg_t *test_req, coap_msg_t *req) +{ + int ret = 0; + + ret = coap_msg_set_type(req, test_req->type); + if (ret < 0) + { + printf("Error : %s\n",strerror(-ret)); + return FAIL; + } + ret = coap_msg_set_code(req, test_req->code_class, test_req->code_detail); + if (ret < 0) + { + printf("Error : %s\n",strerror(-ret)); + return FAIL; + } + ret = coap_msg_add_op(req, test_req->ops.num, test_req->ops.len, test_req->ops.val); + if (ret < 0) + { + printf("Error : %s\n",strerror(-ret)); + return FAIL; + } + if (test_req->payload) + { + ret = coap_msg_set_payload(req, test_req->payload, test_req->payload_len); + if (ret < 0) + { + printf("Error : %s\n",strerror(-ret)); + return FAIL; + } + } + return PASS; +} +//---------------------------------------------------------------------------------------------------------------------- +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Send a request to the server and receive the response + * + * @param[in,out] client Pointer to a client structure + * @param[in] test_req Pointer to a test request message structure + * @param[out] req Pointer to a request message structure + * @param[out] resp Pointer to a response message structure + * + * @returns Test result + */ +test_result_t exchange(coap_client_t *client, test_coap_client_msg_t *test_req, coap_msg_t *req, coap_msg_t *resp,char* returncode) +{ + test_result_t result = PASS; + int ret = 0; + + result = populate_req(test_req, req); + if (result != PASS) + { + printf("populate_req FAIL\n"); + return result; + } + ret = coap_client_exchange(client, req, resp); + if (ret < 0) + { + printf("coap_client_exchange FAIL\n"); + printf("Error : %s\n",strerror(-ret)); + return FAIL; + } + //printf("coap_client_exchange PASS\n"); + print_coap_msg("Sent:", req,returncode); + print_coap_msg("Received:", resp,returncode); + + return PASS; +} +//---------------------------------------------------------------------------------------------------------------------- +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Compare the version and token fields in a request message and a response message + * + * @param[out] req Pointer to a request message structure + * @param[out] resp Pointer to a response message structure + * + * @returns Test result + */ +test_result_t compare_ver_token(coap_msg_t *req, coap_msg_t *resp) +{ + if (coap_msg_get_ver(req) != coap_msg_get_ver(resp)) + { + return FAIL; + } + if (coap_msg_get_token_len(req) != coap_msg_get_token_len(resp)) + { + return FAIL; + } + else if (memcmp(coap_msg_get_token(req), coap_msg_get_token(resp), coap_msg_get_token_len(req)) != 0) + { + return FAIL; + } + return PASS; +} +//---------------------------------------------------------------------------------------------------------------------- +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Check the fields in a response message against the expected values + * + * @param[out] test_resp Pointer to a test response message structure + * @param[out] resp Pointer to a response message structure + * + * @returns Test result + */ +test_result_t check_resp(test_coap_client_msg_t *test_resp, coap_msg_t *resp) +{ + if (test_resp->type != coap_msg_get_type(resp)) + { + return FAIL; + } + if (test_resp->code_class != coap_msg_get_code_class(resp)) + { + return FAIL; + } + if (test_resp->code_detail != coap_msg_get_code_detail(resp)) + { + return FAIL; + } + return PASS; +}
--- a/Coap/coap_client.h Wed May 24 07:51:33 2017 +0000 +++ b/Coap/coap_client.h Thu May 25 11:53:45 2017 +0000 @@ -45,8 +45,6 @@ #define COAP_CLIENT_HOST_BUF_LEN 128 /* Buffer length for host addresses */ #define COAP_CLIENT_PORT_BUF_LEN 8 /* Buffer length for port numbers */ -//unsigned char ucReturnCode[10]; //Return code array - /** * @brief Test result enumeration */ @@ -158,12 +156,12 @@ **/ int coap_client_exchange(coap_client_t *client, coap_msg_t *req, coap_msg_t *resp); -test_result_t test_exchange_func(char* buf,int buf_len); +test_result_t test_exchange_func(char* buf,int buf_len,char* returncode); test_result_t check_resp(test_coap_client_msg_t *test_resp, coap_msg_t *resp); test_result_t compare_ver_token(coap_msg_t *req, coap_msg_t *resp); -test_result_t exchange(coap_client_t *client, test_coap_client_msg_t *test_req, coap_msg_t *req, coap_msg_t *resp); +test_result_t exchange(coap_client_t *client, test_coap_client_msg_t *test_req, coap_msg_t *req, coap_msg_t *resp,char* returncode); test_result_t populate_req(test_coap_client_msg_t *test_req, coap_msg_t *req); -void print_coap_msg(const char *str, coap_msg_t *msg); +void print_coap_msg(const char *str, coap_msg_t *msg,char* returncode);
--- a/Coap/coap_msg.c Wed May 24 07:51:33 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1024 +0,0 @@ -/* - * Copyright (c) 2015 Keith Cullen. - * All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * @file coap_msg.c - * - * @brief Source file for the FreeCoAP message parser/formatter library - */ - -#include <stdlib.h> -#include <string.h> -#include <stdint.h> -#include <time.h> -#include <errno.h> -#include "coap_msg.h" -#include "coap_client.h" - -#define coap_msg_op_list_get_first(list) ((list)->first) /**< Get the first option from an option linked-list */ -#define coap_msg_op_list_get_last(list) ((list)->last) /**< Get the last option in an option linked-list */ -#define coap_msg_op_list_is_empty(list) ((list)->first == NULL) /**< Indicate whether or not an option linked-list is empty */ - -static int coap_msg_rand_init = 0; /**< Indicates whether or not the random number generator has been initialised */ - -void coap_msg_gen_rand_str(char *buf, size_t len) -{ - size_t i = 0; - - if (!coap_msg_rand_init) - { - srand(time(NULL)); - coap_msg_rand_init = 1; - } - for (i = 0; i < len; i++) - { - buf[i] = rand() & 0x000000ff; - } -} - -int coap_msg_op_num_is_recognized(unsigned num) -{ - switch (num) - { - case COAP_MSG_IF_MATCH: - case COAP_MSG_URI_HOST: - case COAP_MSG_ETAG: - case COAP_MSG_IF_NONE_MATCH: - case COAP_MSG_URI_PORT: - case COAP_MSG_LOCATION_PATH: - case COAP_MSG_URI_PATH: - case COAP_MSG_CONTENT_FORMAT: - case COAP_MSG_MAX_AGE: - case COAP_MSG_URI_QUERY: - case COAP_MSG_ACCEPT: - case COAP_MSG_LOCATION_QUERY: - case COAP_MSG_PROXY_URI: - case COAP_MSG_PROXY_SCHEME: - case COAP_MSG_SIZE1: - return 1; - } - return 0; -} - -/** - * @brief Allocate an option structure - * - * @param[in] num Option number - * @param[in] len Option length - * @param[in] val Pointer to the option value - * - * @returns Pointer to the option structure - * @retval NULL Out-of-memory - */ -static coap_msg_op_t *coap_msg_op_new(unsigned num, unsigned len, const char *val) -{ - coap_msg_op_t *op = NULL; - - op = (coap_msg_op_t *)malloc(sizeof(coap_msg_op_t)); - if (op == NULL) - { - return NULL; - } - op->num = num; - op->len = len; - op->val = (char *)malloc(len); - if (op->val == NULL) - { - free(op); - return NULL; - } - memcpy(op->val, val, len); - op->next = NULL; - return op; -} - -/** - * @brief Free an option structure that was allocated by coap_msg_op_new - * - * @param[in,out] op Pointer to the option structure - */ -static void coap_msg_op_delete(coap_msg_op_t *op) -{ - free(op->val); - free(op); -} - -/** - * @brief Initialise an option linked-list structure - * - * @param[out] list Pointer to an option linked-list structure - */ -static void coap_msg_op_list_create(coap_msg_op_list_t *list) -{ - memset(list, 0, sizeof(coap_msg_op_list_t)); -} - -/** - * @brief Deinitialise an option linked-list structure - * - * @param[in,out] list Pointer to an option linked-list structure - */ -static void coap_msg_op_list_destroy(coap_msg_op_list_t *list) -{ - coap_msg_op_t *prev = NULL; - coap_msg_op_t *op = NULL; - - op = list->first; - while (op != NULL) - { - prev = op; - op = op->next; - coap_msg_op_delete(prev); - } - memset(list, 0, sizeof(coap_msg_op_list_t)); -} - -/** - * @brief Allocate an option structure and add it to the end of an option linked-list structure - * - * @param[in,out] list Pointer to an option linked-list structure - * @param[in] num Option number - * @param[in] len Option length - * @param[in] val Pointer to a buffer containing the option value - * - * @returns Operation status - * @retval 0 Success - * @retval <0 Error - */ -static int coap_msg_op_list_add_last(coap_msg_op_list_t *list, unsigned num, unsigned len, const char *val) -{ - coap_msg_op_t *op = NULL; - - op = coap_msg_op_new(num, len, val); - if (op == NULL) - { - return -ENOMEM; - } - if (list->first == NULL) - { - list->first = op; - list->last = op; - } - else - { - list->last->next = op; - list->last = op; - } - return 0; -} - -/** - * @brief Allocate an option structure and add it to an option linked-list structure - * - * The option is added to the list at a position determined by the option number. - * - * @param[in,out] list Pointer to an option linked-list structure - * @param[in] num Option number - * @param[in] len Option length - * @param[in] val Pointer to a buffer containing the option value - * - * @returns Operation status - * @retval 0 Success - * @retval <0 Error - */ -static int coap_msg_op_list_add(coap_msg_op_list_t *list, unsigned num, unsigned len, const char *val) -{ - coap_msg_op_t *prev = NULL; - coap_msg_op_t *op = NULL; - - op = coap_msg_op_new(num, len, val); - if (op == NULL) - { - return -ENOMEM; - } - if (list->first == NULL) - { - /* empty list */ - list->first = op; - list->last = op; - return 0; - } - if (op->num < list->first->num) - { - /* start of the list */ - op->next = list->first; - list->first = op; - return 0; - } - prev = list->first; - while (prev != list->last) - { - /* middle of the list */ - if ((prev->num <= op->num) && (op->num < prev->next->num)) - { - op->next = prev->next; - prev->next = op; - return 0; - } - prev = prev->next; - } - /* end of the list */ - list->last->next = op; - list->last = op; - return 0; -} - -void coap_msg_create(coap_msg_t *msg) -{ - memset(msg, 0, sizeof(coap_msg_t)); - msg->ver = COAP_MSG_VER; - coap_msg_op_list_create(&msg->op_list); -} - -void coap_msg_destroy(coap_msg_t *msg) -{ - coap_msg_op_list_destroy(&msg->op_list); - if (msg->payload != NULL) - { - free(msg->payload); - } - memset(msg, 0, sizeof(coap_msg_t)); -} - -void coap_msg_reset(coap_msg_t *msg) -{ - coap_msg_destroy(msg); - coap_msg_create(msg); -} - -/** - * @brief Check a message for correctness - * - * The following checks from RFC7252 are performed: - * - * An Empty message has the Code field set to 0.00. The Token Length - * field MUST be set to 0 and bytes of data MUST NOT be present after - * the Message ID field. If there are any bytes, they MUST be processed - * as a message format error. - * - * The Reset message MUST echo the Message ID of the Confirmable message - * and MUST be Empty. - * - * A Non-confirmable message always carries either a request or response - * and MUST NOT be Empty. - * - * @param[in] msg Pointer to a message structure - * @returns Operation status - * @retval 0 Success - * @retval <0 Error - */ -static int coap_msg_check(coap_msg_t *msg) -{ - if ((msg->code_class == 0) && (msg->code_detail == 0)) - { - /* empty message */ - if ((msg->type == COAP_MSG_NON) - || (msg->token_len != 0) - || (!coap_msg_op_list_is_empty(&msg->op_list)) - || (msg->payload_len != 0)) - { - return -EBADMSG; - } - } - else - { - /* non-empty message */ - if (msg->type == COAP_MSG_RST) - { - return -EBADMSG; - } - } - return 0; -} - -unsigned coap_msg_check_critical_ops(coap_msg_t *msg) -{ - coap_msg_op_t *op = NULL; - unsigned num = 0; - - op = coap_msg_get_first_op(msg); - while (op != NULL) - { - num = coap_msg_op_get_num(op); - if ((coap_msg_op_num_is_critical(num)) - && (!coap_msg_op_num_is_recognized(num))) - { - return num; /* fail */ - } - op = coap_msg_op_get_next(op); - } - return 0; /* pass */ -} - -unsigned coap_msg_check_unsafe_ops(coap_msg_t *msg) -{ - coap_msg_op_t *op = NULL; - unsigned num = 0; - - op = coap_msg_get_first_op(msg); - while (op != NULL) - { - num = coap_msg_op_get_num(op); - if ((coap_msg_op_num_is_unsafe(num)) - && (!coap_msg_op_num_is_recognized(num))) - { - return num; /* fail */ - } - op = coap_msg_op_get_next(op); - } - return 0; /* pass */ -} - -int coap_msg_parse_type_msg_id(char *buf, size_t len, unsigned *type, unsigned *msg_id) -{ - if (len < 4) - { - return -EBADMSG; - } - *type = (buf[0] >> 4) & 0x03; - *msg_id = ntohs(*((uint16_t *)(&buf[2]))); - return 0; -} - -/** - * @brief Parse the header in a message - * - * @param[out] msg Pointer to a message structure - * @param[in] buf Pointer to a buffer containing the message - * @param[in] len Length of the buffer - * - * @returns Number of bytes parsed or error code - * @retval >0 Number of bytes parsed - * @retval <0 Error - */ -static int coap_msg_parse_hdr(coap_msg_t *msg, char *buf, size_t len) -{ - char *p = buf; - - if (len < 4) - { - return -EBADMSG; - } - msg->ver = (p[0] >> 6) & 0x03; - if (msg->ver != COAP_MSG_VER) - { - return -EINVAL; - } - msg->type = (p[0] >> 4) & 0x03; - msg->token_len = p[0] & 0x0f; - if (msg->token_len > sizeof(msg->token)) - { - return -EBADMSG; - } - msg->code_detail = p[1] & 0x1f; - msg->code_class = (p[1] >> 5) & 0x07; - if ((msg->code_class != COAP_MSG_REQ) - && (msg->code_class != COAP_MSG_SUCCESS) - && (msg->code_class != COAP_MSG_CLIENT_ERR) - && (msg->code_class != COAP_MSG_SERVER_ERR)) - { - return -EBADMSG; - } - msg->msg_id = ntohs(*((uint16_t *)(&p[2]))); - p += 4; - len -= 4; - return p - buf; -} - -/** - * @brief Parse the token in a message - * - * @param[out] msg Pointer to a message structure - * @param[in] buf Pointer to a buffer containing the message - * @param[in] len Length of the buffer - * - * @returns Number of bytes parsed or error code - * @retval >0 Number of bytes parsed - * @retval <0 Error - */ -static int coap_msg_parse_token(coap_msg_t *msg, char *buf, size_t len) -{ - if (len < msg->token_len) - { - return -EBADMSG; - } - memcpy(msg->token, buf, msg->token_len); - return msg->token_len; -} - -/** - * @brief Parse an option in a message - * - * @param[in,out] msg Pointer to a message structure - * @param[in] buf Pointer to a buffer containing the message - * @param[in] len Length of the buffer - * - * @returns Number of bytes parsed or error code - * @retval >0 Number of bytes parsed - * @retval <0 Error - */ -static int coap_msg_parse_op(coap_msg_t *msg, char *buf, size_t len) -{ - coap_msg_op_t *prev = NULL; - unsigned op_delta = 0; - unsigned op_len = 0; - unsigned op_num = 0; - char *p = buf; - int ret = 0; - - if (len < 1) - { - return -EBADMSG; - } - op_delta = (p[0] >> 4) & 0x0f; - op_len = p[0] & 0x0f; - if ((op_delta == 15) || (op_len == 15)) - { - return -EBADMSG; - } - p++; - len--; - if (op_delta == 13) - { - if (len < 1) - { - return -EBADMSG; - } - op_delta += p[0]; - p++; - len--; - } - else if (op_delta == 14) - { - if (len < 2) - { - return -EBADMSG; - } - op_delta = 269 + ntohs(*((uint16_t *)(&p[0]))); - p += 2; - len -= 2; - } - if (op_len == 13) - { - if (len < 1) - { - return -EBADMSG; - } - op_len += p[0]; - p++; - len--; - } - else if (op_len == 14) - { - if (len < 2) - { - return -EBADMSG; - } - op_len = 269 + ntohs(*((uint16_t *)(&p[0]))); - p += 2; - len -= 2; - } - if (len < op_len) - { - return -EBADMSG; - } - prev = coap_msg_op_list_get_last(&msg->op_list); - if (prev == NULL) - { - op_num = op_delta; - } - else - { - op_num = coap_msg_op_get_num(prev) + op_delta; - } - ret = coap_msg_op_list_add_last(&msg->op_list, op_num, op_len, p); - if (ret < 0) - { - return ret; - } - p += op_len; - return p - buf; -} - -/** - * @brief Parse the options in a message - * - * @param[in,out] msg Pointer to a message structure - * @param[in] buf Pointer to a buffer containing the message - * @param[in] len Length of the buffer - * - * @returns Number of bytes parsed or error code - * @retval >0 Number of bytes parsed - * @retval <0 Error - */ -static int coap_msg_parse_ops(coap_msg_t *msg, char *buf, size_t len) -{ - int num = 0; - char *p = buf; - - while (1) - { - if (((p[0] & 0xff) == 0xff) || (len == 0)) - { - break; - } - num = coap_msg_parse_op(msg, p, len); - if (num < 0) - { - return num; - } - p += num; - len -= num; - } - return p - buf; -} - -/** - * @brief Parse the payload in a message - * - * @param[out] msg Pointer to a message structure - * @param[in] buf Pointer to a buffer containing the message - * @param[in] len Length of the buffer - * - * @returns Number of bytes parsed or error code - * @retval >0 Number of bytes parsed - * @retval <0 Error - */ -static int coap_msg_parse_payload(coap_msg_t *msg, char *buf, size_t len) -{ - char *p = buf; - - if (len == 0) - { - return 0; - } - if ((p[0] & 0xff) != 0xff) - { - return -EBADMSG; - } - p++; - len--; - if (len == 0) - { - return -EBADMSG; - } - msg->payload = (char *)malloc(len); - if (msg->payload == NULL) - { - return -ENOMEM; - } - memcpy(msg->payload, p, len); - msg->payload_len = len; - p += len; - return p - buf; -} - -int coap_msg_parse(coap_msg_t *msg, char *buf, size_t len) -{ - int num = 0; - char *p = buf; - - coap_msg_reset(msg); - num = coap_msg_parse_hdr(msg, p, len); - if (num < 0) - { - coap_msg_destroy(msg); - return num; - } - p += num; - len -= num; - num = coap_msg_parse_token(msg, p, len); - if (num < 0) - { - coap_msg_destroy(msg); - return num; - } - p += num; - len -= num; - num = coap_msg_parse_ops(msg, p, len); - if (num < 0) - { - coap_msg_destroy(msg); - return num; - } - p += num; - len -= num; - num = coap_msg_parse_payload(msg, p, len); - if (num < 0) - { - coap_msg_destroy(msg); - return num; - } - return coap_msg_check(msg); -} - -int coap_msg_set_type(coap_msg_t *msg, unsigned type) -{ - if ((type != COAP_MSG_CON) - && (type != COAP_MSG_NON) - && (type != COAP_MSG_ACK) - && (type != COAP_MSG_RST)) - { - return -EINVAL; - } - msg->type = type; - return 0; -} - -int coap_msg_set_code(coap_msg_t *msg, unsigned code_class, unsigned code_detail) -{ - if (code_class > COAP_MSG_MAX_CODE_CLASS) - { - return -EINVAL; - } - if (code_detail > COAP_MSG_MAX_CODE_DETAIL) - { - return -EINVAL; - } - msg->code_class = code_class; - msg->code_detail = code_detail; - return 0; -} - -int coap_msg_set_msg_id(coap_msg_t *msg, unsigned msg_id) -{ - if (msg_id > COAP_MSG_MAX_MSG_ID) - { - return -EINVAL; - } - msg->msg_id = msg_id; - return 0; -} - -int coap_msg_set_token(coap_msg_t *msg, char *buf, size_t len) -{ - if (len > COAP_MSG_MAX_TOKEN_LEN) - { - return -EINVAL; - } - memcpy(msg->token, buf, len); - msg->token_len = len; - return 0; -} - -int coap_msg_add_op(coap_msg_t *msg, unsigned num, unsigned len, const char *val) -{ - return coap_msg_op_list_add(&msg->op_list, num, len, val); -} - -int coap_msg_set_payload(coap_msg_t *msg, char *buf, size_t len) -{ - msg->payload_len = 0; - if (msg->payload != NULL) - { - free(msg->payload); - msg->payload = NULL; - } - if (len > 0) - { - msg->payload = (char *)malloc(len); - if (msg->payload == NULL) - { - return -ENOMEM; - } - memcpy(msg->payload, buf, len); - msg->payload_len = len; - } - return 0; -} - -/** - * @brief Format the header in a message - * - * @param[in] msg Pointer to a message structure - * @param[out] buf Pointer to a buffer to contain the formatted message - * @param[in] len Length of the buffer - * - * @returns Length of the formatted message or error code - * @retval >0 Length of the formatted message - * @retval <0 Error - */ -static int coap_msg_format_hdr(coap_msg_t *msg, char *buf, size_t len) -{ - uint16_t msg_id = 0; - - if (len < 4) - { - return -ENOSPC; - } - buf[0] = (char)((COAP_MSG_VER << 6) - | ((msg->type & 0x03) << 4) - | (msg->token_len & 0x0f)); - buf[1] = (char)(((msg->code_class & 0x07) << 5) - | (msg->code_detail & 0x1f)); - msg_id = htons(msg->msg_id); - memcpy(&buf[2], &msg_id, 2); - return 4; -} - -/** - * @brief Format the token in a message - * - * @param[in] msg Pointer to a message structure - * @param[out] buf Pointer to a buffer to contain the formatted message - * @param[in] len Length of the buffer - * - * @returns Length of the formatted message or error code - * @retval >0 Length of the formatted message - * @retval <0 Error - */ -static int coap_msg_format_token(coap_msg_t *msg, char *buf, size_t len) -{ - if (len < msg->token_len) - { - return -ENOSPC; - } - memcpy(buf, msg->token, msg->token_len); - return msg->token_len; -} - -/** - * @brief Format an option in a message - * - * @param[in] op Pointer to an option structure - * @param[in] prev_num option number of the previous option - * @param[out] buf Pointer to a buffer to contain the formatted message - * @param[in] len Length of the buffer - * - * @returns Length of the formatted message or error code - * @retval >0 Length of the formatted message - * @retval <0 Error - */ -static int coap_msg_format_op(coap_msg_op_t *op, unsigned prev_num, char *buf, size_t len) -{ - unsigned op_delta = 0; - unsigned num = 0; - uint16_t val = 0; - char *p = buf; - - op_delta = op->num - prev_num; - num++; - - /* option delta */ - if (op_delta >= 269) - { - num += 2; - } - else if (op_delta >= 13) - { - num += 1; - } - - /* option length */ - if (op->len >= 269) - { - num += 2; - } - else if (op->num >= 13) - { - num += 1; - } - - /* option value */ - num += op->len; - if (num > len) - { - return -ENOSPC; - } - - /* option delta */ - if (op_delta >= 269) - { - p[0] = 14 << 4; - } - else if (op_delta >= 13) - { - p[0] = 13 << 4; - } - else - { - p[0] = op_delta << 4; - } - - /* option length */ - if (op->len >= 269) - { - p[0] |= 14; - } - else if (op->len >= 13) - { - p[0] |= 13; - } - else - { - p[0] |= op->len; - } - p++; - len--; - - /* option delta extended */ - if (op_delta >= 269) - { - val = htons(op_delta - 269); - memcpy(p, &val, 2); - p += 2; - len -= 2; - } - else if (op_delta >= 13) - { - p[0] = op_delta - 13; - p++; - len--; - } - - /* option length extended */ - if (op->len >= 269) - { - val = htons(op->len - 269); - memcpy(p, &val, 2); - p += 2; - len -= 2; - } - else if (op->len >= 13) - { - p[0] = op->len - 13; - p++; - len--; - } - - /* option value */ - memcpy(p, op->val, op->len); - p += op->len; - - return p - buf; -} - -/** - * @brief Format the options in a message - * - * @param[in] msg Pointer to a message structure - * @param[out] buf Pointer to a buffer to contain the formatted message - * @param[in] len Length of the buffer - * - * @returns Length of the formatted message or error code - * @retval >0 Length of the formatted message - * @retval <0 Error - */ -static int coap_msg_format_ops(coap_msg_t *msg, char *buf, size_t len) -{ - coap_msg_op_t *op = NULL; - unsigned prev_num = 0; - int num = 0; - char *p = buf; - - op = coap_msg_op_list_get_first(&msg->op_list); - while (op != NULL) - { - num = coap_msg_format_op(op, prev_num, p, len); - if (num < 0) - { - return num; - } - p += num; - len -= num; - prev_num = coap_msg_op_get_num(op); - op = coap_msg_op_get_next(op); - } - return p - buf; -} - -/** - * @brief Format the payload in a message - * - * @param[in] msg Pointer to a message structure - * @param[out] buf Pointer to a buffer to contain the formatted message - * @param[in] len Length of the buffer - * - * @returns Length of the formatted message or error code - * @retval >0 Length of the formatted message - * @retval <0 Error - */ -static int coap_msg_format_payload(coap_msg_t *msg, char *buf, size_t len) -{ - if (msg->payload_len == 0) - { - return 0; - } - if (msg->payload_len + 1 > len) - { - return -ENOSPC; - } - buf[0] = 0xff; - memcpy(&buf[1], msg->payload, msg->payload_len); - return msg->payload_len + 1; -} - -int coap_msg_format(coap_msg_t *msg, char *buf, size_t len) -{ - int num = 0; - char *p = buf; - int ret = 0; - - ret = coap_msg_check(msg); - if (ret != 0) - { - return ret; - } - num = coap_msg_format_hdr(msg, p, len); - if (num < 0) - { - return num; - } - p += num; - len -= num; - num = coap_msg_format_token(msg, p, len); - if (num < 0) - { - return num; - } - p += num; - len -= num; - num = coap_msg_format_ops(msg, p, len); - if (num < 0) - { - return num; - } - p += num; - len -= num; - num = coap_msg_format_payload(msg, p, len); - if (num < 0) - { - return num; - } - p += num; - return p - buf; -} - -int coap_msg_copy(coap_msg_t *dst, coap_msg_t *src) -{ - coap_msg_op_t *op = NULL; - int ret = 0; - - dst->ver = src->ver; - ret = coap_msg_set_type(dst, coap_msg_get_type(src)); - if (ret < 0) - { - return ret; - } - ret = coap_msg_set_code(dst, coap_msg_get_code_class(src), coap_msg_get_code_detail(src)); - if (ret < 0) - { - return ret; - } - ret = coap_msg_set_msg_id(dst, coap_msg_get_msg_id(src)); - if (ret < 0) - { - return ret; - } - ret = coap_msg_set_token(dst, coap_msg_get_token(src), coap_msg_get_token_len(src)); - if (ret < 0) - { - return ret; - } - op = coap_msg_get_first_op(src); - while (op != NULL) - { - ret = coap_msg_add_op(dst, coap_msg_op_get_num(op), coap_msg_op_get_len(op), coap_msg_op_get_val(op)); - if (ret < 0) - { - return ret; - } - op = coap_msg_op_get_next(op); - } - ret = coap_msg_set_payload(dst, coap_msg_get_payload(src), coap_msg_get_payload_len(src)); - if (ret < 0) - { - return ret; - } - return 0; -} -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Coap/coap_msg.cpp Thu May 25 11:53:45 2017 +0000 @@ -0,0 +1,1024 @@ +/* + * Copyright (c) 2015 Keith Cullen. + * All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file coap_msg.c + * + * @brief Source file for the FreeCoAP message parser/formatter library + */ + +#include <stdlib.h> +#include <string.h> +#include <stdint.h> +#include <time.h> +#include <errno.h> +#include "coap_msg.h" +#include "coap_client.h" + +#define coap_msg_op_list_get_first(list) ((list)->first) /**< Get the first option from an option linked-list */ +#define coap_msg_op_list_get_last(list) ((list)->last) /**< Get the last option in an option linked-list */ +#define coap_msg_op_list_is_empty(list) ((list)->first == NULL) /**< Indicate whether or not an option linked-list is empty */ + +static int coap_msg_rand_init = 0; /**< Indicates whether or not the random number generator has been initialised */ + +void coap_msg_gen_rand_str(char *buf, size_t len) +{ + size_t i = 0; + + if (!coap_msg_rand_init) + { + srand(time(NULL)); + coap_msg_rand_init = 1; + } + for (i = 0; i < len; i++) + { + buf[i] = rand() & 0x000000ff; + } +} + +int coap_msg_op_num_is_recognized(unsigned num) +{ + switch (num) + { + case COAP_MSG_IF_MATCH: + case COAP_MSG_URI_HOST: + case COAP_MSG_ETAG: + case COAP_MSG_IF_NONE_MATCH: + case COAP_MSG_URI_PORT: + case COAP_MSG_LOCATION_PATH: + case COAP_MSG_URI_PATH: + case COAP_MSG_CONTENT_FORMAT: + case COAP_MSG_MAX_AGE: + case COAP_MSG_URI_QUERY: + case COAP_MSG_ACCEPT: + case COAP_MSG_LOCATION_QUERY: + case COAP_MSG_PROXY_URI: + case COAP_MSG_PROXY_SCHEME: + case COAP_MSG_SIZE1: + return 1; + } + return 0; +} + +/** + * @brief Allocate an option structure + * + * @param[in] num Option number + * @param[in] len Option length + * @param[in] val Pointer to the option value + * + * @returns Pointer to the option structure + * @retval NULL Out-of-memory + */ +static coap_msg_op_t *coap_msg_op_new(unsigned num, unsigned len, const char *val) +{ + coap_msg_op_t *op = NULL; + + op = (coap_msg_op_t *)malloc(sizeof(coap_msg_op_t)); + if (op == NULL) + { + return NULL; + } + op->num = num; + op->len = len; + op->val = (char *)malloc(len); + if (op->val == NULL) + { + free(op); + return NULL; + } + memcpy(op->val, val, len); + op->next = NULL; + return op; +} + +/** + * @brief Free an option structure that was allocated by coap_msg_op_new + * + * @param[in,out] op Pointer to the option structure + */ +static void coap_msg_op_delete(coap_msg_op_t *op) +{ + free(op->val); + free(op); +} + +/** + * @brief Initialise an option linked-list structure + * + * @param[out] list Pointer to an option linked-list structure + */ +static void coap_msg_op_list_create(coap_msg_op_list_t *list) +{ + memset(list, 0, sizeof(coap_msg_op_list_t)); +} + +/** + * @brief Deinitialise an option linked-list structure + * + * @param[in,out] list Pointer to an option linked-list structure + */ +static void coap_msg_op_list_destroy(coap_msg_op_list_t *list) +{ + coap_msg_op_t *prev = NULL; + coap_msg_op_t *op = NULL; + + op = list->first; + while (op != NULL) + { + prev = op; + op = op->next; + coap_msg_op_delete(prev); + } + memset(list, 0, sizeof(coap_msg_op_list_t)); +} + +/** + * @brief Allocate an option structure and add it to the end of an option linked-list structure + * + * @param[in,out] list Pointer to an option linked-list structure + * @param[in] num Option number + * @param[in] len Option length + * @param[in] val Pointer to a buffer containing the option value + * + * @returns Operation status + * @retval 0 Success + * @retval <0 Error + */ +static int coap_msg_op_list_add_last(coap_msg_op_list_t *list, unsigned num, unsigned len, const char *val) +{ + coap_msg_op_t *op = NULL; + + op = coap_msg_op_new(num, len, val); + if (op == NULL) + { + return -ENOMEM; + } + if (list->first == NULL) + { + list->first = op; + list->last = op; + } + else + { + list->last->next = op; + list->last = op; + } + return 0; +} + +/** + * @brief Allocate an option structure and add it to an option linked-list structure + * + * The option is added to the list at a position determined by the option number. + * + * @param[in,out] list Pointer to an option linked-list structure + * @param[in] num Option number + * @param[in] len Option length + * @param[in] val Pointer to a buffer containing the option value + * + * @returns Operation status + * @retval 0 Success + * @retval <0 Error + */ +static int coap_msg_op_list_add(coap_msg_op_list_t *list, unsigned num, unsigned len, const char *val) +{ + coap_msg_op_t *prev = NULL; + coap_msg_op_t *op = NULL; + + op = coap_msg_op_new(num, len, val); + if (op == NULL) + { + return -ENOMEM; + } + if (list->first == NULL) + { + /* empty list */ + list->first = op; + list->last = op; + return 0; + } + if (op->num < list->first->num) + { + /* start of the list */ + op->next = list->first; + list->first = op; + return 0; + } + prev = list->first; + while (prev != list->last) + { + /* middle of the list */ + if ((prev->num <= op->num) && (op->num < prev->next->num)) + { + op->next = prev->next; + prev->next = op; + return 0; + } + prev = prev->next; + } + /* end of the list */ + list->last->next = op; + list->last = op; + return 0; +} + +void coap_msg_create(coap_msg_t *msg) +{ + memset(msg, 0, sizeof(coap_msg_t)); + msg->ver = COAP_MSG_VER; + coap_msg_op_list_create(&msg->op_list); +} + +void coap_msg_destroy(coap_msg_t *msg) +{ + coap_msg_op_list_destroy(&msg->op_list); + if (msg->payload != NULL) + { + free(msg->payload); + } + memset(msg, 0, sizeof(coap_msg_t)); +} + +void coap_msg_reset(coap_msg_t *msg) +{ + coap_msg_destroy(msg); + coap_msg_create(msg); +} + +/** + * @brief Check a message for correctness + * + * The following checks from RFC7252 are performed: + * + * An Empty message has the Code field set to 0.00. The Token Length + * field MUST be set to 0 and bytes of data MUST NOT be present after + * the Message ID field. If there are any bytes, they MUST be processed + * as a message format error. + * + * The Reset message MUST echo the Message ID of the Confirmable message + * and MUST be Empty. + * + * A Non-confirmable message always carries either a request or response + * and MUST NOT be Empty. + * + * @param[in] msg Pointer to a message structure + * @returns Operation status + * @retval 0 Success + * @retval <0 Error + */ +static int coap_msg_check(coap_msg_t *msg) +{ + if ((msg->code_class == 0) && (msg->code_detail == 0)) + { + /* empty message */ + if ((msg->type == COAP_MSG_NON) + || (msg->token_len != 0) + || (!coap_msg_op_list_is_empty(&msg->op_list)) + || (msg->payload_len != 0)) + { + return -EBADMSG; + } + } + else + { + /* non-empty message */ + if (msg->type == COAP_MSG_RST) + { + return -EBADMSG; + } + } + return 0; +} + +unsigned coap_msg_check_critical_ops(coap_msg_t *msg) +{ + coap_msg_op_t *op = NULL; + unsigned num = 0; + + op = coap_msg_get_first_op(msg); + while (op != NULL) + { + num = coap_msg_op_get_num(op); + if ((coap_msg_op_num_is_critical(num)) + && (!coap_msg_op_num_is_recognized(num))) + { + return num; /* fail */ + } + op = coap_msg_op_get_next(op); + } + return 0; /* pass */ +} + +unsigned coap_msg_check_unsafe_ops(coap_msg_t *msg) +{ + coap_msg_op_t *op = NULL; + unsigned num = 0; + + op = coap_msg_get_first_op(msg); + while (op != NULL) + { + num = coap_msg_op_get_num(op); + if ((coap_msg_op_num_is_unsafe(num)) + && (!coap_msg_op_num_is_recognized(num))) + { + return num; /* fail */ + } + op = coap_msg_op_get_next(op); + } + return 0; /* pass */ +} + +int coap_msg_parse_type_msg_id(char *buf, size_t len, unsigned *type, unsigned *msg_id) +{ + if (len < 4) + { + return -EBADMSG; + } + *type = (buf[0] >> 4) & 0x03; + *msg_id = ntohs(*((uint16_t *)(&buf[2]))); + return 0; +} + +/** + * @brief Parse the header in a message + * + * @param[out] msg Pointer to a message structure + * @param[in] buf Pointer to a buffer containing the message + * @param[in] len Length of the buffer + * + * @returns Number of bytes parsed or error code + * @retval >0 Number of bytes parsed + * @retval <0 Error + */ +static int coap_msg_parse_hdr(coap_msg_t *msg, char *buf, size_t len) +{ + char *p = buf; + + if (len < 4) + { + return -EBADMSG; + } + msg->ver = (p[0] >> 6) & 0x03; + if (msg->ver != COAP_MSG_VER) + { + return -EINVAL; + } + msg->type = (coap_msg_type_t)((p[0] >> 4) & 0x03); + msg->token_len = p[0] & 0x0f; + if (msg->token_len > sizeof(msg->token)) + { + return -EBADMSG; + } + msg->code_detail = p[1] & 0x1f; + msg->code_class = (p[1] >> 5) & 0x07; + if ((msg->code_class != COAP_MSG_REQ) + && (msg->code_class != COAP_MSG_SUCCESS) + && (msg->code_class != COAP_MSG_CLIENT_ERR) + && (msg->code_class != COAP_MSG_SERVER_ERR)) + { + return -EBADMSG; + } + msg->msg_id = ntohs(*((uint16_t *)(&p[2]))); + p += 4; + len -= 4; + return p - buf; +} + +/** + * @brief Parse the token in a message + * + * @param[out] msg Pointer to a message structure + * @param[in] buf Pointer to a buffer containing the message + * @param[in] len Length of the buffer + * + * @returns Number of bytes parsed or error code + * @retval >0 Number of bytes parsed + * @retval <0 Error + */ +static int coap_msg_parse_token(coap_msg_t *msg, char *buf, size_t len) +{ + if (len < msg->token_len) + { + return -EBADMSG; + } + memcpy(msg->token, buf, msg->token_len); + return msg->token_len; +} + +/** + * @brief Parse an option in a message + * + * @param[in,out] msg Pointer to a message structure + * @param[in] buf Pointer to a buffer containing the message + * @param[in] len Length of the buffer + * + * @returns Number of bytes parsed or error code + * @retval >0 Number of bytes parsed + * @retval <0 Error + */ +static int coap_msg_parse_op(coap_msg_t *msg, char *buf, size_t len) +{ + coap_msg_op_t *prev = NULL; + unsigned op_delta = 0; + unsigned op_len = 0; + unsigned op_num = 0; + char *p = buf; + int ret = 0; + + if (len < 1) + { + return -EBADMSG; + } + op_delta = (p[0] >> 4) & 0x0f; + op_len = p[0] & 0x0f; + if ((op_delta == 15) || (op_len == 15)) + { + return -EBADMSG; + } + p++; + len--; + if (op_delta == 13) + { + if (len < 1) + { + return -EBADMSG; + } + op_delta += p[0]; + p++; + len--; + } + else if (op_delta == 14) + { + if (len < 2) + { + return -EBADMSG; + } + op_delta = 269 + ntohs(*((uint16_t *)(&p[0]))); + p += 2; + len -= 2; + } + if (op_len == 13) + { + if (len < 1) + { + return -EBADMSG; + } + op_len += p[0]; + p++; + len--; + } + else if (op_len == 14) + { + if (len < 2) + { + return -EBADMSG; + } + op_len = 269 + ntohs(*((uint16_t *)(&p[0]))); + p += 2; + len -= 2; + } + if (len < op_len) + { + return -EBADMSG; + } + prev = coap_msg_op_list_get_last(&msg->op_list); + if (prev == NULL) + { + op_num = op_delta; + } + else + { + op_num = coap_msg_op_get_num(prev) + op_delta; + } + ret = coap_msg_op_list_add_last(&msg->op_list, op_num, op_len, p); + if (ret < 0) + { + return ret; + } + p += op_len; + return p - buf; +} + +/** + * @brief Parse the options in a message + * + * @param[in,out] msg Pointer to a message structure + * @param[in] buf Pointer to a buffer containing the message + * @param[in] len Length of the buffer + * + * @returns Number of bytes parsed or error code + * @retval >0 Number of bytes parsed + * @retval <0 Error + */ +static int coap_msg_parse_ops(coap_msg_t *msg, char *buf, size_t len) +{ + int num = 0; + char *p = buf; + + while (1) + { + if (((p[0] & 0xff) == 0xff) || (len == 0)) + { + break; + } + num = coap_msg_parse_op(msg, p, len); + if (num < 0) + { + return num; + } + p += num; + len -= num; + } + return p - buf; +} + +/** + * @brief Parse the payload in a message + * + * @param[out] msg Pointer to a message structure + * @param[in] buf Pointer to a buffer containing the message + * @param[in] len Length of the buffer + * + * @returns Number of bytes parsed or error code + * @retval >0 Number of bytes parsed + * @retval <0 Error + */ +static int coap_msg_parse_payload(coap_msg_t *msg, char *buf, size_t len) +{ + char *p = buf; + + if (len == 0) + { + return 0; + } + if ((p[0] & 0xff) != 0xff) + { + return -EBADMSG; + } + p++; + len--; + if (len == 0) + { + return -EBADMSG; + } + msg->payload = (char *)malloc(len); + if (msg->payload == NULL) + { + return -ENOMEM; + } + memcpy(msg->payload, p, len); + msg->payload_len = len; + p += len; + return p - buf; +} + +int coap_msg_parse(coap_msg_t *msg, char *buf, size_t len) +{ + int num = 0; + char *p = buf; + + coap_msg_reset(msg); + num = coap_msg_parse_hdr(msg, p, len); + if (num < 0) + { + coap_msg_destroy(msg); + return num; + } + p += num; + len -= num; + num = coap_msg_parse_token(msg, p, len); + if (num < 0) + { + coap_msg_destroy(msg); + return num; + } + p += num; + len -= num; + num = coap_msg_parse_ops(msg, p, len); + if (num < 0) + { + coap_msg_destroy(msg); + return num; + } + p += num; + len -= num; + num = coap_msg_parse_payload(msg, p, len); + if (num < 0) + { + coap_msg_destroy(msg); + return num; + } + return coap_msg_check(msg); +} + +int coap_msg_set_type(coap_msg_t *msg, unsigned type) +{ + if ((type != COAP_MSG_CON) + && (type != COAP_MSG_NON) + && (type != COAP_MSG_ACK) + && (type != COAP_MSG_RST)) + { + return -EINVAL; + } + msg->type = (coap_msg_type_t)type; + return 0; +} + +int coap_msg_set_code(coap_msg_t *msg, unsigned code_class, unsigned code_detail) +{ + if (code_class > COAP_MSG_MAX_CODE_CLASS) + { + return -EINVAL; + } + if (code_detail > COAP_MSG_MAX_CODE_DETAIL) + { + return -EINVAL; + } + msg->code_class = code_class; + msg->code_detail = code_detail; + return 0; +} + +int coap_msg_set_msg_id(coap_msg_t *msg, unsigned msg_id) +{ + if (msg_id > COAP_MSG_MAX_MSG_ID) + { + return -EINVAL; + } + msg->msg_id = msg_id; + return 0; +} + +int coap_msg_set_token(coap_msg_t *msg, char *buf, size_t len) +{ + if (len > COAP_MSG_MAX_TOKEN_LEN) + { + return -EINVAL; + } + memcpy(msg->token, buf, len); + msg->token_len = len; + return 0; +} + +int coap_msg_add_op(coap_msg_t *msg, unsigned num, unsigned len, const char *val) +{ + return coap_msg_op_list_add(&msg->op_list, num, len, val); +} + +int coap_msg_set_payload(coap_msg_t *msg, char *buf, size_t len) +{ + msg->payload_len = 0; + if (msg->payload != NULL) + { + free(msg->payload); + msg->payload = NULL; + } + if (len > 0) + { + msg->payload = (char *)malloc(len); + if (msg->payload == NULL) + { + return -ENOMEM; + } + memcpy(msg->payload, buf, len); + msg->payload_len = len; + } + return 0; +} + +/** + * @brief Format the header in a message + * + * @param[in] msg Pointer to a message structure + * @param[out] buf Pointer to a buffer to contain the formatted message + * @param[in] len Length of the buffer + * + * @returns Length of the formatted message or error code + * @retval >0 Length of the formatted message + * @retval <0 Error + */ +static int coap_msg_format_hdr(coap_msg_t *msg, char *buf, size_t len) +{ + uint16_t msg_id = 0; + + if (len < 4) + { + return -ENOSPC; + } + buf[0] = (char)((COAP_MSG_VER << 6) + | ((msg->type & 0x03) << 4) + | (msg->token_len & 0x0f)); + buf[1] = (char)(((msg->code_class & 0x07) << 5) + | (msg->code_detail & 0x1f)); + msg_id = htons(msg->msg_id); + memcpy(&buf[2], &msg_id, 2); + return 4; +} + +/** + * @brief Format the token in a message + * + * @param[in] msg Pointer to a message structure + * @param[out] buf Pointer to a buffer to contain the formatted message + * @param[in] len Length of the buffer + * + * @returns Length of the formatted message or error code + * @retval >0 Length of the formatted message + * @retval <0 Error + */ +static int coap_msg_format_token(coap_msg_t *msg, char *buf, size_t len) +{ + if (len < msg->token_len) + { + return -ENOSPC; + } + memcpy(buf, msg->token, msg->token_len); + return msg->token_len; +} + +/** + * @brief Format an option in a message + * + * @param[in] op Pointer to an option structure + * @param[in] prev_num option number of the previous option + * @param[out] buf Pointer to a buffer to contain the formatted message + * @param[in] len Length of the buffer + * + * @returns Length of the formatted message or error code + * @retval >0 Length of the formatted message + * @retval <0 Error + */ +static int coap_msg_format_op(coap_msg_op_t *op, unsigned prev_num, char *buf, size_t len) +{ + unsigned op_delta = 0; + unsigned num = 0; + uint16_t val = 0; + char *p = buf; + + op_delta = op->num - prev_num; + num++; + + /* option delta */ + if (op_delta >= 269) + { + num += 2; + } + else if (op_delta >= 13) + { + num += 1; + } + + /* option length */ + if (op->len >= 269) + { + num += 2; + } + else if (op->num >= 13) + { + num += 1; + } + + /* option value */ + num += op->len; + if (num > len) + { + return -ENOSPC; + } + + /* option delta */ + if (op_delta >= 269) + { + p[0] = 14 << 4; + } + else if (op_delta >= 13) + { + p[0] = 13 << 4; + } + else + { + p[0] = op_delta << 4; + } + + /* option length */ + if (op->len >= 269) + { + p[0] |= 14; + } + else if (op->len >= 13) + { + p[0] |= 13; + } + else + { + p[0] |= op->len; + } + p++; + len--; + + /* option delta extended */ + if (op_delta >= 269) + { + val = htons(op_delta - 269); + memcpy(p, &val, 2); + p += 2; + len -= 2; + } + else if (op_delta >= 13) + { + p[0] = op_delta - 13; + p++; + len--; + } + + /* option length extended */ + if (op->len >= 269) + { + val = htons(op->len - 269); + memcpy(p, &val, 2); + p += 2; + len -= 2; + } + else if (op->len >= 13) + { + p[0] = op->len - 13; + p++; + len--; + } + + /* option value */ + memcpy(p, op->val, op->len); + p += op->len; + + return p - buf; +} + +/** + * @brief Format the options in a message + * + * @param[in] msg Pointer to a message structure + * @param[out] buf Pointer to a buffer to contain the formatted message + * @param[in] len Length of the buffer + * + * @returns Length of the formatted message or error code + * @retval >0 Length of the formatted message + * @retval <0 Error + */ +static int coap_msg_format_ops(coap_msg_t *msg, char *buf, size_t len) +{ + coap_msg_op_t *op = NULL; + unsigned prev_num = 0; + int num = 0; + char *p = buf; + + op = coap_msg_op_list_get_first(&msg->op_list); + while (op != NULL) + { + num = coap_msg_format_op(op, prev_num, p, len); + if (num < 0) + { + return num; + } + p += num; + len -= num; + prev_num = coap_msg_op_get_num(op); + op = coap_msg_op_get_next(op); + } + return p - buf; +} + +/** + * @brief Format the payload in a message + * + * @param[in] msg Pointer to a message structure + * @param[out] buf Pointer to a buffer to contain the formatted message + * @param[in] len Length of the buffer + * + * @returns Length of the formatted message or error code + * @retval >0 Length of the formatted message + * @retval <0 Error + */ +static int coap_msg_format_payload(coap_msg_t *msg, char *buf, size_t len) +{ + if (msg->payload_len == 0) + { + return 0; + } + if (msg->payload_len + 1 > len) + { + return -ENOSPC; + } + buf[0] = 0xff; + memcpy(&buf[1], msg->payload, msg->payload_len); + return msg->payload_len + 1; +} + +int coap_msg_format(coap_msg_t *msg, char *buf, size_t len) +{ + int num = 0; + char *p = buf; + int ret = 0; + + ret = coap_msg_check(msg); + if (ret != 0) + { + return ret; + } + num = coap_msg_format_hdr(msg, p, len); + if (num < 0) + { + return num; + } + p += num; + len -= num; + num = coap_msg_format_token(msg, p, len); + if (num < 0) + { + return num; + } + p += num; + len -= num; + num = coap_msg_format_ops(msg, p, len); + if (num < 0) + { + return num; + } + p += num; + len -= num; + num = coap_msg_format_payload(msg, p, len); + if (num < 0) + { + return num; + } + p += num; + return p - buf; +} + +int coap_msg_copy(coap_msg_t *dst, coap_msg_t *src) +{ + coap_msg_op_t *op = NULL; + int ret = 0; + + dst->ver = src->ver; + ret = coap_msg_set_type(dst, coap_msg_get_type(src)); + if (ret < 0) + { + return ret; + } + ret = coap_msg_set_code(dst, coap_msg_get_code_class(src), coap_msg_get_code_detail(src)); + if (ret < 0) + { + return ret; + } + ret = coap_msg_set_msg_id(dst, coap_msg_get_msg_id(src)); + if (ret < 0) + { + return ret; + } + ret = coap_msg_set_token(dst, coap_msg_get_token(src), coap_msg_get_token_len(src)); + if (ret < 0) + { + return ret; + } + op = coap_msg_get_first_op(src); + while (op != NULL) + { + ret = coap_msg_add_op(dst, coap_msg_op_get_num(op), coap_msg_op_get_len(op), coap_msg_op_get_val(op)); + if (ret < 0) + { + return ret; + } + op = coap_msg_op_get_next(op); + } + ret = coap_msg_set_payload(dst, coap_msg_get_payload(src), coap_msg_get_payload_len(src)); + if (ret < 0) + { + return ret; + } + return 0; +} +
--- a/main.cpp Wed May 24 07:51:33 2017 +0000 +++ b/main.cpp Thu May 25 11:53:45 2017 +0000 @@ -7,10 +7,11 @@ #include "GPS.h" #include "MDM.h" #include "main.h" - +#include "coap_msg.h" +#include "coap_client.h" + extern "C" { - #include "coap_msg.h" - #include "coap_client.h" + #include "rtc_api.h" } // @@ -31,7 +32,7 @@ unsigned long ulGSMRxCntr=0, ulGSMTxCntr=0 ; //-- Asset unsigned long ulTotalRxCntr=0,ulTotalTxCntr=0; //-- Asset char caLssFinalAssetMsg[150]; -double dLatitude=0,dLongitude=0; +double dLatitude=31.47691,dLongitude=74.34259; static unsigned int uiWakeupCounter=0,uiWatchdogCounter=0; unsigned char ucBtteryLevel=0; MDMParser::DevStatus devStatus={}; @@ -43,6 +44,9 @@ //unsigned int uiAlarmIntervalCounter = 0; //unsigned int uiHeartBeatCounter = 0; + +char ucReturnCode[10]; //Return code array +int alarmtime = 1495698480;//2017-3-29-11:56:7 //---------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------- /* @@ -50,7 +54,7 @@ { LPC_SC->PCONP |=1<1; //timer0 power on LPC_TIM0->MR0 = sec * 23980000; //1sec * sec - LPC_TIM0->MCR = 3; //interrupt and reset control + LPC_TIM0->MCR = 1; //interrupt and reset control //3 = Interrupt & reset timer0 on match //1 = Interrupt only, no reset of timer0 NVIC_EnableIRQ(TIMER0_IRQn); //enable timer0 interrupt @@ -63,33 +67,34 @@ if((LPC_TIM0->IR & 0x01) == 0x01) // if MR0 interrupt, proceed { LPC_TIM0->IR |= 1 << 0; // Clear MR0 interrupt flag - - uiAlarmIntervalCounter++; - if(uiAlarmIntervalCounter >= 10)//20 sec - { - bAlarmIntervalFlag = true; - uiAlarmIntervalCounter = 0; - printf("Alarm Packet time...\r\n"); - } - - uiHeartBeatCounter++; - if(uiHeartBeatCounter >= 60)//2 min - { - bHeartBeatFlag = true; - uiHeartBeatCounter = 0; - printf("Heartbeat Packet time...\r\n"); - } + printf("Timer 0 interrupt generated 10 sec\r\n"); + LPC_TIM0->TCR = 0; } } */ //---------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------- -void rtc_setup(void) +void rtc_setup(time_t t, unsigned int sec) { + struct tm *timeinfo = localtime(&t); + unsigned int min; + rtc_init(); - rtc_write(1490788567);//2017-3-29-11:56:7 + rtc_write(alarmtime);//2017-3-29-11:56:7 + + //min = sec/60; + //sec = sec%60; + //set alarm time + LPC_RTC->ALSEC = timeinfo->tm_sec+sec;//10 sec + //LPC_RTC->ALMIN = timeinfo->tm_min+min;//10 sec + + LPC_RTC->AMR &= (~(1 << 0)) & 0xFF; + + LPC_RTC->ILR = (1 << 0) | (1 << 1); + + printf("RTC Alarm Time Set\r\n"); NVIC_EnableIRQ(RTC_IRQn); @@ -130,7 +135,7 @@ MDMSerial mdm; gmdm = &mdm; us_ticker_init(); - //timer0_init(2); + while(1) { @@ -142,10 +147,8 @@ if(bSendingDataFlag)//&& bAlarmIntervalFlag) { //bAlarmIntervalFlag = false; - unsigned int counter = 50; - while( (ret = gps.getMessage(buf, sizeof(buf))) > 0 || counter > 0) + while( (ret = gps.getMessage(buf, sizeof(buf))) > 0) { - counter--; int len = LENGTH(ret); ulGPSRxCntr = len; if( (PROTOCOL(ret)==GPSParser::NMEA) && (len > 6) ) @@ -200,13 +203,13 @@ sprintf( caLssFinalAssetMsg, "$AQLSS,02,%s,%s*xx",devStatus.imei,devStatus.imsi); printf( "HeartBeat: %s\r\n", caLssFinalAssetMsg ); } - + ssocket = mdm.socketSocket(MDMParser::IPPROTO_UDP, pport); if( ssocket >= 0 ) { mdm.socketSetBlocking(ssocket, 10); ipp = 0x97092263;//0x052753CE; - if (PASS == test_exchange_func(caLssFinalAssetMsg,strlen(caLssFinalAssetMsg))) + if (PASS == test_exchange_func(caLssFinalAssetMsg,strlen(caLssFinalAssetMsg),ucReturnCode)) { printf("\r\n-----------------<pass>-----------------\n"); } @@ -215,22 +218,26 @@ mdm.socketFree(ssocket); responce_checker(); - } + } mdm.disconnect(); - mdm.powerOff(); + /*mdm.powerOff();*/ + if(bSleepModeFlag) { - rtc_setup(); - hal_deepsleep(); - //sleep_mode(); - //uiWakeupCounter++; + rtc_setup(alarmtime,10); + printf("Enter Sleep Mode\r\n"); + hal_sleep(); + printf("Exit Sleep Mode\r\n"); + myled = !myled; + uiWakeupCounter++; } - if(bSendingDataFlag) + + /*if(bSendingDataFlag) wait( 10 ); else wait( 60 ); - myled = !myled; + myled = !myled;*/ } //} } @@ -295,17 +302,9 @@ printf("Ussd Got Answer: \"%s\"\r\n", buffer); } */ -void sleep_mode(void) -{ - /* Deep-Sleep Mode, set SLEEPDEEP bit */ - //SCB->SCR |= 0x4; - LPC_SC->PCON = 0x9; - __WFI(); - //SystemInit(); -} void responce_checker(void) -{/* +{ if(!strcmp((const char*)ucReturnCode,"RC 000")) { printf("Acknowledged RC 000\r\n"); @@ -350,5 +349,5 @@ else if(!strcmp((const char*)ucReturnCode,"EC 002")) { printf("Error Code 002 Received Payload is corrupted\r\n"); - }*/ + } }