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
36 size_t
coap_opt_parse(const coap_opt_t * opt,size_t length,coap_option_t * result)37 coap_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
111 coap_opt_iterator_t *
coap_option_iterator_init(const coap_pdu_t * pdu,coap_opt_iterator_t * oi,const coap_opt_filter_t * filter)112 coap_option_iterator_init(const coap_pdu_t *pdu, coap_opt_iterator_t *oi,
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
135 COAP_STATIC_INLINE int
opt_finished(coap_opt_iterator_t * oi)136 opt_finished(coap_opt_iterator_t *oi) {
137 assert(oi);
138
139 if (oi->bad || oi->length == 0 ||
140 !oi->next_option || *oi->next_option == COAP_PAYLOAD_START) {
141 oi->bad = 1;
142 }
143
144 return oi->bad;
145 }
146
147 coap_opt_t *
coap_option_next(coap_opt_iterator_t * oi)148 coap_option_next(coap_opt_iterator_t *oi) {
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
197 coap_opt_t *
coap_check_option(const coap_pdu_t * pdu,coap_option_num_t number,coap_opt_iterator_t * oi)198 coap_check_option(const coap_pdu_t *pdu, coap_option_num_t number,
199 coap_opt_iterator_t *oi) {
200 coap_opt_filter_t f;
201
202 coap_option_filter_clear(&f);
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
210 uint32_t
coap_opt_length(const coap_opt_t * opt)211 coap_opt_length(const coap_opt_t *opt) {
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
247 const uint8_t *
coap_opt_value(const coap_opt_t * opt)248 coap_opt_value(const coap_opt_t *opt) {
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
282 size_t
coap_opt_size(const coap_opt_t * opt)283 coap_opt_size(const coap_opt_t *opt) {
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
290 size_t
coap_opt_setheader(coap_opt_t * opt,size_t maxlen,uint16_t delta,size_t length)291 coap_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
349 size_t
coap_opt_encode_size(uint16_t delta,size_t length)350 coap_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
370 size_t
coap_opt_encode(coap_opt_t * opt,size_t maxlen,uint16_t delta,const uint8_t * val,size_t length)371 coap_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
401 /** Returns true iff @p number denotes an option number larger than 255. */
402 COAP_STATIC_INLINE int
is_long_option(coap_option_num_t number)403 is_long_option(coap_option_num_t number) { return number > 255; }
404
405 /** Operation specifiers for coap_filter_op(). */
406 enum filter_op_t { FILTER_SET, FILTER_CLEAR, FILTER_GET };
407
408 /**
409 * Applies @p op on @p filter with respect to @p number. The following
410 * operations are defined:
411 *
412 * FILTER_SET: Store @p number into an empty slot in @p filter. Returns
413 * @c 1 on success, or @c 0 if no spare slot was available.
414 *
415 * FILTER_CLEAR: Remove @p number from filter if it exists.
416 *
417 * FILTER_GET: Search for @p number in @p filter. Returns @c 1 if found,
418 * or @c 0 if not found.
419 *
420 * @param filter The filter object.
421 * @param number The option number to set, get or clear in @p filter.
422 * @param op The operation to apply to @p filter and @p number.
423 *
424 * @return 1 on success, and 0 when FILTER_GET yields no
425 * hit or no free slot is available to store @p number with FILTER_SET.
426 */
427 static int
coap_option_filter_op(coap_opt_filter_t * filter,coap_option_num_t number,enum filter_op_t op)428 coap_option_filter_op(coap_opt_filter_t *filter,
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
487 void
coap_option_filter_clear(coap_opt_filter_t * filter)488 coap_option_filter_clear(coap_opt_filter_t *filter) {
489 memset(filter, 0, sizeof(coap_opt_filter_t));
490 }
491
492 int
coap_option_filter_set(coap_opt_filter_t * filter,coap_option_num_t option)493 coap_option_filter_set(coap_opt_filter_t *filter, coap_option_num_t option) {
494 return coap_option_filter_op(filter, option, FILTER_SET);
495 }
496
497 int
coap_option_filter_unset(coap_opt_filter_t * filter,coap_option_num_t option)498 coap_option_filter_unset(coap_opt_filter_t *filter, coap_option_num_t option) {
499 return coap_option_filter_op(filter, option, FILTER_CLEAR);
500 }
501
502 int
coap_option_filter_get(coap_opt_filter_t * filter,coap_option_num_t option)503 coap_option_filter_get(coap_opt_filter_t *filter, coap_option_num_t option) {
504 return coap_option_filter_op(filter, option, FILTER_GET);
505 }
506
507 coap_optlist_t *
coap_new_optlist(uint16_t number,size_t length,const uint8_t * data)508 coap_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) {
516 coap_log(LOG_CRIT,
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
537 static int
order_opts(void * a,void * b)538 order_opts(void *a, void *b) {
539 coap_optlist_t *o1 = (coap_optlist_t *)a;
540 coap_optlist_t *o2 = (coap_optlist_t *)b;
541
542 if (!a || !b)
543 return a < b ? -1 : 1;
544
545 return (int)(o1->number - o2->number);
546 }
547
548 int
coap_add_optlist_pdu(coap_pdu_t * pdu,coap_optlist_t ** options)549 coap_add_optlist_pdu(coap_pdu_t *pdu, coap_optlist_t** options) {
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
564 int
coap_insert_optlist(coap_optlist_t ** head,coap_optlist_t * node)565 coap_insert_optlist(coap_optlist_t **head, coap_optlist_t *node) {
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
577 static int
coap_internal_delete(coap_optlist_t * node)578 coap_internal_delete(coap_optlist_t *node) {
579 if (node) {
580 coap_free_type(COAP_OPTLIST, node);
581 }
582 return 1;
583 }
584
585 void
coap_delete_optlist(coap_optlist_t * queue)586 coap_delete_optlist(coap_optlist_t *queue) {
587 coap_optlist_t *elt, *tmp;
588
589 if (!queue)
590 return;
591
592 LL_FOREACH_SAFE(queue, elt, tmp) {
593 coap_internal_delete(elt);
594 }
595 }
596
597