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