option.c
Go to the documentation of this file.
/*
* option.c -- helpers for handling options in CoAP PDUs
*
* Copyright (C) 2010-2013 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 <stdio.h>
#include <string.h>
#include "option.h"
#include "encode.h" /* for coap_fls() */
#include "debug.h"
coap_opt_t *
options_start(coap_pdu_t *pdu) {
if (pdu && pdu->hdr &&
(pdu->hdr->token + pdu->hdr->token_length
< (unsigned char *)pdu->hdr + pdu->length)) {
coap_opt_t *opt = pdu->hdr->token + pdu->hdr->token_length;
return (*opt == COAP_PAYLOAD_START) ? NULL : opt;
} else
return NULL;
}
size_t
coap_opt_parse(const coap_opt_t *opt, size_t length, coap_option_t *result) {
const coap_opt_t *opt_start = opt; /* store where parsing starts */
assert(opt); assert(result);
#define ADVANCE_OPT(o,e,step) if ((e) < step) { \
debug("cannot advance opt past end\n"); \
return 0; \
} else { \
(e) -= step; \
(o) = ((unsigned char *)(o)) + step; \
}
if (length < 1)
return 0;
result->delta = (*opt & 0xf0) >> 4;
result->length = *opt & 0x0f;
switch(result->delta) {
case 15:
if (*opt != COAP_PAYLOAD_START) {
debug("ignored reserved option delta 15\n");
}
return 0;
case 14:
/* Handle two-byte value: First, the MSB + 269 is stored as delta value.
* After that, the option pointer is advanced to the LSB which is handled
* just like case delta == 13. */
ADVANCE_OPT(opt,length,1);
result->delta = ((*opt & 0xff) << 8) + 269;
if (result->delta < 269) {
debug("delta too large\n");
return 0;
}
/* fall through */
case 13:
ADVANCE_OPT(opt,length,1);
result->delta += *opt & 0xff;
break;
default:
;
}
switch(result->length) {
case 15:
debug("found reserved option length 15\n");
return 0;
case 14:
/* Handle two-byte value: First, the MSB + 269 is stored as delta value.
* After that, the option pointer is advanced to the LSB which is handled
* just like case delta == 13. */
ADVANCE_OPT(opt,length,1);
result->length = ((*opt & 0xff) << 8) + 269;
/* fall through */
case 13:
ADVANCE_OPT(opt,length,1);
result->length += *opt & 0xff;
break;
default:
;
}
ADVANCE_OPT(opt,length,1);
/* opt now points to value, if present */
result->value = (unsigned char *)opt;
if (length < result->length) {
debug("invalid option length\n");
return 0;
}
#undef ADVANCE_OPT
return (opt + result->length) - opt_start;
}
coap_opt_iterator_t *
coap_option_iterator_init(coap_pdu_t *pdu, coap_opt_iterator_t *oi,
const coap_opt_filter_t filter) {
assert(pdu);
assert(pdu->hdr);
assert(oi);
memset(oi, 0, sizeof(coap_opt_iterator_t));
oi->next_option = (unsigned char *)pdu->hdr + sizeof(coap_hdr_t)
+ pdu->hdr->token_length;
if ((unsigned char *)pdu->hdr + pdu->length <= oi->next_option) {
oi->bad = 1;
return NULL;
}
assert((sizeof(coap_hdr_t) + pdu->hdr->token_length) <= pdu->length);
oi->length = pdu->length - (sizeof(coap_hdr_t) + pdu->hdr->token_length);
if (filter) {
memcpy(oi->filter, filter, sizeof(coap_opt_filter_t));
oi->filtered = 1;
}
return oi;
}
static inline int
opt_finished(coap_opt_iterator_t *oi) {
assert(oi);
if (oi->bad || oi->length == 0 ||
!oi->next_option || *oi->next_option == COAP_PAYLOAD_START) {
oi->bad = 1;
}
return oi->bad;
}
coap_opt_t *
coap_option_next(coap_opt_iterator_t *oi) {
coap_option_t option;
coap_opt_t *current_opt = NULL;
size_t optsize;
int b; /* to store result of coap_option_getb() */
assert(oi);
if (opt_finished(oi))
return NULL;
while (1) {
/* oi->option always points to the next option to deliver; as
* opt_finished() filters out any bad conditions, we can assume that
* oi->option is valid. */
current_opt = oi->next_option;
/* Advance internal pointer to next option, skipping any option that
* is not included in oi->filter. */
optsize = coap_opt_parse(oi->next_option, oi->length, &option);
if (optsize) {
assert(optsize <= oi->length);
oi->next_option += optsize;
oi->length -= optsize;
oi->type += option.delta;
} else { /* current option is malformed */
oi->bad = 1;
return NULL;
}
/* Exit the while loop when:
* - no filtering is done at all
* - the filter matches for the current option
* - the filter is too small for the current option number
*/
if (!oi->filtered ||
(b = coap_option_getb(oi->filter, oi->type)) > 0)
break;
else if (b < 0) { /* filter too small, cannot proceed */
oi->bad = 1;
return NULL;
}
}
return current_opt;
}
coap_opt_t *
coap_check_option(coap_pdu_t *pdu, unsigned short type,
coap_opt_iterator_t *oi) {
coap_opt_filter_t f;
coap_option_filter_clear(f);
coap_option_setb(f, type);
coap_option_iterator_init(pdu, oi, f);
return coap_option_next(oi);
}
unsigned short
coap_opt_delta(const coap_opt_t *opt) {
unsigned short n;
n = (*opt++ & 0xf0) >> 4;
switch (n) {
case 15: /* error */
warn("coap_opt_delta: illegal option delta\n");
/* This case usually should not happen, hence we do not have a
* proper way to indicate an error. */
return 0;
case 14:
/* Handle two-byte value: First, the MSB + 269 is stored as delta value.
* After that, the option pointer is advanced to the LSB which is handled
* just like case delta == 13. */
n = ((*opt++ & 0xff) << 8) + 269;
/* fall through */
case 13:
n += *opt & 0xff;
break;
default: /* n already contains the actual delta value */
;
}
return n;
}
unsigned short
coap_opt_length(const coap_opt_t *opt) {
unsigned short length;
length = *opt & 0x0f;
switch (*opt & 0xf0) {
case 0xf0:
debug("illegal option delta\n");
return 0;
case 0xe0:
++opt;
/* fall through to skip another byte */
case 0xd0:
++opt;
/* fall through to skip another byte */
default:
++opt;
}
switch (length) {
case 0x0f:
debug("illegal option length\n");
return 0;
case 0x0e:
length = (*opt++ << 8) + 269;
/* fall through */
case 0x0d:
length += *opt++;
break;
default:
;
}
return length;
}
unsigned char *
coap_opt_value(coap_opt_t *opt) {
size_t ofs = 1;
switch (*opt & 0xf0) {
case 0xf0:
debug("illegal option delta\n");
return 0;
case 0xe0:
++ofs;
/* fall through */
case 0xd0:
++ofs;
break;
default:
;
}
switch (*opt & 0x0f) {
case 0x0f:
debug("illegal option length\n");
return 0;
case 0x0e:
++ofs;
/* fall through */
case 0x0d:
++ofs;
break;
default:
;
}
return (unsigned char *)opt + ofs;
}
size_t
coap_opt_size(const coap_opt_t *opt) {
coap_option_t option;
/* we must assume that opt is encoded correctly */
return coap_opt_parse(opt, (size_t)-1, &option);
}
size_t
coap_opt_setheader(coap_opt_t *opt, size_t maxlen,
unsigned short delta, size_t length) {
size_t skip = 0;
assert(opt);
if (maxlen == 0) /* need at least one byte */
return 0;
if (delta < 13) {
opt[0] = delta << 4;
} else if (delta < 270) {
if (maxlen < 2) {
debug("insufficient space to encode option delta %d\n", delta);
return 0;
}
opt[0] = 0xd0;
opt[++skip] = delta - 13;
} else {
if (maxlen < 3) {
debug("insufficient space to encode option delta %d\n", delta);
return 0;
}
opt[0] = 0xe0;
opt[++skip] = ((delta - 269) >> 8) & 0xff;
opt[++skip] = (delta - 269) & 0xff;
}
if (length < 13) {
opt[0] |= length & 0x0f;
} else if (length < 270) {
if (maxlen < skip + 2) {
debug("insufficient space to encode option length %zu\n", length);
return 0;
}
opt[0] |= 0x0d;
opt[++skip] = length - 13;
} else {
if (maxlen < skip + 3) {
debug("insufficient space to encode option delta %d\n", delta);
return 0;
}
opt[0] |= 0x0e;
opt[++skip] = ((length - 269) >> 8) & 0xff;
opt[++skip] = (length - 269) & 0xff;
}
return skip + 1;
}
size_t
coap_opt_encode(coap_opt_t *opt, size_t maxlen, unsigned short delta,
const unsigned char *val, size_t length) {
size_t l = 1;
l = coap_opt_setheader(opt, maxlen, delta, length);
assert(l <= maxlen);
if (!l) {
debug("coap_opt_encode: cannot set option header\n");
return 0;
}
maxlen -= l;
opt += l;
if (maxlen < length) {
debug("coap_opt_encode: option too large for buffer\n");
return 0;
}
if (val) /* better be safe here */
memcpy(opt, val, length);
return l + length;
}
/* coap_opt_filter_t has the following internal structure: */
typedef struct {
uint16_t mask;
#define LONG_MASK ((1 << COAP_OPT_FILTER_LONG) - 1)
#define SHORT_MASK \
(~LONG_MASK & ((1 << (COAP_OPT_FILTER_LONG + COAP_OPT_FILTER_SHORT)) - 1))
uint16_t long_opts[COAP_OPT_FILTER_LONG];
uint8_t short_opts[COAP_OPT_FILTER_SHORT];
} opt_filter;
static inline int
is_long_option(unsigned short type) { return type > 255; }
enum filter_op_t { FILTER_SET, FILTER_CLEAR, FILTER_GET };
static int
coap_option_filter_op(coap_opt_filter_t filter,
unsigned short type,
enum filter_op_t op) {
size_t index = 0;
opt_filter *of = (opt_filter *)filter;
uint16_t nr, mask = 0;
if (is_long_option(type)) {
mask = LONG_MASK;
for (nr = 1; index < COAP_OPT_FILTER_LONG; nr <<= 1, index++) {
if (((of->mask & nr) > 0) && (of->long_opts[index] == type)) {
if (op == FILTER_CLEAR) {
of->mask &= ~nr;
}
return 1;
}
}
} else {
mask = SHORT_MASK;
for (nr = 1 << COAP_OPT_FILTER_LONG; index < COAP_OPT_FILTER_SHORT;
nr <<= 1, index++) {
if (((of->mask & nr) > 0) && (of->short_opts[index] == (type & 0xff))) {
if (op == FILTER_CLEAR) {
of->mask &= ~nr;
}
return 1;
}
}
}
/* type was not found, so there is nothing to do if op is CLEAR or GET */
if ((op == FILTER_CLEAR) || (op == FILTER_GET)) {
return 0;
}
/* handle FILTER_SET: */
index = coap_fls(~of->mask & mask);
if (!index) {
return 0;
}
if (is_long_option(type)) {
of->long_opts[index - 1] = type;
} else {
of->short_opts[index - COAP_OPT_FILTER_LONG - 1] = type;
}
of->mask |= 1 << (index - 1);
return 1;
}
int
coap_option_filter_set(coap_opt_filter_t filter, unsigned short type) {
return coap_option_filter_op(filter, type, FILTER_SET);
}
int
coap_option_filter_unset(coap_opt_filter_t filter, unsigned short type) {
return coap_option_filter_op(filter, type, FILTER_CLEAR);
}
int
coap_option_filter_get(const coap_opt_filter_t filter, unsigned short type) {
/* Ugly cast to make the const go away (FILTER_GET wont change filter
* but as _set and _unset do, the function does not take a const). */
return coap_option_filter_op((uint16_t *)filter, type, FILTER_GET);
}
LONG_MASK
#define LONG_MASK
Definition: option.c:414
opt_filter::mask
uint16_t mask
Definition: option.c:412
SHORT_MASK
#define SHORT_MASK
Definition: option.c:415
encode.h
coap_hdr_t::token
unsigned char token[]
Definition: pdu.h:192
coap_option_filter_set
int coap_option_filter_set(coap_opt_filter_t filter, unsigned short type)
Sets the corresponding entry for type in filter.
Definition: option.c:509
warn
#define warn(...)
Definition: debug.h:65
coap_option_t::delta
unsigned short delta
Definition: option.h:30
debug.h
COAP_OPT_FILTER_LONG
#define COAP_OPT_FILTER_LONG
The number of option types above 255 that can be stored in an option filter.
Definition: option.h:100
coap_opt_value
unsigned char * coap_opt_value(coap_opt_t *opt)
Returns a pointer to the value of the given option.
Definition: option.c:286
opt_filter
Definition: option.c:411
FILTER_SET
Definition: option.c:427
coap_pdu_t::length
unsigned short length
PDU length (including header, options, data)
Definition: pdu.h:234
coap_hdr_t
Definition: pdu.h:185
coap_opt_iterator_t::length
size_t length
remaining length of PDU
Definition: option.h:254
filter_op_t
filter_op_t
Operation specifiers for coap_filter_op().
Definition: option.c:427
coap_option_getb
static int coap_option_getb(const coap_opt_filter_t filter, unsigned short type)
Gets the corresponding bit for type in filter.
Definition: option.h:233
coap_option_next
coap_opt_t * coap_option_next(coap_opt_iterator_t *oi)
Updates the iterator oi to point to the next option.
Definition: option.c:159
coap_option_filter_op
static int coap_option_filter_op(coap_opt_filter_t filter, unsigned short type, enum filter_op_t op)
Applies op on filter with respect to type.
Definition: option.c:449
coap_opt_iterator_t::filter
coap_opt_filter_t filter
option filter
Definition: option.h:259
coap_fls
int coap_fls(unsigned int i)
Definition: encode.c:17
option.h
Helpers for handling options in CoAP PDUs.
coap_option_setb
static int coap_option_setb(coap_opt_filter_t filter, unsigned short type)
Sets the corresponding bit for type in filter.
Definition: option.h:199
coap_pdu_t::hdr
coap_hdr_t * hdr
Address of the first byte of the CoAP message.
Definition: pdu.h:229
opt_finished
static int opt_finished(coap_opt_iterator_t *oi)
Definition: option.c:147
COAP_OPT_FILTER_SHORT
#define COAP_OPT_FILTER_SHORT
The number of option types below 256 that can be stored in an option filter.
Definition: option.h:92
debug
#define debug(...)
Definition: debug.h:66
coap_pdu_t
Header structure for CoAP PDUs.
Definition: pdu.h:227
coap_option_iterator_init
coap_opt_iterator_t * coap_option_iterator_init(coap_pdu_t *pdu, coap_opt_iterator_t *oi, const coap_opt_filter_t filter)
Initializes the given option iterator oi to point to the beginning of the pdu&#39;s option list...
Definition: option.c:120
coap_option_t::length
size_t length
Definition: option.h:31
FILTER_CLEAR
Definition: option.c:427
coap_option_t
Representation of CoAP options.
Definition: option.h:29
assert
#define assert(...)
Definition: mem.c:17
FILTER_GET
Definition: option.c:427
coap_opt_iterator_t::next_option
coap_opt_t * next_option
pointer to the unparsed next option
Definition: option.h:258
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_opt_delta
unsigned short coap_opt_delta(const coap_opt_t *opt)
Decodes the delta value of the next option.
Definition: option.c:222
coap_opt_iterator_t
Iterator to run through PDU options.
Definition: option.h:253
coap_opt_iterator_t::bad
unsigned int bad
iterator object is ok if not set
Definition: option.h:256
coap_opt_iterator_t::filtered
unsigned int filtered
denotes whether or not filter is used
Definition: option.h:257
coap_opt_iterator_t::type
unsigned short type
decoded option type
Definition: option.h:255
coap_opt_filter_t
uint16_t coap_opt_filter_t[COAP_OPT_FILTER_SIZE]
Fixed-size vector we use for option filtering.
Definition: option.h:135
coap_option_filter_clear
static void coap_option_filter_clear(coap_opt_filter_t f)
Clears filter f.
Definition: option.h:146
COAP_PAYLOAD_START
#define COAP_PAYLOAD_START
Definition: pdu.h:207
ADVANCE_OPT
#define ADVANCE_OPT(o, e, step)
coap_hdr_t::token_length
unsigned int token_length
Definition: pdu.h:186
opt_filter::long_opts
uint16_t long_opts[COAP_OPT_FILTER_LONG]
Definition: option.c:418
coap_opt_setheader
size_t coap_opt_setheader(coap_opt_t *opt, size_t maxlen, unsigned short delta, size_t length)
Encodes the given delta and length values into opt.
Definition: option.c:329
opt_filter::short_opts
uint8_t short_opts[COAP_OPT_FILTER_SHORT]
Definition: option.c:419
coap_option_filter_get
int coap_option_filter_get(const coap_opt_filter_t filter, unsigned short type)
Checks if type is contained in filter.
Definition: option.c:519
options_start
coap_opt_t * options_start(coap_pdu_t *pdu)
Calculates the beginning of the PDU&#39;s option section.
Definition: option.c:25
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_check_option
coap_opt_t * coap_check_option(coap_pdu_t *pdu, unsigned short type, coap_opt_iterator_t *oi)
Retrieves the first option of type type from pdu.
Definition: option.c:209
coap_opt_size
size_t coap_opt_size(const coap_opt_t *opt)
Returns the size of the given option, taking into account a possible option jump. ...
Definition: option.c:321
is_long_option
static int is_long_option(unsigned short type)
Returns true iff type denotes an option type larger than 255.
Definition: option.c:424
coap_option_t::value
unsigned char * value
Definition: option.h:32
coap_opt_length
unsigned short coap_opt_length(const coap_opt_t *opt)
Returns the length of the given option.
Definition: option.c:251
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
coap_option_filter_unset
int coap_option_filter_unset(coap_opt_filter_t filter, unsigned short type)
Clears the corresponding entry for type in filter.
Definition: option.c:514