• 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  *
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(oi);
116 
117   memset(oi, 0, sizeof(coap_opt_iterator_t));
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->number += 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_filter_get(&oi->filter, oi->number)) > 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(const coap_pdu_t * pdu,coap_option_num_t number,coap_opt_iterator_t * oi)201 coap_check_option(const coap_pdu_t *pdu, coap_option_num_t number,
202                   coap_opt_iterator_t *oi) {
203   coap_opt_filter_t f;
204 
205   coap_option_filter_clear(&f);
206   coap_option_filter_set(&f, number);
207 
208   coap_option_iterator_init(pdu, oi, &f);
209 
210   return coap_option_next(oi);
211 }
212 
213 uint32_t
coap_opt_length(const coap_opt_t * opt)214 coap_opt_length(const coap_opt_t *opt) {
215   uint32_t length;
216 
217   length = *opt & 0x0f;
218   switch (*opt & 0xf0) {
219   case 0xf0:
220     coap_log(LOG_DEBUG, "illegal option delta\n");
221     return 0;
222   case 0xe0:
223     ++opt;
224     /* fall through */
225     /* to skip another byte */
226   case 0xd0:
227     ++opt;
228     /* fall through */
229     /* to skip another byte */
230   default:
231     ++opt;
232   }
233 
234   switch (length) {
235   case 0x0f:
236     coap_log(LOG_DEBUG, "illegal option length\n");
237     return 0;
238   case 0x0e:
239     length = (*opt++ << 8) + 269;
240     /* fall through */
241   case 0x0d:
242     length += *opt++;
243     break;
244   default:
245     ;
246   }
247   return length;
248 }
249 
250 const uint8_t *
coap_opt_value(const coap_opt_t * opt)251 coap_opt_value(const coap_opt_t *opt) {
252   size_t ofs = 1;
253 
254   switch (*opt & 0xf0) {
255   case 0xf0:
256     coap_log(LOG_DEBUG, "illegal option delta\n");
257     return 0;
258   case 0xe0:
259     ++ofs;
260     /* fall through */
261   case 0xd0:
262     ++ofs;
263     break;
264   default:
265     ;
266   }
267 
268   switch (*opt & 0x0f) {
269   case 0x0f:
270     coap_log(LOG_DEBUG, "illegal option length\n");
271     return 0;
272   case 0x0e:
273     ++ofs;
274     /* fall through */
275   case 0x0d:
276     ++ofs;
277     break;
278   default:
279     ;
280   }
281 
282   return (const uint8_t *)opt + ofs;
283 }
284 
285 size_t
coap_opt_size(const coap_opt_t * opt)286 coap_opt_size(const coap_opt_t *opt) {
287   coap_option_t option;
288 
289   /* we must assume that opt is encoded correctly */
290   return coap_opt_parse(opt, (size_t)-1, &option);
291 }
292 
293 size_t
coap_opt_setheader(coap_opt_t * opt,size_t maxlen,uint16_t delta,size_t length)294 coap_opt_setheader(coap_opt_t *opt, size_t maxlen,
295                    uint16_t delta, size_t length) {
296   size_t skip = 0;
297 
298   assert(opt);
299 
300   if (maxlen == 0)                /* need at least one byte */
301     return 0;
302 
303   if (delta < 13) {
304     opt[0] = (coap_opt_t)(delta << 4);
305   } else if (delta < 269) {
306     if (maxlen < 2) {
307       coap_log(LOG_DEBUG, "insufficient space to encode option delta %d\n",
308                delta);
309       return 0;
310     }
311 
312     opt[0] = 0xd0;
313     opt[++skip] = (coap_opt_t)(delta - 13);
314   } else {
315     if (maxlen < 3) {
316       coap_log(LOG_DEBUG, "insufficient space to encode option delta %d\n",
317                delta);
318       return 0;
319     }
320 
321     opt[0] = 0xe0;
322     opt[++skip] = ((delta - 269) >> 8) & 0xff;
323     opt[++skip] = (delta - 269) & 0xff;
324   }
325 
326   if (length < 13) {
327     opt[0] |= length & 0x0f;
328   } else if (length < 269) {
329     if (maxlen < skip + 2) {
330       coap_log(LOG_DEBUG, "insufficient space to encode option length %zu\n",
331                length);
332       return 0;
333     }
334 
335     opt[0] |= 0x0d;
336     opt[++skip] = (coap_opt_t)(length - 13);
337   } else {
338     if (maxlen < skip + 3) {
339       coap_log(LOG_DEBUG, "insufficient space to encode option delta %d\n",
340                delta);
341       return 0;
342     }
343 
344     opt[0] |= 0x0e;
345     opt[++skip] = ((length - 269) >> 8) & 0xff;
346     opt[++skip] = (length - 269) & 0xff;
347   }
348 
349   return skip + 1;
350 }
351 
352 size_t
coap_opt_encode_size(uint16_t delta,size_t length)353 coap_opt_encode_size(uint16_t delta, size_t length) {
354   size_t n = 1;
355 
356   if (delta >= 13) {
357     if (delta < 269)
358       n += 1;
359     else
360       n += 2;
361   }
362 
363   if (length >= 13) {
364     if (length < 269)
365       n += 1;
366     else
367       n += 2;
368   }
369 
370   return n + length;
371 }
372 
373 size_t
coap_opt_encode(coap_opt_t * opt,size_t maxlen,uint16_t delta,const uint8_t * val,size_t length)374 coap_opt_encode(coap_opt_t *opt, size_t maxlen, uint16_t delta,
375                 const uint8_t *val, size_t length) {
376   size_t l = 1;
377 
378   l = coap_opt_setheader(opt, maxlen, delta, length);
379   assert(l <= maxlen);
380 
381   if (!l) {
382     coap_log(LOG_DEBUG, "coap_opt_encode: cannot set option header\n");
383     return 0;
384   }
385 
386   maxlen -= l;
387   opt += l;
388 
389   if (maxlen < length) {
390     coap_log(LOG_DEBUG, "coap_opt_encode: option too large for buffer\n");
391     return 0;
392   }
393 
394   if (val)                        /* better be safe here */
395     memcpy(opt, val, length);
396 
397   return l + length;
398 }
399 
400 #define LONG_MASK ((1 << COAP_OPT_FILTER_LONG) - 1)
401 #define SHORT_MASK \
402   (~LONG_MASK & ((1 << (COAP_OPT_FILTER_LONG + COAP_OPT_FILTER_SHORT)) - 1))
403 
404 /** Returns true iff @p number denotes an option number larger than 255. */
405 COAP_STATIC_INLINE int
is_long_option(coap_option_num_t number)406 is_long_option(coap_option_num_t number) { return number > 255; }
407 
408 /** Operation specifiers for coap_filter_op(). */
409 enum filter_op_t { FILTER_SET, FILTER_CLEAR, FILTER_GET };
410 
411 /**
412  * Applies @p op on @p filter with respect to @p number. The following
413  * operations are defined:
414  *
415  * FILTER_SET: Store @p number into an empty slot in @p filter. Returns
416  * @c 1 on success, or @c 0 if no spare slot was available.
417  *
418  * FILTER_CLEAR: Remove @p number from filter if it exists.
419  *
420  * FILTER_GET: Search for @p number in @p filter. Returns @c 1 if found,
421  * or @c 0 if not found.
422  *
423  * @param filter The filter object.
424  * @param number The option number to set, get or clear in @p filter.
425  * @param op     The operation to apply to @p filter and @p number.
426  *
427  * @return 1 on success, and 0 when FILTER_GET yields no
428  * hit or no free slot is available to store @p number with FILTER_SET.
429  */
430 static int
coap_option_filter_op(coap_opt_filter_t * filter,coap_option_num_t number,enum filter_op_t op)431 coap_option_filter_op(coap_opt_filter_t *filter,
432                       coap_option_num_t number,
433                       enum filter_op_t op) {
434   size_t lindex = 0;
435   coap_opt_filter_t *of = filter;
436   uint16_t nr, mask = 0;
437 
438   if (is_long_option(number)) {
439     mask = LONG_MASK;
440 
441     for (nr = 1; lindex < COAP_OPT_FILTER_LONG; nr <<= 1, lindex++) {
442 
443       if (((of->mask & nr) > 0) && (of->long_opts[lindex] == number)) {
444         if (op == FILTER_CLEAR) {
445           of->mask &= ~nr;
446         }
447 
448         return 1;
449       }
450     }
451   } else {
452     mask = SHORT_MASK;
453 
454     for (nr = 1 << COAP_OPT_FILTER_LONG; lindex < COAP_OPT_FILTER_SHORT;
455          nr <<= 1, lindex++) {
456 
457       if (((of->mask & nr) > 0) && (of->short_opts[lindex] == (number & 0xff))) {
458         if (op == FILTER_CLEAR) {
459           of->mask &= ~nr;
460         }
461 
462         return 1;
463       }
464     }
465   }
466 
467   /* number was not found, so there is nothing to do if op is CLEAR or GET */
468   if ((op == FILTER_CLEAR) || (op == FILTER_GET)) {
469     return 0;
470   }
471 
472   /* handle FILTER_SET: */
473 
474   lindex = coap_fls(~of->mask & mask);
475   if (!lindex) {
476     return 0;
477   }
478 
479   if (is_long_option(number)) {
480     of->long_opts[lindex - 1] = number;
481   } else {
482     of->short_opts[lindex - COAP_OPT_FILTER_LONG - 1] = (uint8_t)number;
483   }
484 
485   of->mask |= 1 << (lindex - 1);
486 
487   return 1;
488 }
489 
490 void
coap_option_filter_clear(coap_opt_filter_t * filter)491 coap_option_filter_clear(coap_opt_filter_t *filter) {
492   memset(filter, 0, sizeof(coap_opt_filter_t));
493 }
494 
495 int
coap_option_filter_set(coap_opt_filter_t * filter,coap_option_num_t option)496 coap_option_filter_set(coap_opt_filter_t *filter, coap_option_num_t option) {
497   return coap_option_filter_op(filter, option, FILTER_SET);
498 }
499 
500 int
coap_option_filter_unset(coap_opt_filter_t * filter,coap_option_num_t option)501 coap_option_filter_unset(coap_opt_filter_t *filter, coap_option_num_t option) {
502   return coap_option_filter_op(filter, option, FILTER_CLEAR);
503 }
504 
505 int
coap_option_filter_get(coap_opt_filter_t * filter,coap_option_num_t option)506 coap_option_filter_get(coap_opt_filter_t *filter, coap_option_num_t option) {
507   return coap_option_filter_op(filter, option, FILTER_GET);
508 }
509 
510 coap_optlist_t *
coap_new_optlist(uint16_t number,size_t length,const uint8_t * data)511 coap_new_optlist(uint16_t number,
512                           size_t length,
513                           const uint8_t *data
514 ) {
515   coap_optlist_t *node;
516 
517 #ifdef WITH_LWIP
518   if (length > MEMP_LEN_COAPOPTLIST) {
519     coap_log(LOG_CRIT,
520              "coap_new_optlist: size too large (%zu > MEMP_LEN_COAPOPTLIST)\n",
521              length);
522     return NULL;
523   }
524 #endif /* WITH_LWIP */
525   node = coap_malloc_type(COAP_OPTLIST, sizeof(coap_optlist_t) + length);
526 
527   if (node) {
528     memset(node, 0, (sizeof(coap_optlist_t) + length));
529     node->number = number;
530     node->length = length;
531     node->data = (uint8_t *)&node[1];
532     memcpy(node->data, data, length);
533   } else {
534     coap_log(LOG_WARNING, "coap_new_optlist: malloc failure\n");
535   }
536 
537   return node;
538 }
539 
540 static int
order_opts(void * a,void * b)541 order_opts(void *a, void *b) {
542   coap_optlist_t *o1 = (coap_optlist_t *)a;
543   coap_optlist_t *o2 = (coap_optlist_t *)b;
544 
545   if (!a || !b)
546     return a < b ? -1 : 1;
547 
548   return (int)(o1->number - o2->number);
549 }
550 
551 int
coap_add_optlist_pdu(coap_pdu_t * pdu,coap_optlist_t ** options)552 coap_add_optlist_pdu(coap_pdu_t *pdu, coap_optlist_t** options) {
553   coap_optlist_t *opt;
554 
555   if (options && *options) {
556     /* sort options for delta encoding */
557     LL_SORT((*options), order_opts);
558 
559     LL_FOREACH((*options), opt) {
560       coap_add_option(pdu, opt->number, opt->length, opt->data);
561     }
562     return 1;
563   }
564   return 0;
565 }
566 
567 int
coap_insert_optlist(coap_optlist_t ** head,coap_optlist_t * node)568 coap_insert_optlist(coap_optlist_t **head, coap_optlist_t *node) {
569   if (!node) {
570     coap_log(LOG_DEBUG, "optlist not provided\n");
571   } else {
572     /* must append at the list end to avoid re-ordering of
573      * options during sort */
574     LL_APPEND((*head), node);
575   }
576 
577   return node != NULL;
578 }
579 
580 static int
coap_internal_delete(coap_optlist_t * node)581 coap_internal_delete(coap_optlist_t *node) {
582   if (node) {
583     coap_free_type(COAP_OPTLIST, node);
584   }
585   return 1;
586 }
587 
588 void
coap_delete_optlist(coap_optlist_t * queue)589 coap_delete_optlist(coap_optlist_t *queue) {
590   coap_optlist_t *elt, *tmp;
591 
592   if (!queue)
593     return;
594 
595   LL_FOREACH_SAFE(queue, elt, tmp) {
596     coap_internal_delete(elt);
597   }
598 }
599 
600