pdu.c
Go to the documentation of this file.
/* pdu.c -- CoAP message structure
*
* Copyright (C) 2010--2016 Olaf Bergmann <bergmann@tzi.org>
*
* This file is part of the CoAP library libcoap. Please see
* README for terms of use.
*/
#include "coap_config.h"
#if defined(HAVE_ASSERT_H) && !defined(assert)
# include <assert.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#include "debug.h"
#include "pdu.h"
#include "option.h"
#include "encode.h"
#include "mem.h"
void
coap_pdu_clear(coap_pdu_t *pdu, size_t size) {
assert(pdu);
#ifdef WITH_LWIP
/* the pdu itself is not wiped as opposed to the other implementations,
* because we have to rely on the pbuf to be set there. */
pdu->hdr = pdu->pbuf->payload;
#else
pdu->max_delta = 0;
pdu->data = NULL;
#endif
memset(pdu->hdr, 0, size);
pdu->max_size = size;
pdu->hdr->version = COAP_DEFAULT_VERSION;
/* data is NULL unless explicitly set by coap_add_data() */
pdu->length = sizeof(coap_hdr_t);
}
#ifdef WITH_LWIP
coap_pdu_t *
coap_pdu_from_pbuf(struct pbuf *pbuf)
{
if (pbuf == NULL) return NULL;
LWIP_ASSERT("Can only deal with contiguous PBUFs", pbuf->tot_len == pbuf->len);
LWIP_ASSERT("coap_read needs to receive an exclusive copy of the incoming pbuf", pbuf->ref == 1);
coap_pdu_t *result = coap_malloc_type(COAP_PDU, sizeof(coap_pdu_t));
if (!result) {
pbuf_free(pbuf);
return NULL;
}
memset(result, 0, sizeof(coap_pdu_t));
result->max_size = pbuf->tot_len;
result->length = pbuf->tot_len;
result->hdr = pbuf->payload;
result->pbuf = pbuf;
return result;
}
#endif
coap_pdu_t *
coap_pdu_init(unsigned char type, unsigned char code,
unsigned short id, size_t size) {
coap_pdu_t *pdu;
#ifdef WITH_LWIP
struct pbuf *p;
#endif
assert(size <= COAP_MAX_PDU_SIZE);
/* Size must be large enough to fit the header. */
if (size < sizeof(coap_hdr_t) || size > COAP_MAX_PDU_SIZE)
return NULL;
/* size must be large enough for hdr */
#if defined(WITH_POSIX) || defined(WITH_CONTIKI)
pdu = coap_malloc_type(COAP_PDU, sizeof(coap_pdu_t));
if (!pdu) return NULL;
pdu->hdr = coap_malloc_type(COAP_PDU_BUF, size);
if (pdu->hdr == NULL) {
coap_free_type(COAP_PDU, pdu);
pdu = NULL;
}
#endif /* WITH_POSIX or WITH_CONTIKI */
#ifdef WITH_LWIP
pdu = (coap_pdu_t*)coap_malloc_type(COAP_PDU, sizeof(coap_pdu_t));
if (!pdu) return NULL;
p = pbuf_alloc(PBUF_TRANSPORT, size, PBUF_RAM);
if (p == NULL) {
coap_free_type(COAP_PDU, pdu);
pdu = NULL;
}
#endif
if (pdu) {
#ifdef WITH_LWIP
pdu->pbuf = p;
#endif
coap_pdu_clear(pdu, size);
pdu->hdr->id = id;
pdu->hdr->type = type;
pdu->hdr->code = code;
}
return pdu;
}
coap_pdu_t *
coap_new_pdu(void) {
coap_pdu_t *pdu;
#ifndef WITH_CONTIKI
pdu = coap_pdu_init(0, 0, ntohs(COAP_INVALID_TID), COAP_MAX_PDU_SIZE);
#else /* WITH_CONTIKI */
pdu = coap_pdu_init(0, 0, uip_ntohs(COAP_INVALID_TID), COAP_MAX_PDU_SIZE);
#endif /* WITH_CONTIKI */
#ifndef NDEBUG
if (!pdu)
coap_log(LOG_CRIT, "coap_new_pdu: cannot allocate memory for new PDU\n");
#endif
return pdu;
}
void
coap_delete_pdu(coap_pdu_t *pdu) {
#if defined(WITH_POSIX) || defined(WITH_CONTIKI)
if (pdu != NULL) {
if (pdu->hdr != NULL) {
coap_free_type(COAP_PDU_BUF, pdu->hdr);
}
coap_free_type(COAP_PDU, pdu);
}
#endif
#ifdef WITH_LWIP
if (pdu != NULL) /* accepting double free as the other implementation accept that too */
pbuf_free(pdu->pbuf);
coap_free_type(COAP_PDU, pdu);
#endif
}
int
coap_add_token(coap_pdu_t *pdu, size_t len, const unsigned char *data) {
const size_t HEADERLENGTH = len + 4;
/* must allow for pdu == NULL as callers may rely on this */
if (!pdu || len > 8 || pdu->max_size < HEADERLENGTH)
return 0;
pdu->hdr->token_length = len;
if (len)
memcpy(pdu->hdr->token, data, len);
pdu->max_delta = 0;
pdu->length = HEADERLENGTH;
pdu->data = NULL;
return 1;
}
size_t
coap_add_option(coap_pdu_t *pdu, unsigned short type, unsigned int len, const unsigned char *data) {
size_t optsize;
coap_opt_t *opt;
assert(pdu);
pdu->data = NULL;
if (type < pdu->max_delta) {
warn("coap_add_option: options are not in correct order\n");
return 0;
}
opt = (unsigned char *)pdu->hdr + pdu->length;
/* encode option and check length */
optsize = coap_opt_encode(opt, pdu->max_size - pdu->length,
type - pdu->max_delta, data, len);
if (!optsize) {
warn("coap_add_option: cannot add option\n");
/* error */
return 0;
} else {
pdu->max_delta = type;
pdu->length += optsize;
}
return optsize;
}
unsigned char*
coap_add_option_later(coap_pdu_t *pdu, unsigned short type, unsigned int len) {
size_t optsize;
coap_opt_t *opt;
assert(pdu);
pdu->data = NULL;
if (type < pdu->max_delta) {
warn("coap_add_option: options are not in correct order\n");
return NULL;
}
opt = (unsigned char *)pdu->hdr + pdu->length;
/* encode option and check length */
optsize = coap_opt_encode(opt, pdu->max_size - pdu->length,
type - pdu->max_delta, NULL, len);
if (!optsize) {
warn("coap_add_option: cannot add option\n");
/* error */
return NULL;
} else {
pdu->max_delta = type;
pdu->length += optsize;
}
return ((unsigned char*)opt) + optsize - len;
}
int
coap_add_data(coap_pdu_t *pdu, unsigned int len, const unsigned char *data) {
assert(pdu);
assert(pdu->data == NULL);
if (len == 0)
return 1;
if (pdu->length + len + 1 > pdu->max_size) {
warn("coap_add_data: cannot add: data too large for PDU\n");
assert(pdu->data == NULL);
return 0;
}
pdu->data = (unsigned char *)pdu->hdr + pdu->length;
*pdu->data = COAP_PAYLOAD_START;
pdu->data++;
memcpy(pdu->data, data, len);
pdu->length += len + 1;
return 1;
}
int
coap_get_data(coap_pdu_t *pdu, size_t *len, unsigned char **data) {
assert(pdu);
assert(len);
assert(data);
if (pdu->data) {
*len = (unsigned char *)pdu->hdr + pdu->length - pdu->data;
*data = pdu->data;
} else { /* no data, clear everything */
*len = 0;
*data = NULL;
}
return *data != NULL;
}
#ifndef SHORT_ERROR_RESPONSE
typedef struct {
unsigned char code;
char *phrase;
} error_desc_t;
/* if you change anything here, make sure, that the longest string does not
* exceed COAP_ERROR_PHRASE_LENGTH. */
error_desc_t coap_error[] = {
{ COAP_RESPONSE_CODE(201), "Created" },
{ COAP_RESPONSE_CODE(202), "Deleted" },
{ COAP_RESPONSE_CODE(203), "Valid" },
{ COAP_RESPONSE_CODE(204), "Changed" },
{ COAP_RESPONSE_CODE(205), "Content" },
{ COAP_RESPONSE_CODE(231), "Continue" },
{ COAP_RESPONSE_CODE(400), "Bad Request" },
{ COAP_RESPONSE_CODE(401), "Unauthorized" },
{ COAP_RESPONSE_CODE(402), "Bad Option" },
{ COAP_RESPONSE_CODE(403), "Forbidden" },
{ COAP_RESPONSE_CODE(404), "Not Found" },
{ COAP_RESPONSE_CODE(405), "Method Not Allowed" },
{ COAP_RESPONSE_CODE(406), "Not Acceptable" },
{ COAP_RESPONSE_CODE(408), "Request Entity Incomplete" },
{ COAP_RESPONSE_CODE(412), "Precondition Failed" },
{ COAP_RESPONSE_CODE(413), "Request Entity Too Large" },
{ COAP_RESPONSE_CODE(415), "Unsupported Content-Format" },
{ COAP_RESPONSE_CODE(500), "Internal Server Error" },
{ COAP_RESPONSE_CODE(501), "Not Implemented" },
{ COAP_RESPONSE_CODE(502), "Bad Gateway" },
{ COAP_RESPONSE_CODE(503), "Service Unavailable" },
{ COAP_RESPONSE_CODE(504), "Gateway Timeout" },
{ COAP_RESPONSE_CODE(505), "Proxying Not Supported" },
{ 0, NULL } /* end marker */
};
char *
coap_response_phrase(unsigned char code) {
int i;
for (i = 0; coap_error[i].code; ++i) {
if (coap_error[i].code == code)
return coap_error[i].phrase;
}
return NULL;
}
#endif
static size_t
next_option_safe(coap_opt_t **optp, size_t *length) {
coap_option_t option;
size_t optsize;
assert(optp); assert(*optp);
assert(length);
optsize = coap_opt_parse(*optp, *length, &option);
if (optsize) {
assert(optsize <= *length);
*optp += optsize;
*length -= optsize;
}
return optsize;
}
int
coap_pdu_parse(unsigned char *data, size_t length, coap_pdu_t *pdu) {
coap_opt_t *opt;
assert(data);
assert(pdu);
if (pdu->max_size < length) {
debug("insufficient space to store parsed PDU\n");
return 0;
}
if (length < sizeof(coap_hdr_t)) {
debug("discarded invalid PDU\n");
}
#ifdef WITH_LWIP
/* this verifies that with the classical copy-at-parse-time and lwip's
* zerocopy-into-place approaches, both share the same idea of destination
* addresses */
LWIP_ASSERT("coap_pdu_parse with unexpected addresses", data == (void*)pdu->hdr);
LWIP_ASSERT("coap_pdu_parse with unexpected length", length == pdu->length);
#else
pdu->hdr->version = data[0] >> 6;
pdu->hdr->type = (data[0] >> 4) & 0x03;
pdu->hdr->token_length = data[0] & 0x0f;
pdu->hdr->code = data[1];
#endif
pdu->data = NULL;
/* sanity checks */
if (pdu->hdr->code == 0) {
if (length != sizeof(coap_hdr_t) || pdu->hdr->token_length) {
debug("coap_pdu_parse: empty message is not empty\n");
goto discard;
}
}
if (length < sizeof(coap_hdr_t) + pdu->hdr->token_length
|| pdu->hdr->token_length > 8) {
debug("coap_pdu_parse: invalid Token\n");
goto discard;
}
#ifndef WITH_LWIP
/* Copy message id in network byte order, so we can easily write the
* response back to the network. */
memcpy(&pdu->hdr->id, data + 2, 2);
/* Append data (including the Token) to pdu structure, if any. */
if (length > sizeof(coap_hdr_t)) {
memcpy(pdu->hdr + 1, data + sizeof(coap_hdr_t), length - sizeof(coap_hdr_t));
}
pdu->length = length;
/* Finally calculate beginning of data block and thereby check integrity
* of the PDU structure. */
#endif
/* skip header + token */
length -= (pdu->hdr->token_length + sizeof(coap_hdr_t));
opt = (unsigned char *)(pdu->hdr + 1) + pdu->hdr->token_length;
while (length && *opt != COAP_PAYLOAD_START) {
if (!next_option_safe(&opt, (size_t *)&length)) {
debug("coap_pdu_parse: drop\n");
goto discard;
}
}
/* end of packet or start marker */
if (length) {
assert(*opt == COAP_PAYLOAD_START);
opt++; length--;
if (!length) {
debug("coap_pdu_parse: message ending in payload start marker\n");
goto discard;
}
debug("set data to %p (pdu ends at %p)\n", (unsigned char *)opt,
(unsigned char *)pdu->hdr + pdu->length);
pdu->data = (unsigned char *)opt;
}
return 1;
discard:
return 0;
}
encode.h
coap_hdr_t::token
unsigned char token[]
Definition: pdu.h:192
warn
#define warn(...)
Definition: debug.h:65
COAP_RESPONSE_CODE
#define COAP_RESPONSE_CODE(N)
Definition: pdu.h:96
mem.h
debug.h
coap_add_option_later
unsigned char * coap_add_option_later(coap_pdu_t *pdu, unsigned short type, unsigned int len)
de-duplicate code with coap_add_option
Definition: pdu.c:203
coap_hdr_t::id
unsigned short id
Definition: pdu.h:191
next_option_safe
static size_t next_option_safe(coap_opt_t **optp, size_t *length)
Advances *optp to next option if still in PDU.
Definition: pdu.c:325
coap_pdu_t::length
unsigned short length
PDU length (including header, options, data)
Definition: pdu.h:234
coap_add_token
int coap_add_token(coap_pdu_t *pdu, size_t len, const unsigned char *data)
Adds token of length len to pdu.
Definition: pdu.c:153
coap_hdr_t
Definition: pdu.h:185
coap_pdu_parse
int coap_pdu_parse(unsigned char *data, size_t length, coap_pdu_t *pdu)
Parses data into the CoAP PDU structure given in result.
Definition: pdu.c:344
error_desc_t
Definition: pdu.c:274
coap_get_data
int coap_get_data(coap_pdu_t *pdu, size_t *len, unsigned char **data)
Retrieves the length and data pointer of specified PDU.
Definition: pdu.c:257
option.h
Helpers for handling options in CoAP PDUs.
coap_pdu_t::hdr
coap_hdr_t * hdr
Address of the first byte of the CoAP message.
Definition: pdu.h:229
coap_pdu_clear
void coap_pdu_clear(coap_pdu_t *pdu, size_t size)
Clears any contents from pdu and resets version field, length and data pointers.
Definition: pdu.c:29
coap_pdu_t::max_delta
unsigned short max_delta
highest option number
Definition: pdu.h:233
debug
#define debug(...)
Definition: debug.h:66
COAP_DEFAULT_VERSION
#define COAP_DEFAULT_VERSION
Definition: pdu.h:30
COAP_INVALID_TID
#define COAP_INVALID_TID
Indicates an invalid transaction id.
Definition: pdu.h:166
coap_hdr_t::code
unsigned int code
Definition: pdu.h:189
coap_pdu_t
Header structure for CoAP PDUs.
Definition: pdu.h:227
COAP_PDU_BUF
Definition: mem.h:38
coap_error
error_desc_t coap_error[]
Definition: pdu.c:281
coap_option_t
Representation of CoAP options.
Definition: option.h:29
assert
#define assert(...)
Definition: mem.c:17
coap_pdu_t::max_size
size_t max_size
allocated storage for options and data
Definition: pdu.h:228
coap_opt_parse
size_t coap_opt_parse(const coap_opt_t *opt, size_t length, coap_option_t *result)
Parses the option pointed to by opt into result.
Definition: option.c:39
coap_add_option
size_t coap_add_option(coap_pdu_t *pdu, unsigned short type, unsigned int len, const unsigned char *data)
de-duplicate code with coap_add_option_later
Definition: pdu.c:171
error_desc_t::phrase
char * phrase
Definition: pdu.c:276
COAP_MAX_PDU_SIZE
#define COAP_MAX_PDU_SIZE
Definition: pdu.h:27
coap_add_data
int coap_add_data(coap_pdu_t *pdu, unsigned int len, const unsigned char *data)
Adds given data to the pdu that is passed as first parameter.
Definition: pdu.c:234
LOG_CRIT
Definition: debug.h:29
pdu.h
Pre-defined constants that reflect defaults for CoAP.
coap_delete_pdu
void coap_delete_pdu(coap_pdu_t *pdu)
Definition: pdu.c:136
coap_pdu_init
coap_pdu_t * coap_pdu_init(unsigned char type, unsigned char code, unsigned short id, size_t size)
Creates a new CoAP PDU of given size (must be large enough to hold the basic CoAP message header (coa...
Definition: pdu.c:75
COAP_PAYLOAD_START
#define COAP_PAYLOAD_START
Definition: pdu.h:207
error_desc_t::code
unsigned char code
Definition: pdu.c:275
coap_hdr_t::token_length
unsigned int token_length
Definition: pdu.h:186
coap_malloc_type
void * coap_malloc_type(coap_memory_tag_t type, size_t size)
Allocates a chunk of size bytes and returns a pointer to the newly allocated memory.
coap_hdr_t::version
unsigned int version
Definition: pdu.h:188
coap_new_pdu
coap_pdu_t * coap_new_pdu(void)
Creates a new CoAP PDU.
Definition: pdu.c:119
coap_hdr_t::type
unsigned int type
Definition: pdu.h:187
coap_opt_t
unsigned char coap_opt_t
Use byte-oriented access methods here because sliding a complex struct coap_opt_t over the data buffe...
Definition: option.h:25
coap_free_type
void coap_free_type(coap_memory_tag_t type, void *p)
Releases the memory that was allocated by coap_malloc_type().
coap_log
#define coap_log(...)
Definition: debug.h:58
coap_response_phrase
char * coap_response_phrase(unsigned char code)
Returns a human-readable response phrase for the specified CoAP response code.
Definition: pdu.c:309
COAP_PDU
Definition: mem.h:37
coap_pdu_t::data
unsigned char * data
payload
Definition: pdu.h:235
coap_opt_encode
size_t coap_opt_encode(coap_opt_t *opt, size_t maxlen, unsigned short delta, const unsigned char *val, size_t length)
Encodes option with given delta into opt.
Definition: option.c:384