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