mbed 5.4 with sleep mode

Dependencies:   C027_Support mbed-dev

Fork of C027_SupportTest_coap by Umar Naeem

Coap/coap_msg.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_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;
}