1 /*
2 * option.c -- helpers for handling options in CoAP PDUs
3 *
4 * Copyright (C) 2010-2013 Olaf Bergmann <bergmann@tzi.org>
5 * Copyright (c) 2021 Huawei Device Co., Ltd. All rights reserved.
6 *
7 * This file is part of the CoAP library libcoap. Please see
8 * README for terms of use.
9 */
10
11
12 #include "coap_internal.h"
13
14 #include <stdio.h>
15 #include <string.h>
16
17 #define ADVANCE_OPT(o,e,step) if ((e) < step) { \
18 coap_log(LOG_DEBUG, "cannot advance opt past end\n"); \
19 return 0; \
20 } else { \
21 (e) -= step; \
22 (o) = ((o)) + step; \
23 }
24
25 /*
26 * Used to prevent access to *opt when pointing to after end of buffer
27 * after doing a ADVANCE_OPT()
28 */
29 #define ADVANCE_OPT_CHECK(o,e,step) do { \
30 ADVANCE_OPT(o,e,step); \
31 if ((e) < 1) \
32 return 0; \
33 } while (0)
34
35 size_t
coap_opt_parse(const coap_opt_t * opt,size_t length,coap_option_t * result)36 coap_opt_parse(const coap_opt_t *opt, size_t length, coap_option_t *result) {
37
38 const coap_opt_t *opt_start = opt; /* store where parsing starts */
39
40 assert(opt); assert(result);
41
42 if (length < 1)
43 return 0;
44
45 result->delta = (*opt & 0xf0) >> 4;
46 result->length = *opt & 0x0f;
47
48 switch(result->delta) {
49 case 15:
50 if (*opt != COAP_PAYLOAD_START) {
51 coap_log(LOG_DEBUG, "ignored reserved option delta 15\n");
52 }
53 return 0;
54 case 14:
55 /* Handle two-byte value: First, the MSB + 269 is stored as delta value.
56 * After that, the option pointer is advanced to the LSB which is handled
57 * just like case delta == 13. */
58 ADVANCE_OPT_CHECK(opt,length,1);
59 result->delta = ((*opt & 0xff) << 8) + 269;
60 if (result->delta < 269) {
61 coap_log(LOG_DEBUG, "delta too large\n");
62 return 0;
63 }
64 /* fall through */
65 case 13:
66 ADVANCE_OPT_CHECK(opt,length,1);
67 result->delta += *opt & 0xff;
68 break;
69
70 default:
71 ;
72 }
73
74 switch(result->length) {
75 case 15:
76 coap_log(LOG_DEBUG, "found reserved option length 15\n");
77 return 0;
78 case 14:
79 /* Handle two-byte value: First, the MSB + 269 is stored as delta value.
80 * After that, the option pointer is advanced to the LSB which is handled
81 * just like case delta == 13. */
82 ADVANCE_OPT_CHECK(opt,length,1);
83 result->length = ((*opt & 0xff) << 8) + 269;
84 /* fall through */
85 case 13:
86 ADVANCE_OPT_CHECK(opt,length,1);
87 result->length += *opt & 0xff;
88 break;
89
90 default:
91 ;
92 }
93
94 /* ADVANCE_OPT() is correct here */
95 ADVANCE_OPT(opt,length,1);
96 /* opt now points to value, if present */
97
98 result->value = opt;
99 if (length < result->length) {
100 coap_log(LOG_DEBUG, "invalid option length\n");
101 return 0;
102 }
103
104 #undef ADVANCE_OPT
105 #undef ADVANCE_OPT_CHECK
106
107 return (opt + result->length) - opt_start;
108 }
109
110 coap_opt_iterator_t *
coap_option_iterator_init(const coap_pdu_t * pdu,coap_opt_iterator_t * oi,const coap_opt_filter_t filter)111 coap_option_iterator_init(const coap_pdu_t *pdu, coap_opt_iterator_t *oi,
112 const coap_opt_filter_t filter) {
113 assert(pdu);
114 assert(oi);
115
116 memset(oi, 0, sizeof(coap_opt_iterator_t));
117
118 if (pdu->token == NULL) {
119 oi->bad = 1;
120 return NULL;
121 }
122
123 oi->next_option = pdu->token + pdu->token_length;
124 if (pdu->token + pdu->used_size <= oi->next_option) {
125 oi->bad = 1;
126 return NULL;
127 }
128
129 oi->length = pdu->used_size - pdu->token_length;
130
131 if (filter) {
132 memcpy(oi->filter, filter, sizeof(coap_opt_filter_t));
133 oi->filtered = 1;
134 }
135 return oi;
136 }
137
138 COAP_STATIC_INLINE int
opt_finished(coap_opt_iterator_t * oi)139 opt_finished(coap_opt_iterator_t *oi) {
140 assert(oi);
141
142 if (oi->bad || oi->length == 0 ||
143 !oi->next_option || *oi->next_option == COAP_PAYLOAD_START) {
144 oi->bad = 1;
145 }
146
147 return oi->bad;
148 }
149
150 coap_opt_t *
coap_option_next(coap_opt_iterator_t * oi)151 coap_option_next(coap_opt_iterator_t *oi) {
152 coap_option_t option;
153 coap_opt_t *current_opt = NULL;
154 size_t optsize;
155 int b; /* to store result of coap_option_getb() */
156
157 assert(oi);
158
159 if (opt_finished(oi))
160 return NULL;
161
162 while (1) {
163 /* oi->option always points to the next option to deliver; as
164 * opt_finished() filters out any bad conditions, we can assume that
165 * oi->option is valid. */
166 current_opt = oi->next_option;
167
168 /* Advance internal pointer to next option, skipping any option that
169 * is not included in oi->filter. */
170 optsize = coap_opt_parse(oi->next_option, oi->length, &option);
171 if (optsize) {
172 assert(optsize <= oi->length);
173
174 oi->next_option += optsize;
175 oi->length -= optsize;
176
177 oi->type += option.delta;
178 } else { /* current option is malformed */
179 oi->bad = 1;
180 return NULL;
181 }
182
183 /* Exit the while loop when:
184 * - no filtering is done at all
185 * - the filter matches for the current option
186 * - the filter is too small for the current option number
187 */
188 if (!oi->filtered ||
189 (b = coap_option_getb(oi->filter, oi->type)) > 0)
190 break;
191 else if (b < 0) { /* filter too small, cannot proceed */
192 oi->bad = 1;
193 return NULL;
194 }
195 }
196
197 return current_opt;
198 }
199
200 coap_opt_t *
coap_check_option(coap_pdu_t * pdu,uint16_t type,coap_opt_iterator_t * oi)201 coap_check_option(coap_pdu_t *pdu, uint16_t type,
202 coap_opt_iterator_t *oi) {
203 coap_opt_filter_t f;
204
205 coap_option_filter_clear(f);
206 coap_option_setb(f, type);
207
208 coap_option_iterator_init(pdu, oi, f);
209
210 return coap_option_next(oi);
211 }
212
213 uint16_t
coap_opt_delta(const coap_opt_t * opt)214 coap_opt_delta(const coap_opt_t *opt) {
215 uint16_t n;
216
217 n = (*opt++ & 0xf0) >> 4;
218
219 switch (n) {
220 case 15: /* error */
221 coap_log(LOG_WARNING, "coap_opt_delta: illegal option delta\n");
222
223 /* This case usually should not happen, hence we do not have a
224 * proper way to indicate an error. */
225 return 0;
226 case 14:
227 /* Handle two-byte value: First, the MSB + 269 is stored as delta value.
228 * After that, the option pointer is advanced to the LSB which is handled
229 * just like case delta == 13. */
230 n = ((*opt++ & 0xff) << 8) + 269;
231 /* fall through */
232 case 13:
233 n += *opt & 0xff;
234 break;
235 default: /* n already contains the actual delta value */
236 ;
237 }
238
239 return n;
240 }
241
242 uint16_t
coap_opt_length(const coap_opt_t * opt)243 coap_opt_length(const coap_opt_t *opt) {
244 uint16_t length;
245
246 length = *opt & 0x0f;
247 switch (*opt & 0xf0) {
248 case 0xf0:
249 coap_log(LOG_DEBUG, "illegal option delta\n");
250 return 0;
251 case 0xe0:
252 ++opt;
253 /* fall through */
254 /* to skip another byte */
255 case 0xd0:
256 ++opt;
257 /* fall through */
258 /* to skip another byte */
259 default:
260 ++opt;
261 }
262
263 switch (length) {
264 case 0x0f:
265 coap_log(LOG_DEBUG, "illegal option length\n");
266 return 0;
267 case 0x0e:
268 length = (*opt++ << 8) + 269;
269 /* fall through */
270 case 0x0d:
271 length += *opt++;
272 break;
273 default:
274 ;
275 }
276 return length;
277 }
278
279 const uint8_t *
coap_opt_value(const coap_opt_t * opt)280 coap_opt_value(const coap_opt_t *opt) {
281 size_t ofs = 1;
282
283 switch (*opt & 0xf0) {
284 case 0xf0:
285 coap_log(LOG_DEBUG, "illegal option delta\n");
286 return 0;
287 case 0xe0:
288 ++ofs;
289 /* fall through */
290 case 0xd0:
291 ++ofs;
292 break;
293 default:
294 ;
295 }
296
297 switch (*opt & 0x0f) {
298 case 0x0f:
299 coap_log(LOG_DEBUG, "illegal option length\n");
300 return 0;
301 case 0x0e:
302 ++ofs;
303 /* fall through */
304 case 0x0d:
305 ++ofs;
306 break;
307 default:
308 ;
309 }
310
311 return (const uint8_t *)opt + ofs;
312 }
313
314 size_t
coap_opt_size(const coap_opt_t * opt)315 coap_opt_size(const coap_opt_t *opt) {
316 coap_option_t option;
317
318 /* we must assume that opt is encoded correctly */
319 return coap_opt_parse(opt, (size_t)-1, &option);
320 }
321
322 size_t
coap_opt_setheader(coap_opt_t * opt,size_t maxlen,uint16_t delta,size_t length)323 coap_opt_setheader(coap_opt_t *opt, size_t maxlen,
324 uint16_t delta, size_t length) {
325 size_t skip = 0;
326
327 assert(opt);
328
329 if (maxlen == 0) /* need at least one byte */
330 return 0;
331
332 if (delta < 13) {
333 opt[0] = (coap_opt_t)(delta << 4);
334 } else if (delta < 269) {
335 if (maxlen < 2) {
336 coap_log(LOG_DEBUG, "insufficient space to encode option delta %d\n",
337 delta);
338 return 0;
339 }
340
341 opt[0] = 0xd0;
342 opt[++skip] = (coap_opt_t)(delta - 13);
343 } else {
344 if (maxlen < 3) {
345 coap_log(LOG_DEBUG, "insufficient space to encode option delta %d\n",
346 delta);
347 return 0;
348 }
349
350 opt[0] = 0xe0;
351 opt[++skip] = ((delta - 269) >> 8) & 0xff;
352 opt[++skip] = (delta - 269) & 0xff;
353 }
354
355 if (length < 13) {
356 opt[0] |= length & 0x0f;
357 } else if (length < 269) {
358 if (maxlen < skip + 2) {
359 coap_log(LOG_DEBUG, "insufficient space to encode option length %zu\n",
360 length);
361 return 0;
362 }
363
364 opt[0] |= 0x0d;
365 opt[++skip] = (coap_opt_t)(length - 13);
366 } else {
367 if (maxlen < skip + 3) {
368 coap_log(LOG_DEBUG, "insufficient space to encode option delta %d\n",
369 delta);
370 return 0;
371 }
372
373 opt[0] |= 0x0e;
374 opt[++skip] = ((length - 269) >> 8) & 0xff;
375 opt[++skip] = (length - 269) & 0xff;
376 }
377
378 return skip + 1;
379 }
380
381 size_t
coap_opt_encode_size(uint16_t delta,size_t length)382 coap_opt_encode_size(uint16_t delta, size_t length) {
383 size_t n = 1;
384
385 if (delta >= 13) {
386 if (delta < 269)
387 n += 1;
388 else
389 n += 2;
390 }
391
392 if (length >= 13) {
393 if (length < 269)
394 n += 1;
395 else
396 n += 2;
397 }
398
399 return n + length;
400 }
401
402 size_t
coap_opt_encode(coap_opt_t * opt,size_t maxlen,uint16_t delta,const uint8_t * val,size_t length)403 coap_opt_encode(coap_opt_t *opt, size_t maxlen, uint16_t delta,
404 const uint8_t *val, size_t length) {
405 size_t l = 1;
406
407 l = coap_opt_setheader(opt, maxlen, delta, length);
408 assert(l <= maxlen);
409
410 if (!l) {
411 coap_log(LOG_DEBUG, "coap_opt_encode: cannot set option header\n");
412 return 0;
413 }
414
415 maxlen -= l;
416 opt += l;
417
418 if (maxlen < length) {
419 coap_log(LOG_DEBUG, "coap_opt_encode: option too large for buffer\n");
420 return 0;
421 }
422
423 if (val) /* better be safe here */
424 memcpy(opt, val, length);
425
426 return l + length;
427 }
428
429 /* coap_opt_filter_t has the following internal structure: */
430 typedef struct {
431 uint16_t mask;
432
433 #define LONG_MASK ((1 << COAP_OPT_FILTER_LONG) - 1)
434 #define SHORT_MASK \
435 (~LONG_MASK & ((1 << (COAP_OPT_FILTER_LONG + COAP_OPT_FILTER_SHORT)) - 1))
436
437 uint16_t long_opts[COAP_OPT_FILTER_LONG];
438 uint8_t short_opts[COAP_OPT_FILTER_SHORT];
439 } opt_filter;
440
441 /** Returns true iff @p type denotes an option type larger than 255. */
442 COAP_STATIC_INLINE int
is_long_option(uint16_t type)443 is_long_option(uint16_t type) { return type > 255; }
444
445 /** Operation specifiers for coap_filter_op(). */
446 enum filter_op_t { FILTER_SET, FILTER_CLEAR, FILTER_GET };
447
448 /**
449 * Applies @p op on @p filter with respect to @p type. The following
450 * operations are defined:
451 *
452 * FILTER_SET: Store @p type into an empty slot in @p filter. Returns
453 * @c 1 on success, or @c 0 if no spare slot was available.
454 *
455 * FILTER_CLEAR: Remove @p type from filter if it exists.
456 *
457 * FILTER_GET: Search for @p type in @p filter. Returns @c 1 if found,
458 * or @c 0 if not found.
459 *
460 * @param filter The filter object.
461 * @param type The option type to set, get or clear in @p filter.
462 * @param op The operation to apply to @p filter and @p type.
463 *
464 * @return 1 on success, and 0 when FILTER_GET yields no
465 * hit or no free slot is available to store @p type with FILTER_SET.
466 */
467 static int
coap_option_filter_op(coap_opt_filter_t filter,uint16_t type,enum filter_op_t op)468 coap_option_filter_op(coap_opt_filter_t filter,
469 uint16_t type,
470 enum filter_op_t op) {
471 size_t lindex = 0;
472 opt_filter *of = (opt_filter *)filter;
473 uint16_t nr, mask = 0;
474
475 if (is_long_option(type)) {
476 mask = LONG_MASK;
477
478 for (nr = 1; lindex < COAP_OPT_FILTER_LONG; nr <<= 1, lindex++) {
479
480 if (((of->mask & nr) > 0) && (of->long_opts[lindex] == type)) {
481 if (op == FILTER_CLEAR) {
482 of->mask &= ~nr;
483 }
484
485 return 1;
486 }
487 }
488 } else {
489 mask = SHORT_MASK;
490
491 for (nr = 1 << COAP_OPT_FILTER_LONG; lindex < COAP_OPT_FILTER_SHORT;
492 nr <<= 1, lindex++) {
493
494 if (((of->mask & nr) > 0) && (of->short_opts[lindex] == (type & 0xff))) {
495 if (op == FILTER_CLEAR) {
496 of->mask &= ~nr;
497 }
498
499 return 1;
500 }
501 }
502 }
503
504 /* type was not found, so there is nothing to do if op is CLEAR or GET */
505 if ((op == FILTER_CLEAR) || (op == FILTER_GET)) {
506 return 0;
507 }
508
509 /* handle FILTER_SET: */
510
511 lindex = coap_fls(~of->mask & mask);
512 if (!lindex) {
513 return 0;
514 }
515
516 if (is_long_option(type)) {
517 of->long_opts[lindex - 1] = type;
518 } else {
519 of->short_opts[lindex - COAP_OPT_FILTER_LONG - 1] = (uint8_t)type;
520 }
521
522 of->mask |= 1 << (lindex - 1);
523
524 return 1;
525 }
526
527 int
coap_option_filter_set(coap_opt_filter_t filter,uint16_t type)528 coap_option_filter_set(coap_opt_filter_t filter, uint16_t type) {
529 return coap_option_filter_op(filter, type, FILTER_SET);
530 }
531
532 int
coap_option_filter_unset(coap_opt_filter_t filter,uint16_t type)533 coap_option_filter_unset(coap_opt_filter_t filter, uint16_t type) {
534 return coap_option_filter_op(filter, type, FILTER_CLEAR);
535 }
536
537 int
coap_option_filter_get(coap_opt_filter_t filter,uint16_t type)538 coap_option_filter_get(coap_opt_filter_t filter, uint16_t type) {
539 /* Ugly cast to make the const go away (FILTER_GET wont change filter
540 * but as _set and _unset do, the function does not take a const). */
541 return coap_option_filter_op((uint16_t *)filter, type, FILTER_GET);
542 }
543
544 coap_optlist_t *
coap_new_optlist(uint16_t number,size_t length,const uint8_t * data)545 coap_new_optlist(uint16_t number,
546 size_t length,
547 const uint8_t *data
548 ) {
549 coap_optlist_t *node;
550
551 node = coap_malloc_type(COAP_OPTLIST, sizeof(coap_optlist_t) + length);
552
553 if (node) {
554 memset(node, 0, (sizeof(coap_optlist_t) + length));
555 node->number = number;
556 node->length = length;
557 node->data = (uint8_t *)&node[1];
558 memcpy(node->data, data, length);
559 } else {
560 coap_log(LOG_WARNING, "coap_new_optlist: malloc failure\n");
561 }
562
563 return node;
564 }
565
566 static int
order_opts(void * a,void * b)567 order_opts(void *a, void *b) {
568 coap_optlist_t *o1 = (coap_optlist_t *)a;
569 coap_optlist_t *o2 = (coap_optlist_t *)b;
570
571 if (!a || !b)
572 return a < b ? -1 : 1;
573
574 return (int)(o1->number - o2->number);
575 }
576
577 int
coap_add_optlist_pdu(coap_pdu_t * pdu,coap_optlist_t ** options)578 coap_add_optlist_pdu(coap_pdu_t *pdu, coap_optlist_t** options) {
579 coap_optlist_t *opt;
580
581 if (options && *options) {
582 /* sort options for delta encoding */
583 LL_SORT((*options), order_opts);
584
585 LL_FOREACH((*options), opt) {
586 coap_add_option(pdu, opt->number, opt->length, opt->data);
587 }
588 return 1;
589 }
590 return 0;
591 }
592
593 int
coap_insert_optlist(coap_optlist_t ** head,coap_optlist_t * node)594 coap_insert_optlist(coap_optlist_t **head, coap_optlist_t *node) {
595 if (!node) {
596 coap_log(LOG_DEBUG, "optlist not provided\n");
597 } else {
598 /* must append at the list end to avoid re-ordering of
599 * options during sort */
600 LL_APPEND((*head), node);
601 }
602
603 return node != NULL;
604 }
605
606 static int
coap_internal_delete(coap_optlist_t * node)607 coap_internal_delete(coap_optlist_t *node) {
608 if (node) {
609 coap_free_type(COAP_OPTLIST, node);
610 }
611 return 1;
612 }
613
614 void
coap_delete_optlist(coap_optlist_t * queue)615 coap_delete_optlist(coap_optlist_t *queue) {
616 coap_optlist_t *elt, *tmp;
617
618 if (!queue)
619 return;
620
621 LL_FOREACH_SAFE(queue, elt, tmp) {
622 coap_internal_delete(elt);
623 }
624 }
625
626