Loading...
Searching...
No Matches
coap_option.c
Go to the documentation of this file.
/*
* coap_option.c -- helpers for handling options in CoAP PDUs
*
* Copyright (C) 2010-2013,2022-2024 Olaf Bergmann <bergmann@tzi.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*
* This file is part of the CoAP library libcoap. Please see
* README for terms of use.
*/
#include "coap3/coap_libcoap_build.h"
#include <stdio.h>
#include <string.h>
#define ADVANCE_OPT(o,e,step) if ((e) < step) { \
coap_log_debug("cannot advance opt past end\n"); \
return 0; \
} else { \
(e) -= step; \
(o) = ((o)) + step; \
}
/*
* Used to prevent access to *opt when pointing to after end of buffer
* after doing a ADVANCE_OPT()
*/
#define ADVANCE_OPT_CHECK(o,e,step) do { \
ADVANCE_OPT(o,e,step); \
if ((e) < 1) \
return 0; \
} while (0)
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);
if (length < 1)
return 0;
result->delta = (*opt & 0xf0) >> 4;
result->length = *opt & 0x0f;
switch (result->delta) {
case 15:
if (*opt != COAP_PAYLOAD_START) {
coap_log_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_CHECK(opt,length,1);
result->delta = ((*opt & 0xff) << 8) + 269;
if (result->delta < 269) {
coap_log_debug("delta too large\n");
return 0;
}
/* fall through */
case 13:
ADVANCE_OPT_CHECK(opt,length,1);
result->delta += *opt & 0xff;
break;
default:
;
}
switch (result->length) {
case 15:
coap_log_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_CHECK(opt,length,1);
result->length = ((*opt & 0xff) << 8) + 269;
/* fall through */
case 13:
ADVANCE_OPT_CHECK(opt,length,1);
result->length += *opt & 0xff;
break;
default:
;
}
/* ADVANCE_OPT() is correct here */
ADVANCE_OPT(opt,length,1);
/* opt now points to value, if present */
result->value = opt;
if (length < result->length) {
coap_log_debug("invalid option length\n");
return 0;
}
#undef ADVANCE_OPT
#undef ADVANCE_OPT_CHECK
return (opt + result->length) - opt_start;
}
coap_opt_iterator_t *
coap_option_iterator_init(const coap_pdu_t *pdu, coap_opt_iterator_t *oi,
const coap_opt_filter_t *filter) {
assert(pdu);
assert(pdu->token);
assert(oi);
memset(oi, 0, sizeof(coap_opt_iterator_t));
oi->next_option = pdu->token + pdu->e_token_length;
if (pdu->token + pdu->used_size <= oi->next_option) {
oi->bad = 1;
return NULL;
}
oi->length = pdu->used_size - pdu->e_token_length;
if (filter) {
memcpy(&oi->filter, filter, sizeof(coap_opt_filter_t));
oi->filtered = 1;
}
return oi;
}
COAP_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;
assert(oi);
if (opt_finished(oi))
return NULL;
while (1) {
/* oi->next_option always points to the next option to deliver; as
* opt_finished() filters out any bad conditions, we can assume that
* oi->next_option is valid. */
current_opt = oi->next_option;
/*
* Advance internal pointer to next option.
* optsize will be 0 when no more options
*/
optsize = coap_opt_parse(oi->next_option, oi->length, &option);
if (optsize) {
assert(optsize <= oi->length);
oi->next_option += optsize;
oi->length -= optsize;
oi->number += 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
*/
if (!oi->filtered ||
coap_option_filter_get(&oi->filter, oi->number) > 0)
break;
}
return current_opt;
}
coap_opt_t *
coap_check_option(const coap_pdu_t *pdu, coap_option_num_t number,
coap_opt_iterator_t *oi) {
coap_opt_filter_t f;
coap_option_filter_clear(&f);
coap_option_filter_set(&f, number);
coap_option_iterator_init(pdu, oi, &f);
return coap_option_next(oi);
}
uint32_t
coap_opt_length(const coap_opt_t *opt) {
uint32_t length;
length = *opt & 0x0f;
switch (*opt & 0xf0) {
case 0xf0:
coap_log_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:
coap_log_debug("illegal option length\n");
return 0;
case 0x0e:
length = (*opt++ << 8) + 269;
/* fall through */
case 0x0d:
length += *opt++;
break;
default:
;
}
return length;
}
const uint8_t *
coap_opt_value(const coap_opt_t *opt) {
size_t ofs = 1;
switch (*opt & 0xf0) {
case 0xf0:
coap_log_debug("illegal option delta\n");
return 0;
case 0xe0:
++ofs;
/* fall through */
case 0xd0:
++ofs;
break;
default:
;
}
switch (*opt & 0x0f) {
case 0x0f:
coap_log_debug("illegal option length\n");
return 0;
case 0x0e:
++ofs;
/* fall through */
case 0x0d:
++ofs;
break;
default:
;
}
return (const uint8_t *)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,
uint16_t 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] = (coap_opt_t)(delta << 4);
} else if (delta < 269) {
if (maxlen < 2) {
coap_log_debug("insufficient space to encode option delta %d\n",
delta);
return 0;
}
opt[0] = 0xd0;
opt[++skip] = (coap_opt_t)(delta - 13);
} else {
if (maxlen < 3) {
coap_log_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 < 269) {
if (maxlen < skip + 2) {
coap_log_debug("insufficient space to encode option length %zu\n",
length);
return 0;
}
opt[0] |= 0x0d;
opt[++skip] = (coap_opt_t)(length - 13);
} else {
if (maxlen < skip + 3) {
coap_log_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_size(uint16_t delta, size_t length) {
size_t n = 1;
if (delta >= 13) {
if (delta < 269)
n += 1;
else
n += 2;
}
if (length >= 13) {
if (length < 269)
n += 1;
else
n += 2;
}
return n + length;
}
size_t
coap_opt_encode(coap_opt_t *opt, size_t maxlen, uint16_t delta,
const uint8_t *val, size_t length) {
size_t l = 1;
l = coap_opt_setheader(opt, maxlen, delta, length);
assert(l <= maxlen);
if (!l) {
coap_log_debug("coap_opt_encode: cannot set option header\n");
return 0;
}
maxlen -= l;
opt += l;
if (maxlen < length) {
coap_log_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;
}
#define LONG_MASK ((1 << COAP_OPT_FILTER_LONG) - 1)
#define SHORT_MASK \
(~LONG_MASK & ((1 << (COAP_OPT_FILTER_LONG + COAP_OPT_FILTER_SHORT)) - 1))
COAP_STATIC_INLINE int
is_long_option(coap_option_num_t number) {
return number > 255;
}
enum filter_op_t { FILTER_SET, FILTER_CLEAR, FILTER_GET };
static int
coap_option_filter_op(coap_opt_filter_t *filter,
coap_option_num_t number,
enum filter_op_t op) {
size_t lindex = 0;
coap_opt_filter_t *of = filter;
uint16_t nr, mask = 0;
if (is_long_option(number)) {
mask = LONG_MASK;
for (nr = 1; lindex < COAP_OPT_FILTER_LONG; nr <<= 1, lindex++) {
if (((of->mask & nr) > 0) && (of->long_opts[lindex] == number)) {
if (op == FILTER_CLEAR) {
of->mask &= ~nr;
}
return 1;
}
}
} else {
mask = SHORT_MASK;
for (nr = 1 << COAP_OPT_FILTER_LONG; lindex < COAP_OPT_FILTER_SHORT;
nr <<= 1, lindex++) {
if (((of->mask & nr) > 0) && (of->short_opts[lindex] == (number & 0xff))) {
if (op == FILTER_CLEAR) {
of->mask &= ~nr;
}
return 1;
}
}
}
/* number 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: */
lindex = coap_fls(~of->mask & mask);
if (!lindex) {
return 0;
}
if (is_long_option(number)) {
of->long_opts[lindex - 1] = number;
} else {
of->short_opts[lindex - COAP_OPT_FILTER_LONG - 1] = (uint8_t)number;
}
of->mask |= 1 << (lindex - 1);
return 1;
}
void
coap_option_filter_clear(coap_opt_filter_t *filter) {
memset(filter, 0, sizeof(coap_opt_filter_t));
}
int
coap_option_filter_set(coap_opt_filter_t *filter, coap_option_num_t option) {
return coap_option_filter_op(filter, option, FILTER_SET);
}
int
coap_option_filter_unset(coap_opt_filter_t *filter, coap_option_num_t option) {
return coap_option_filter_op(filter, option, FILTER_CLEAR);
}
int
coap_option_filter_get(coap_opt_filter_t *filter, coap_option_num_t option) {
return coap_option_filter_op(filter, option, FILTER_GET);
}
coap_optlist_t *
coap_new_optlist(uint16_t number,
size_t length,
const uint8_t *data
) {
coap_optlist_t *node;
#ifdef WITH_LWIP
if (length > MEMP_LEN_COAPOPTLIST) {
coap_log_crit("coap_new_optlist: size too large (%zu > MEMP_LEN_COAPOPTLIST)\n",
length);
return NULL;
}
#endif /* WITH_LWIP */
node = coap_malloc_type(COAP_OPTLIST, sizeof(coap_optlist_t) + length);
if (node) {
memset(node, 0, (sizeof(coap_optlist_t) + length));
node->number = number;
node->length = length;
node->data = (uint8_t *)&node[1];
memcpy(node->data, data, length);
} else {
coap_log_warn("coap_new_optlist: malloc failure\n");
}
return node;
}
static int
order_opts(void *a, void *b) {
coap_optlist_t *o1 = (coap_optlist_t *)a;
coap_optlist_t *o2 = (coap_optlist_t *)b;
if (!a || !b)
return a < b ? -1 : 1;
return (int)(o1->number - o2->number);
}
int
coap_add_optlist_pdu(coap_pdu_t *pdu, coap_optlist_t **options) {
coap_optlist_t *opt;
if (options && *options) {
if (pdu->data) {
coap_log_warn("coap_add_optlist_pdu: PDU already contains data\n");
return 0;
}
/* sort options for delta encoding */
LL_SORT((*options), order_opts);
LL_FOREACH((*options), opt) {
if (!coap_add_option_internal(pdu, opt->number, opt->length, opt->data))
return 0;
}
}
return 1;
}
int
coap_insert_optlist(coap_optlist_t **head, coap_optlist_t *node) {
if (!node) {
coap_log_debug("optlist not provided\n");
} else {
/* must append at the list end to avoid re-ordering of
* options during sort */
LL_APPEND((*head), node);
}
return node != NULL;
}
static int
coap_internal_delete(coap_optlist_t *node) {
if (node) {
coap_free_type(COAP_OPTLIST, node);
}
return 1;
}
void
coap_delete_optlist(coap_optlist_t *queue) {
coap_optlist_t *elt, *tmp;
if (!queue)
return;
LL_FOREACH_SAFE(queue, elt, tmp) {
coap_internal_delete(elt);
}
}
coap_fls
int coap_fls(unsigned int i)
Definition coap_encode.c:21
coap_libcoap_build.h
Library specific build wrapper for coap_internal.h.
COAP_OPTLIST
@ COAP_OPTLIST
Definition coap_mem.h:52
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_free_type
void coap_free_type(coap_memory_tag_t type, void *p)
Releases the memory that was allocated by coap_malloc_type().
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 coap_option.c:41
filter_op_t
filter_op_t
Operation specifiers for coap_filter_op().
Definition coap_option.c:409
FILTER_CLEAR
@ FILTER_CLEAR
Definition coap_option.c:409
FILTER_GET
@ FILTER_GET
Definition coap_option.c:409
FILTER_SET
@ FILTER_SET
Definition coap_option.c:409
ADVANCE_OPT_CHECK
#define ADVANCE_OPT_CHECK(o, e, step)
Definition coap_option.c:34
SHORT_MASK
#define SHORT_MASK
Definition coap_option.c:399
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 coap_option.c:284
opt_finished
COAP_STATIC_INLINE int opt_finished(coap_opt_iterator_t *oi)
Definition coap_option.c:141
coap_internal_delete
static int coap_internal_delete(coap_optlist_t *node)
Definition coap_option.c:584
ADVANCE_OPT
#define ADVANCE_OPT(o, e, step)
Definition coap_option.c:22
coap_option_filter_op
static int coap_option_filter_op(coap_opt_filter_t *filter, coap_option_num_t number, enum filter_op_t op)
Applies op on filter with respect to number.
Definition coap_option.c:431
is_long_option
COAP_STATIC_INLINE int is_long_option(coap_option_num_t number)
Returns true iff number denotes an option number larger than 255.
Definition coap_option.c:404
order_opts
static int order_opts(void *a, void *b)
Definition coap_option.c:540
LONG_MASK
#define LONG_MASK
Definition coap_option.c:398
coap_option_num_t
uint16_t coap_option_num_t
Definition coap_option.h:20
coap_opt_t
uint8_t coap_opt_t
Use byte-oriented access methods here because sliding a complex struct coap_opt_t over the data buffe...
Definition coap_option.h:26
coap_log_debug
#define coap_log_debug(...)
Definition coap_debug.h:120
coap_log_warn
#define coap_log_warn(...)
Definition coap_debug.h:102
coap_log_crit
#define coap_log_crit(...)
Definition coap_debug.h:90
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 coap_option.c:153
coap_new_optlist
coap_optlist_t * coap_new_optlist(uint16_t number, size_t length, const uint8_t *data)
Create a new optlist entry.
Definition coap_option.c:511
coap_opt_encode
size_t coap_opt_encode(coap_opt_t *opt, size_t maxlen, uint16_t delta, const uint8_t *val, size_t length)
Encodes option with given delta into opt.
Definition coap_option.c:372
coap_opt_length
uint32_t coap_opt_length(const coap_opt_t *opt)
Returns the length of the given option.
Definition coap_option.c:212
coap_option_iterator_init
coap_opt_iterator_t * coap_option_iterator_init(const 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's option list.
Definition coap_option.c:117
coap_delete_optlist
void coap_delete_optlist(coap_optlist_t *queue)
Removes all entries from the optlist_chain, freeing off their memory usage.
Definition coap_option.c:592
coap_opt_encode_size
size_t coap_opt_encode_size(uint16_t delta, size_t length)
Compute storage bytes needed for an option with given delta and length.
Definition coap_option.c:351
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 coap_option.h:78
coap_option_filter_unset
int coap_option_filter_unset(coap_opt_filter_t *filter, coap_option_num_t option)
Clears the corresponding entry for number in filter.
Definition coap_option.c:501
coap_add_optlist_pdu
int coap_add_optlist_pdu(coap_pdu_t *pdu, coap_optlist_t **options)
The current optlist of optlist_chain is first sorted (as per RFC7272 ordering requirements) and then ...
Definition coap_option.c:551
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 coap_option.h:86
coap_option_filter_clear
void coap_option_filter_clear(coap_opt_filter_t *filter)
Clears filter filter.
Definition coap_option.c:491
coap_check_option
coap_opt_t * coap_check_option(const coap_pdu_t *pdu, coap_option_num_t number, coap_opt_iterator_t *oi)
Retrieves the first option of number number from pdu.
Definition coap_option.c:199
coap_insert_optlist
int coap_insert_optlist(coap_optlist_t **head, coap_optlist_t *node)
Adds optlist to the given optlist_chain.
Definition coap_option.c:571
coap_opt_value
const uint8_t * coap_opt_value(const coap_opt_t *opt)
Returns a pointer to the value of the given option.
Definition coap_option.c:249
coap_option_filter_get
int coap_option_filter_get(coap_opt_filter_t *filter, coap_option_num_t option)
Checks if number is contained in filter.
Definition coap_option.c:506
coap_option_filter_set
int coap_option_filter_set(coap_opt_filter_t *filter, coap_option_num_t option)
Sets the corresponding entry for number in filter.
Definition coap_option.c:496
coap_opt_setheader
size_t coap_opt_setheader(coap_opt_t *opt, size_t maxlen, uint16_t delta, size_t length)
Encodes the given delta and length values into opt.
Definition coap_option.c:292
COAP_PAYLOAD_START
#define COAP_PAYLOAD_START
Definition coap_pdu_internal.h:99
coap_add_option_internal
size_t coap_add_option_internal(coap_pdu_t *pdu, coap_option_num_t number, size_t len, const uint8_t *data)
Adds option of given number to pdu that is passed as first parameter.
Definition coap_pdu.c:760
COAP_STATIC_INLINE
#define COAP_STATIC_INLINE
Definition libcoap.h:53
coap_opt_filter_t
Definition coap_option.h:101
coap_opt_filter_t::mask
uint16_t mask
Definition coap_option.h:102
coap_opt_filter_t::short_opts
uint8_t short_opts[COAP_OPT_FILTER_SHORT]
Definition coap_option.h:104
coap_opt_filter_t::long_opts
uint16_t long_opts[COAP_OPT_FILTER_LONG]
Definition coap_option.h:103
coap_opt_iterator_t
Iterator to run through PDU options.
Definition coap_option.h:168
coap_opt_iterator_t::next_option
coap_opt_t * next_option
pointer to the unparsed next option
Definition coap_option.h:173
coap_opt_iterator_t::filter
coap_opt_filter_t filter
option filter
Definition coap_option.h:174
coap_opt_iterator_t::bad
unsigned int bad
iterator object is ok if not set
Definition coap_option.h:171
coap_opt_iterator_t::length
size_t length
remaining length of PDU
Definition coap_option.h:169
coap_opt_iterator_t::filtered
unsigned int filtered
denotes whether or not filter is used
Definition coap_option.h:172
coap_opt_iterator_t::number
coap_option_num_t number
decoded option number
Definition coap_option.h:170
coap_option_t
Representation of CoAP options.
Definition coap_option.h:32
coap_option_t::value
const uint8_t * value
Definition coap_option.h:35
coap_option_t::delta
uint16_t delta
Definition coap_option.h:33
coap_option_t::length
size_t length
Definition coap_option.h:34
coap_optlist_t
Representation of chained list of CoAP options to install.
Definition coap_option.h:325
coap_optlist_t::number
uint16_t number
the option number (no delta coding)
Definition coap_option.h:327
coap_optlist_t::length
size_t length
the option value length
Definition coap_option.h:328
coap_optlist_t::data
uint8_t * data
the option data
Definition coap_option.h:329
coap_pdu_t
structure for CoAP PDUs
Definition coap_pdu_internal.h:135
coap_pdu_t::token
uint8_t * token
first byte of token (or extended length bytes prefix), if any, or options
Definition coap_pdu_internal.h:155
coap_pdu_t::data
uint8_t * data
first byte of payload, if any
Definition coap_pdu_internal.h:157
coap_pdu_t::e_token_length
uint32_t e_token_length
length of Token space (includes leading extended bytes
Definition coap_pdu_internal.h:146
coap_pdu_t::used_size
size_t used_size
used bytes of storage for token, options and payload
Definition coap_pdu_internal.h:151