• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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