libcoap 4.3.5-develop-19cef11
coap_option.c
Go to the documentation of this file.
1/*
2 * coap_option.c -- helpers for handling options in CoAP PDUs
3 *
4 * Copyright (C) 2010-2013,2022-2024 Olaf Bergmann <bergmann@tzi.org>
5 *
6 * SPDX-License-Identifier: BSD-2-Clause
7 *
8 * This file is part of the CoAP library libcoap. Please see
9 * README for terms of use.
10 */
11
18
19#include <stdio.h>
20#include <string.h>
21
22#define ADVANCE_OPT(o,e,step) if ((e) < step) { \
23 coap_log_debug("cannot advance opt past end\n"); \
24 return 0; \
25 } else { \
26 (e) -= step; \
27 (o) = ((o)) + step; \
28 }
29
30/*
31 * Used to prevent access to *opt when pointing to after end of buffer
32 * after doing a ADVANCE_OPT()
33 */
34#define ADVANCE_OPT_CHECK(o,e,step) do { \
35 ADVANCE_OPT(o,e,step); \
36 if ((e) < 1) \
37 return 0; \
38 } while (0)
39
40size_t
41coap_opt_parse(const coap_opt_t *opt, size_t length, coap_option_t *result) {
42
43 const coap_opt_t *opt_start = opt; /* store where parsing starts */
44
45 assert(opt);
46 assert(result);
47
48 if (length < 1)
49 return 0;
50
51 result->delta = (*opt & 0xf0) >> 4;
52 result->length = *opt & 0x0f;
53
54 switch (result->delta) {
55 case 15:
56 if (*opt != COAP_PAYLOAD_START) {
57 coap_log_debug("ignored reserved option delta 15\n");
58 }
59 return 0;
60 case 14:
61 /* Handle two-byte value: First, the MSB + 269 is stored as delta value.
62 * After that, the option pointer is advanced to the LSB which is handled
63 * just like case delta == 13. */
64 ADVANCE_OPT_CHECK(opt,length,1);
65 result->delta = ((*opt & 0xff) << 8) + 269;
66 if (result->delta < 269) {
67 coap_log_debug("delta too large\n");
68 return 0;
69 }
70 /* fall through */
71 case 13:
72 ADVANCE_OPT_CHECK(opt,length,1);
73 result->delta += *opt & 0xff;
74 break;
75
76 default:
77 ;
78 }
79
80 switch (result->length) {
81 case 15:
82 coap_log_debug("found reserved option length 15\n");
83 return 0;
84 case 14:
85 /* Handle two-byte value: First, the MSB + 269 is stored as delta value.
86 * After that, the option pointer is advanced to the LSB which is handled
87 * just like case delta == 13. */
88 ADVANCE_OPT_CHECK(opt,length,1);
89 result->length = ((*opt & 0xff) << 8) + 269;
90 /* fall through */
91 case 13:
92 ADVANCE_OPT_CHECK(opt,length,1);
93 result->length += *opt & 0xff;
94 break;
95
96 default:
97 ;
98 }
99
100 /* ADVANCE_OPT() is correct here */
101 ADVANCE_OPT(opt,length,1);
102 /* opt now points to value, if present */
103
104 result->value = opt;
105 if (length < result->length) {
106 coap_log_debug("invalid option length\n");
107 return 0;
108 }
109
110#undef ADVANCE_OPT
111#undef ADVANCE_OPT_CHECK
112
113 return (opt + result->length) - opt_start;
114}
115
118 const coap_opt_filter_t *filter) {
119 assert(pdu);
120 assert(pdu->token);
121 assert(oi);
122
123 memset(oi, 0, sizeof(coap_opt_iterator_t));
124
125 oi->next_option = pdu->token + pdu->e_token_length;
126 if (pdu->token + pdu->used_size <= oi->next_option) {
127 oi->bad = 1;
128 return NULL;
129 }
130
131 oi->length = pdu->used_size - pdu->e_token_length;
132
133 if (filter) {
134 memcpy(&oi->filter, filter, sizeof(coap_opt_filter_t));
135 oi->filtered = 1;
136 }
137 return oi;
138}
139
142 assert(oi);
143
144 if (oi->bad || oi->length == 0 ||
146 oi->bad = 1;
147 }
148
149 return oi->bad;
150}
151
154 coap_option_t option;
155 coap_opt_t *current_opt = NULL;
156 size_t optsize;
157
158 assert(oi);
159
160 if (opt_finished(oi))
161 return NULL;
162
163 while (1) {
164 /* oi->next_option always points to the next option to deliver; as
165 * opt_finished() filters out any bad conditions, we can assume that
166 * oi->next_option is valid. */
167 current_opt = oi->next_option;
168
169 /*
170 * Advance internal pointer to next option.
171 * optsize will be 0 when no more options
172 */
173 optsize = coap_opt_parse(oi->next_option, oi->length, &option);
174 if (optsize) {
175 assert(optsize <= oi->length);
176
177 oi->next_option += optsize;
178 oi->length -= optsize;
179
180 oi->number += option.delta;
181 } else { /* current option is malformed */
182 oi->bad = 1;
183 return NULL;
184 }
185
186 /* Exit the while loop when:
187 * - no filtering is done at all
188 * - the filter matches for the current option
189 */
190 if (!oi->filtered ||
191 coap_option_filter_get(&oi->filter, oi->number) > 0)
192 break;
193 }
194
195 return current_opt;
196}
197
202
204 coap_option_filter_set(&f, number);
205
206 coap_option_iterator_init(pdu, oi, &f);
207
208 return coap_option_next(oi);
209}
210
211uint32_t
213 uint32_t length;
214
215 length = *opt & 0x0f;
216 switch (*opt & 0xf0) {
217 case 0xf0:
218 coap_log_debug("illegal option delta\n");
219 return 0;
220 case 0xe0:
221 ++opt;
222 /* fall through */
223 /* to skip another byte */
224 case 0xd0:
225 ++opt;
226 /* fall through */
227 /* to skip another byte */
228 default:
229 ++opt;
230 }
231
232 switch (length) {
233 case 0x0f:
234 coap_log_debug("illegal option length\n");
235 return 0;
236 case 0x0e:
237 length = (*opt++ << 8) + 269;
238 /* fall through */
239 case 0x0d:
240 length += *opt++;
241 break;
242 default:
243 ;
244 }
245 return length;
246}
247
248const uint8_t *
250 size_t ofs = 1;
251
252 switch (*opt & 0xf0) {
253 case 0xf0:
254 coap_log_debug("illegal option delta\n");
255 return 0;
256 case 0xe0:
257 ++ofs;
258 /* fall through */
259 case 0xd0:
260 ++ofs;
261 break;
262 default:
263 ;
264 }
265
266 switch (*opt & 0x0f) {
267 case 0x0f:
268 coap_log_debug("illegal option length\n");
269 return 0;
270 case 0x0e:
271 ++ofs;
272 /* fall through */
273 case 0x0d:
274 ++ofs;
275 break;
276 default:
277 ;
278 }
279
280 return (const uint8_t *)opt + ofs;
281}
282
283size_t
285 coap_option_t option;
286
287 /* we must assume that opt is encoded correctly */
288 return coap_opt_parse(opt, (size_t)-1, &option);
289}
290
291size_t
292coap_opt_setheader(coap_opt_t *opt, size_t maxlen,
293 uint16_t delta, size_t length) {
294 size_t skip = 0;
295
296 assert(opt);
297
298 if (maxlen == 0) /* need at least one byte */
299 return 0;
300
301 if (delta < 13) {
302 opt[0] = (coap_opt_t)(delta << 4);
303 } else if (delta < 269) {
304 if (maxlen < 2) {
305 coap_log_debug("insufficient space to encode option delta %d\n",
306 delta);
307 return 0;
308 }
309
310 opt[0] = 0xd0;
311 opt[++skip] = (coap_opt_t)(delta - 13);
312 } else {
313 if (maxlen < 3) {
314 coap_log_debug("insufficient space to encode option delta %d\n",
315 delta);
316 return 0;
317 }
318
319 opt[0] = 0xe0;
320 opt[++skip] = ((delta - 269) >> 8) & 0xff;
321 opt[++skip] = (delta - 269) & 0xff;
322 }
323
324 if (length < 13) {
325 opt[0] |= length & 0x0f;
326 } else if (length < 269) {
327 if (maxlen < skip + 2) {
328 coap_log_debug("insufficient space to encode option length %zu\n",
329 length);
330 return 0;
331 }
332
333 opt[0] |= 0x0d;
334 opt[++skip] = (coap_opt_t)(length - 13);
335 } else {
336 if (maxlen < skip + 3) {
337 coap_log_debug("insufficient space to encode option delta %d\n",
338 delta);
339 return 0;
340 }
341
342 opt[0] |= 0x0e;
343 opt[++skip] = ((length - 269) >> 8) & 0xff;
344 opt[++skip] = (length - 269) & 0xff;
345 }
346
347 return skip + 1;
348}
349
350size_t
351coap_opt_encode_size(uint16_t delta, size_t length) {
352 size_t n = 1;
353
354 if (delta >= 13) {
355 if (delta < 269)
356 n += 1;
357 else
358 n += 2;
359 }
360
361 if (length >= 13) {
362 if (length < 269)
363 n += 1;
364 else
365 n += 2;
366 }
367
368 return n + length;
369}
370
371size_t
372coap_opt_encode(coap_opt_t *opt, size_t maxlen, uint16_t delta,
373 const uint8_t *val, size_t length) {
374 size_t l = 1;
375
376 l = coap_opt_setheader(opt, maxlen, delta, length);
377 assert(l <= maxlen);
378
379 if (!l) {
380 coap_log_debug("coap_opt_encode: cannot set option header\n");
381 return 0;
382 }
383
384 maxlen -= l;
385 opt += l;
386
387 if (maxlen < length) {
388 coap_log_debug("coap_opt_encode: option too large for buffer\n");
389 return 0;
390 }
391
392 if (val) /* better be safe here */
393 memcpy(opt, val, length);
394
395 return l + length;
396}
397
398#define LONG_MASK ((1 << COAP_OPT_FILTER_LONG) - 1)
399#define SHORT_MASK \
400 (~LONG_MASK & ((1 << (COAP_OPT_FILTER_LONG + COAP_OPT_FILTER_SHORT)) - 1))
401
405 return number > 255;
406}
407
410
430static int
432 coap_option_num_t number,
433 enum filter_op_t op) {
434 size_t lindex = 0;
435 coap_opt_filter_t *of = filter;
436 uint16_t nr, mask = 0;
437
438 if (is_long_option(number)) {
439 mask = LONG_MASK;
440
441 for (nr = 1; lindex < COAP_OPT_FILTER_LONG; nr <<= 1, lindex++) {
442
443 if (((of->mask & nr) > 0) && (of->long_opts[lindex] == number)) {
444 if (op == FILTER_CLEAR) {
445 of->mask &= ~nr;
446 }
447
448 return 1;
449 }
450 }
451 } else {
452 mask = SHORT_MASK;
453
454 for (nr = 1 << COAP_OPT_FILTER_LONG; lindex < COAP_OPT_FILTER_SHORT;
455 nr <<= 1, lindex++) {
456
457 if (((of->mask & nr) > 0) && (of->short_opts[lindex] == (number & 0xff))) {
458 if (op == FILTER_CLEAR) {
459 of->mask &= ~nr;
460 }
461
462 return 1;
463 }
464 }
465 }
466
467 /* number was not found, so there is nothing to do if op is CLEAR or GET */
468 if ((op == FILTER_CLEAR) || (op == FILTER_GET)) {
469 return 0;
470 }
471
472 /* handle FILTER_SET: */
473
474 lindex = coap_fls(~of->mask & mask);
475 if (!lindex) {
476 return 0;
477 }
478
479 if (is_long_option(number)) {
480 of->long_opts[lindex - 1] = number;
481 } else {
482 of->short_opts[lindex - COAP_OPT_FILTER_LONG - 1] = (uint8_t)number;
483 }
484
485 of->mask |= 1 << (lindex - 1);
486
487 return 1;
488}
489
490void
492 memset(filter, 0, sizeof(coap_opt_filter_t));
493}
494
495int
497 return coap_option_filter_op(filter, option, FILTER_SET);
498}
499
500int
502 return coap_option_filter_op(filter, option, FILTER_CLEAR);
503}
504
505int
507 return coap_option_filter_op(filter, option, FILTER_GET);
508}
509
511coap_new_optlist(uint16_t number,
512 size_t length,
513 const uint8_t *data
514 ) {
515 coap_optlist_t *node;
516
517#ifdef WITH_LWIP
518 if (length > MEMP_LEN_COAPOPTLIST) {
519 coap_log_crit("coap_new_optlist: size too large (%zu > MEMP_LEN_COAPOPTLIST)\n",
520 length);
521 return NULL;
522 }
523#endif /* WITH_LWIP */
524 node = coap_malloc_type(COAP_OPTLIST, sizeof(coap_optlist_t) + length);
525
526 if (node) {
527 memset(node, 0, (sizeof(coap_optlist_t) + length));
528 node->number = number;
529 node->length = length;
530 node->data = (uint8_t *)&node[1];
531 memcpy(node->data, data, length);
532 } else {
533 coap_log_warn("coap_new_optlist: malloc failure\n");
534 }
535
536 return node;
537}
538
539static int
540order_opts(void *a, void *b) {
543
544 if (!a || !b)
545 return a < b ? -1 : 1;
546
547 return (int)(o1->number - o2->number);
548}
549
550int
552 coap_optlist_t *opt;
553
554 if (options && *options) {
555 if (pdu->data) {
556 coap_log_warn("coap_add_optlist_pdu: PDU already contains data\n");
557 return 0;
558 }
559 /* sort options for delta encoding */
560 LL_SORT((*options), order_opts);
561
562 LL_FOREACH((*options), opt) {
563 if (!coap_add_option_internal(pdu, opt->number, opt->length, opt->data))
564 return 0;
565 }
566 }
567 return 1;
568}
569
570int
572 if (!node) {
573 coap_log_debug("optlist not provided\n");
574 } else {
575 /* must append at the list end to avoid re-ordering of
576 * options during sort */
577 LL_APPEND((*head), node);
578 }
579
580 return node != NULL;
581}
582
583static int
585 if (node) {
587 }
588 return 1;
589}
590
591void
593 coap_optlist_t *elt, *tmp;
594
595 if (!queue)
596 return;
597
598 LL_FOREACH_SAFE(queue, elt, tmp) {
600 }
601}
int coap_fls(unsigned int i)
Definition: coap_encode.c:21
Library specific build wrapper for coap_internal.h.
@ COAP_OPTLIST
Definition: coap_mem.h:52
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.
void coap_free_type(coap_memory_tag_t type, void *p)
Releases the memory that was allocated by coap_malloc_type().
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
Operation specifiers for coap_filter_op().
Definition: coap_option.c:409
@ FILTER_CLEAR
Definition: coap_option.c:409
@ FILTER_GET
Definition: coap_option.c:409
@ FILTER_SET
Definition: coap_option.c:409
#define ADVANCE_OPT_CHECK(o, e, step)
Definition: coap_option.c:34
#define SHORT_MASK
Definition: coap_option.c:399
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
COAP_STATIC_INLINE int opt_finished(coap_opt_iterator_t *oi)
Definition: coap_option.c:141
static int coap_internal_delete(coap_optlist_t *node)
Definition: coap_option.c:584
#define ADVANCE_OPT(o, e, step)
Definition: coap_option.c:22
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
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
static int order_opts(void *a, void *b)
Definition: coap_option.c:540
#define LONG_MASK
Definition: coap_option.c:398
uint16_t coap_option_num_t
Definition: coap_option.h:20
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
#define coap_log_debug(...)
Definition: coap_debug.h:120
#define coap_log_warn(...)
Definition: coap_debug.h:102
#define coap_log_crit(...)
Definition: coap_debug.h:90
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_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
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
uint32_t coap_opt_length(const coap_opt_t *opt)
Returns the length of the given option.
Definition: coap_option.c:212
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
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
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
#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
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
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
#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
void coap_option_filter_clear(coap_opt_filter_t *filter)
Clears filter filter.
Definition: coap_option.c:491
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
int coap_insert_optlist(coap_optlist_t **head, coap_optlist_t *node)
Adds optlist to the given optlist_chain.
Definition: coap_option.c:571
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
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
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
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
#define COAP_PAYLOAD_START
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:769
#define COAP_STATIC_INLINE
Definition: libcoap.h:53
uint8_t short_opts[COAP_OPT_FILTER_SHORT]
Definition: coap_option.h:104
uint16_t long_opts[COAP_OPT_FILTER_LONG]
Definition: coap_option.h:103
Iterator to run through PDU options.
Definition: coap_option.h:168
coap_opt_t * next_option
pointer to the unparsed next option
Definition: coap_option.h:173
coap_opt_filter_t filter
option filter
Definition: coap_option.h:174
unsigned int bad
iterator object is ok if not set
Definition: coap_option.h:171
size_t length
remaining length of PDU
Definition: coap_option.h:169
unsigned int filtered
denotes whether or not filter is used
Definition: coap_option.h:172
coap_option_num_t number
decoded option number
Definition: coap_option.h:170
Representation of CoAP options.
Definition: coap_option.h:32
const uint8_t * value
Definition: coap_option.h:35
uint16_t delta
Definition: coap_option.h:33
size_t length
Definition: coap_option.h:34
Representation of chained list of CoAP options to install.
Definition: coap_option.h:325
uint16_t number
the option number (no delta coding)
Definition: coap_option.h:327
size_t length
the option value length
Definition: coap_option.h:328
uint8_t * data
the option data
Definition: coap_option.h:329
structure for CoAP PDUs
uint8_t * token
first byte of token (or extended length bytes prefix), if any, or options
uint8_t * data
first byte of payload, if any
uint32_t e_token_length
length of Token space (includes leading extended bytes
size_t used_size
used bytes of storage for token, options and payload