mbed 5.4 with sleep mode

Dependencies:   C027_Support mbed-dev

Fork of C027_SupportTest_coap by Umar Naeem

Coap/coap_client.c

Committer:
ranaumarnaeem
Date:
2017-03-24
Revision:
34:d6ce8f961b8b

File content as of revision 34:d6ce8f961b8b:

/*
 * 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                                            *
 ****************************************************************************************************/

int coap_client_create(coap_client_t *client,
                       const char *host,
                       const char *port)
{
    strncpy(client->server_host, host, sizeof(client->server_host) - 1);
    strncpy(client->server_port, port, sizeof(client->server_port) - 1);

    printf("Connected to host %s and port %s", client->server_host, client->server_port);
    return 0;
}

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;
    }

    printf("Sent to host %s and port %s\n", client->server_host, client->server_port);
    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;
    }
    printf("Received from host %s and port %s\n", client->server_host, client->server_port);
    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 host %s and port %s\n", client->server_host, client->server_port);
    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;

    /* 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)
    {
        int start_time = getseconds();
        while(1)
        {
            num = coap_client_recv(client, resp);
            if (num < 0)
            {
                return num;
            }
            else if(num > 0)
            {
                break;    
            }
            int stop_time = getseconds();
            if((stop_time - start_time) > 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;

    /*  wait for piggy-backed response in ack message
     *  or ack message and separate response message
     */
    printf("Expecting acknowledgement from host %s and port %s\n", client->server_host, client->server_port);
    while (1)
    {
        int start_time = getseconds();
        while(1)
        {
            num = coap_client_recv(client, resp);
            if (num < 0)
            {
                return num;
            }
            else if(num > 0)
            {
                break;    
            }
            int stop_time = getseconds();
            if((stop_time - start_time) > COAP_CLIENT_ACK_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)
            {
                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;

    printf("Expecting response from host %s and port %s\n", client->server_host, client->server_port);
    while (1)
    {
        int start_time = getseconds();
        while(1)
        {
            num = coap_client_recv(client, resp);
            if (num < 0)
            {
                return num;
            }
            else if(num > 0)
            {
                break;    
            }
            int stop_time = getseconds();
            if((stop_time - start_time) > 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;
    }

    if (coap_msg_get_type(req) == COAP_MSG_CON)
    {
        printf("Sending confirmable request to host %s and port %s\n", client->server_host, client->server_port);
    }
    else if (coap_msg_get_type(req) == COAP_MSG_NON)
    {
        printf("Sending non-confirmable request to host %s and port %s\n", client->server_host, client->server_port);
    }

    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----------------------------------------\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 = "test 1: send a confirmable request 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);

    ret = coap_client_create(&client,
                             test1_data.host,
                             test1_data.port);

    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]);
    }
    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;
    }
    if (test_resp->payload_len != coap_msg_get_payload_len(resp))
    {
        return FAIL;
    }
    else if (memcmp(test_resp->payload, coap_msg_get_payload(resp), test_resp->payload_len))
    {
        return FAIL;
    }
    return PASS;
}