libcoap 4.3.5-develop-fcba074
Loading...
Searching...
No Matches
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-2026 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
16
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 uint32_t delta; /* wider than result->delta to detect overflow */
45
46 assert(opt);
47 assert(result);
48
49 if (length < 1)
50 return 0;
51
52 result->delta = (*opt & 0xf0) >> 4;
53 result->length = *opt & 0x0f;
54 delta = result->delta;
55
56 switch (result->delta) {
57 case 15:
58 if (*opt != COAP_PAYLOAD_START) {
59 coap_log_debug("ignored reserved option delta 15\n");
60 }
61 return 0;
62 case 14:
63 /* Handle two-byte value: First, the MSB + 269 is stored as delta value.
64 * After that, the option pointer is advanced to the LSB which is handled
65 * just like case delta == 13. */
66 ADVANCE_OPT_CHECK(opt,length,1);
67 delta = ((*opt & 0xff) << 8) + 269;
68 /* fall through */
69 case 13:
70 ADVANCE_OPT_CHECK(opt,length,1);
71 delta += *opt & 0xff;
72 if (delta > 0xffff) {
73 coap_log_debug("delta too large\n");
74 return 0;
75 }
76 result->delta = (uint16_t)delta;
77 break;
78
79 default:
80 ;
81 }
82
83 switch (result->length) {
84 case 15:
85 coap_log_debug("found reserved option length 15\n");
86 return 0;
87 case 14:
88 /* Handle two-byte value: First, the MSB + 269 is stored as delta value.
89 * After that, the option pointer is advanced to the LSB which is handled
90 * just like case delta == 13. */
91 ADVANCE_OPT_CHECK(opt,length,1);
92 result->length = ((*opt & 0xff) << 8) + 269;
93 /* fall through */
94 case 13:
95 ADVANCE_OPT_CHECK(opt,length,1);
96 result->length += *opt & 0xff;
97 break;
98
99 default:
100 ;
101 }
102
103 /* ADVANCE_OPT() is correct here */
104 ADVANCE_OPT(opt,length,1);
105 /* opt now points to value, if present */
106
107 result->value = opt;
108 if (length < result->length) {
109 coap_log_debug("invalid option length\n");
110 return 0;
111 }
112
113#undef ADVANCE_OPT
114#undef ADVANCE_OPT_CHECK
115
116 return (opt + result->length) - opt_start;
117}
118
121 const coap_opt_filter_t *filter) {
122 assert(pdu);
123 assert(pdu->token);
124 assert(oi);
125
126 memset(oi, 0, sizeof(coap_opt_iterator_t));
127
128 oi->next_option = pdu->token + pdu->e_token_length;
129 if (pdu->token + pdu->used_size <= oi->next_option) {
130 oi->bad = 1;
131 return NULL;
132 }
133
134 oi->length = pdu->used_size - pdu->e_token_length;
135
136 if (filter) {
137 memcpy(&oi->filter, filter, sizeof(coap_opt_filter_t));
138 oi->filtered = 1;
139 }
140 return oi;
141}
142
145 assert(oi);
146
147 if (oi->bad || oi->length == 0 ||
149 oi->bad = 1;
150 }
151
152 return oi->bad;
153}
154
157 coap_option_t option;
158 coap_opt_t *current_opt = NULL;
159 size_t optsize;
160
161 assert(oi);
162
163 if (opt_finished(oi))
164 return NULL;
165
166 while (1) {
167 /* oi->next_option always points to the next option to deliver; as
168 * opt_finished() filters out any bad conditions, we can assume that
169 * oi->next_option is valid. */
170 current_opt = oi->next_option;
171
172 /*
173 * Advance internal pointer to next option.
174 * optsize will be 0 when no more options
175 */
176 optsize = coap_opt_parse(oi->next_option, oi->length, &option);
177 if (optsize) {
178 assert(optsize <= oi->length);
179
180 oi->next_option += optsize;
181 oi->length -= optsize;
182
183 oi->number += option.delta;
184 } else { /* current option is malformed */
185 oi->bad = 1;
186 return NULL;
187 }
188
189 /* Exit the while loop when:
190 * - no filtering is done at all
191 * - the filter matches for the current option
192 */
193 if (!oi->filtered ||
194 coap_option_filter_get(&oi->filter, oi->number) > 0)
195 break;
196 }
197
198 return current_opt;
199}
200
205
207 coap_option_filter_set(&f, number);
208
209 coap_option_iterator_init(pdu, oi, &f);
210
211 return coap_option_next(oi);
212}
213
214uint32_t
216 uint32_t length;
217
218 length = *opt & 0x0f;
219 switch (*opt & 0xf0) {
220 case 0xf0:
221 coap_log_debug("illegal option delta\n");
222 return 0;
223 case 0xe0:
224 ++opt;
225 /* fall through */
226 /* to skip another byte */
227 case 0xd0:
228 ++opt;
229 /* fall through */
230 /* to skip another byte */
231 default:
232 ++opt;
233 }
234
235 switch (length) {
236 case 0x0f:
237 coap_log_debug("illegal option length\n");
238 return 0;
239 case 0x0e:
240 length = (*opt++ << 8) + 269;
241 /* fall through */
242 case 0x0d:
243 length += *opt++;
244 break;
245 default:
246 ;
247 }
248 return length;
249}
250
251const uint8_t *
253 size_t ofs = 1;
254
255 switch (*opt & 0xf0) {
256 case 0xf0:
257 coap_log_debug("illegal option delta\n");
258 return 0;
259 case 0xe0:
260 ++ofs;
261 /* fall through */
262 case 0xd0:
263 ++ofs;
264 break;
265 default:
266 ;
267 }
268
269 switch (*opt & 0x0f) {
270 case 0x0f:
271 coap_log_debug("illegal option length\n");
272 return 0;
273 case 0x0e:
274 ++ofs;
275 /* fall through */
276 case 0x0d:
277 ++ofs;
278 break;
279 default:
280 ;
281 }
282
283 return (const uint8_t *)opt + ofs;
284}
285
286size_t
288 coap_option_t option;
289
290 /* we must assume that opt is encoded correctly */
291 return coap_opt_parse(opt, (size_t)-1, &option);
292}
293
294size_t
295coap_opt_setheader(coap_opt_t *opt, size_t maxlen,
296 uint16_t delta, size_t length) {
297 size_t skip = 0;
298
299 assert(opt);
300
301 if (maxlen == 0) /* need at least one byte */
302 return 0;
303
304 if (delta < 13) {
305 opt[0] = (coap_opt_t)(delta << 4);
306 } else if (delta < 269) {
307 if (maxlen < 2) {
308 coap_log_debug("insufficient space to encode option delta %d\n",
309 delta);
310 return 0;
311 }
312
313 opt[0] = 0xd0;
314 opt[++skip] = (coap_opt_t)(delta - 13);
315 } else {
316 if (maxlen < 3) {
317 coap_log_debug("insufficient space to encode option delta %d\n",
318 delta);
319 return 0;
320 }
321
322 opt[0] = 0xe0;
323 opt[++skip] = ((delta - 269) >> 8) & 0xff;
324 opt[++skip] = (delta - 269) & 0xff;
325 }
326
327 if (length < 13) {
328 opt[0] |= length & 0x0f;
329 } else if (length < 269) {
330 if (maxlen < skip + 2) {
331 coap_log_debug("insufficient space to encode option length %" PRIuS "\n",
332 length);
333 return 0;
334 }
335
336 opt[0] |= 0x0d;
337 opt[++skip] = (coap_opt_t)(length - 13);
338 } else {
339 if (maxlen < skip + 3) {
340 coap_log_debug("insufficient space to encode option delta %d\n",
341 delta);
342 return 0;
343 }
344
345 opt[0] |= 0x0e;
346 opt[++skip] = ((length - 269) >> 8) & 0xff;
347 opt[++skip] = (length - 269) & 0xff;
348 }
349
350 return skip + 1;
351}
352
353size_t
354coap_opt_encode_size(uint16_t delta, size_t length) {
355 size_t n = 1;
356
357 if (delta >= 13) {
358 if (delta < 269)
359 n += 1;
360 else
361 n += 2;
362 }
363
364 if (length >= 13) {
365 if (length < 269)
366 n += 1;
367 else
368 n += 2;
369 }
370
371 return n + length;
372}
373
374size_t
375coap_opt_encode(coap_opt_t *opt, size_t maxlen, uint16_t delta,
376 const uint8_t *val, size_t length) {
377 size_t l = 1;
378
379 l = coap_opt_setheader(opt, maxlen, delta, length);
380 assert(l <= maxlen);
381
382 if (!l) {
383 coap_log_debug("coap_opt_encode: cannot set option header\n");
384 return 0;
385 }
386
387 maxlen -= l;
388 opt += l;
389
390 if (maxlen < length) {
391 coap_log_debug("coap_opt_encode: option too large for buffer\n");
392 return 0;
393 }
394
395 if (val) /* better be safe here */
396 memcpy(opt, val, length);
397
398 return l + length;
399}
400
401#define LONG_MASK ((1 << COAP_OPT_FILTER_LONG) - 1)
402#define SHORT_MASK \
403 (~LONG_MASK & ((1 << (COAP_OPT_FILTER_LONG + COAP_OPT_FILTER_SHORT)) - 1))
404
408 return number > 255;
409}
410
413
433static int
435 coap_option_num_t number,
436 enum filter_op_t op) {
437 size_t lindex = 0;
438 coap_opt_filter_t *of = filter;
439 uint16_t nr, mask = 0;
440
441 if (is_long_option(number)) {
442 mask = LONG_MASK;
443
444 for (nr = 1; lindex < COAP_OPT_FILTER_LONG; nr <<= 1, lindex++) {
445
446 if (((of->mask & nr) > 0) && (of->long_opts[lindex] == number)) {
447 if (op == FILTER_CLEAR) {
448 of->mask &= ~nr;
449 }
450
451 return 1;
452 }
453 }
454 } else {
455 mask = SHORT_MASK;
456
457 for (nr = 1 << COAP_OPT_FILTER_LONG; lindex < COAP_OPT_FILTER_SHORT;
458 nr <<= 1, lindex++) {
459
460 if (((of->mask & nr) > 0) && (of->short_opts[lindex] == (number & 0xff))) {
461 if (op == FILTER_CLEAR) {
462 of->mask &= ~nr;
463 }
464
465 return 1;
466 }
467 }
468 }
469
470 /* number was not found, so there is nothing to do if op is CLEAR or GET */
471 if ((op == FILTER_CLEAR) || (op == FILTER_GET)) {
472 return 0;
473 }
474
475 /* handle FILTER_SET: */
476
477 lindex = coap_fls(~of->mask & mask);
478 if (!lindex) {
479 return 0;
480 }
481
482 if (is_long_option(number)) {
483 of->long_opts[lindex - 1] = number;
484 } else {
485 of->short_opts[lindex - COAP_OPT_FILTER_LONG - 1] = (uint8_t)number;
486 }
487
488 of->mask |= 1 << (lindex - 1);
489
490 return 1;
491}
492
493void
495 memset(filter, 0, sizeof(coap_opt_filter_t));
496}
497
498int
502
503int
507
508int
512
515 size_t length,
516 const uint8_t *data) {
517 coap_optlist_t *node;
518
519 if (!coap_pdu_parse_opt_base(number, length, data)) {
520 coap_log_warn("coap_new_optlist: %d: Invalid option length / data\n", number);
521 return NULL;
522 }
523#if defined(WITH_LWIP) && MEMP_USE_CUSTOM_POOLS
524 if (length > MEMP_LEN_COAPOPTLIST) {
525 coap_log_crit("coap_new_optlist: size too large (%" PRIuS " > MEMP_LEN_COAPOPTLIST)\n",
526 length);
527 return NULL;
528 }
529#endif /* WITH_LWIP && MEMP_USE_CUSTOM_POOLS */
530 node = coap_malloc_type(COAP_OPTLIST, sizeof(coap_optlist_t) + length);
531
532 if (node) {
533 memset(node, 0, (sizeof(coap_optlist_t) + length));
534 node->number = number;
535 node->length = length;
536 node->data = (uint8_t *)&node[1];
537 memcpy(node->data, data, length);
538 } else {
539 coap_log_warn("coap_new_optlist: malloc failure\n");
540 }
541
542 return node;
543}
544
545static int
546order_opts(void *a, void *b) {
549
550 if (!a || !b)
551 return a < b ? -1 : 1;
552
553 return (int)(o1->number - o2->number);
554}
555
556int
558 coap_optlist_t *opt;
559
560 if (options && *options) {
561 if (pdu->data) {
562 coap_log_warn("coap_add_optlist_pdu: PDU already contains data\n");
563 return 0;
564 }
565 /* sort options for delta encoding */
566 LL_SORT((*options), order_opts);
567
568 LL_FOREACH((*options), opt) {
569 if (!coap_add_option_internal(pdu, opt->number, opt->length, opt->data))
570 return 0;
571 }
572 }
573 return 1;
574}
575
576int
578 if (options && *options) {
579 /* sort options for delta encoding */
580 LL_SORT((*options), order_opts);
581 return 1;
582 }
583 return 0;
584}
585
586int
588 if (!node) {
589 coap_log_debug("optlist not provided\n");
590 } else {
591 /* must append at the list end to avoid re-ordering of
592 * options during sort */
593 LL_APPEND((*head), node);
594 }
595
596 return node != NULL;
597}
598
599static int
601 if (node) {
603 }
604 return 1;
605}
606
607void
609 coap_optlist_t *elt, *tmp;
610
611 if (!queue)
612 return;
613
614 LL_FOREACH_SAFE(queue, elt, tmp) {
616 }
617}
int coap_fls(unsigned int i)
Definition coap_encode.c:21
#define PRIuS
Library specific build wrapper for coap_internal.h.
@ COAP_OPTLIST
Definition coap_mem.h:46
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().
@ FILTER_CLEAR
@ FILTER_GET
@ FILTER_SET
#define ADVANCE_OPT_CHECK(o, e, step)
Definition coap_option.c:34
#define SHORT_MASK
size_t coap_opt_size(const coap_opt_t *opt)
Returns the size of the given option, taking into account a possible option jump.
COAP_STATIC_INLINE int opt_finished(coap_opt_iterator_t *oi)
static int coap_internal_delete(coap_optlist_t *node)
#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.
COAP_STATIC_INLINE int is_long_option(coap_option_num_t number)
Returns true iff number denotes an option number larger than 255.
static int order_opts(void *a, void *b)
#define LONG_MASK
#define NULL
Definition coap_option.h:30
uint16_t coap_option_num_t
Definition coap_option.h:37
uint8_t coap_opt_t
Use byte-oriented access methods here because sliding a complex struct coap_opt_t over the data buffe...
#define coap_log_debug(...)
Definition coap_debug.h:126
#define coap_log_warn(...)
Definition coap_debug.h:108
#define coap_log_crit(...)
Definition coap_debug.h:96
coap_opt_t * coap_option_next(coap_opt_iterator_t *oi)
Updates the iterator oi to point to the next option.
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.
coap_optlist_t * coap_new_optlist(coap_option_num_t number, size_t length, const uint8_t *data)
Create a new optlist entry.
uint32_t coap_opt_length(const coap_opt_t *opt)
Returns the length of the given option.
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.
void coap_delete_optlist(coap_optlist_t *queue)
Removes all entries from the optlist_chain, freeing off their memory usage.
size_t coap_opt_encode_size(uint16_t delta, size_t length)
Compute storage bytes needed for an option with given delta and length.
int coap_sort_optlist(coap_optlist_t **options)
Sorts the optlist chain.
#define COAP_OPT_FILTER_SHORT
The number of option types below 256 that can be stored in an option filter.
int coap_option_filter_unset(coap_opt_filter_t *filter, coap_option_num_t option)
Clears the corresponding entry for number in filter.
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 ...
#define COAP_OPT_FILTER_LONG
The number of option types above 255 that can be stored in an option filter.
void coap_option_filter_clear(coap_opt_filter_t *filter)
Clears filter filter.
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.
int coap_insert_optlist(coap_optlist_t **head, coap_optlist_t *node)
Adds optlist to the given optlist_chain.
const uint8_t * coap_opt_value(const coap_opt_t *opt)
Returns a pointer to the value of the given option.
int coap_option_filter_get(coap_opt_filter_t *filter, coap_option_num_t option)
Checks if number is contained in filter.
int coap_option_filter_set(coap_opt_filter_t *filter, coap_option_num_t option)
Sets the corresponding entry for number in filter.
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.
int coap_pdu_parse_opt_base(coap_option_num_t number, size_t len, const uint8_t *opt_val)
Check the length / data (if appropriate) of an option.
Definition coap_pdu.c:1307
#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:863
#define COAP_STATIC_INLINE
Definition libcoap.h:57
uint8_t short_opts[COAP_OPT_FILTER_SHORT]
uint16_t long_opts[COAP_OPT_FILTER_LONG]
Iterator to run through PDU options.
coap_opt_t * next_option
pointer to the unparsed next option
coap_opt_filter_t filter
option filter
unsigned int bad
iterator object is ok if not set
size_t length
remaining length of PDU
unsigned int filtered
denotes whether or not filter is used
coap_option_num_t number
decoded option number
Representation of CoAP options.
const uint8_t * value
uint16_t delta
Representation of chained list of CoAP options to install.
coap_option_num_t number
the option number (no delta coding)
size_t length
the option value length
uint8_t * data
the option data
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