libcoap  4.3.1
coap_option.c
Go to the documentation of this file.
1 /*
2  * coap_option.c -- helpers for handling options in CoAP PDUs
3  *
4  * Copyright (C) 2010-2013,2022 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 
17 #include "coap3/coap_internal.h"
18 
19 #include <stdio.h>
20 #include <string.h>
21 
22 #define ADVANCE_OPT(o,e,step) if ((e) < step) { \
23  coap_log(LOG_DEBUG, "cannot advance opt past end\n"); \
24  return 0; \
25  } else { \
26  (e) -= step; \
27  (o) = ((o)) + step; \
28  }
29 
30 /*
31  * Used to prevent access to *opt when pointing to after end of buffer
32  * after doing a ADVANCE_OPT()
33  */
34 #define ADVANCE_OPT_CHECK(o,e,step) do { \
35  ADVANCE_OPT(o,e,step); \
36  if ((e) < 1) \
37  return 0; \
38  } while (0)
39 
40 size_t
41 coap_opt_parse(const coap_opt_t *opt, size_t length, coap_option_t *result) {
42 
43  const coap_opt_t *opt_start = opt; /* store where parsing starts */
44 
45  assert(opt); assert(result);
46 
47  if (length < 1)
48  return 0;
49 
50  result->delta = (*opt & 0xf0) >> 4;
51  result->length = *opt & 0x0f;
52 
53  switch(result->delta) {
54  case 15:
55  if (*opt != COAP_PAYLOAD_START) {
56  coap_log(LOG_DEBUG, "ignored reserved option delta 15\n");
57  }
58  return 0;
59  case 14:
60  /* Handle two-byte value: First, the MSB + 269 is stored as delta value.
61  * After that, the option pointer is advanced to the LSB which is handled
62  * just like case delta == 13. */
63  ADVANCE_OPT_CHECK(opt,length,1);
64  result->delta = ((*opt & 0xff) << 8) + 269;
65  if (result->delta < 269) {
66  coap_log(LOG_DEBUG, "delta too large\n");
67  return 0;
68  }
69  /* fall through */
70  case 13:
71  ADVANCE_OPT_CHECK(opt,length,1);
72  result->delta += *opt & 0xff;
73  break;
74 
75  default:
76  ;
77  }
78 
79  switch(result->length) {
80  case 15:
81  coap_log(LOG_DEBUG, "found reserved option length 15\n");
82  return 0;
83  case 14:
84  /* Handle two-byte value: First, the MSB + 269 is stored as delta value.
85  * After that, the option pointer is advanced to the LSB which is handled
86  * just like case delta == 13. */
87  ADVANCE_OPT_CHECK(opt,length,1);
88  result->length = ((*opt & 0xff) << 8) + 269;
89  /* fall through */
90  case 13:
91  ADVANCE_OPT_CHECK(opt,length,1);
92  result->length += *opt & 0xff;
93  break;
94 
95  default:
96  ;
97  }
98 
99  /* ADVANCE_OPT() is correct here */
100  ADVANCE_OPT(opt,length,1);
101  /* opt now points to value, if present */
102 
103  result->value = opt;
104  if (length < result->length) {
105  coap_log(LOG_DEBUG, "invalid option length\n");
106  return 0;
107  }
108 
109 #undef ADVANCE_OPT
110 #undef ADVANCE_OPT_CHECK
111 
112  return (opt + result->length) - opt_start;
113 }
114 
117  const coap_opt_filter_t *filter) {
118  assert(pdu);
119  assert(pdu->token);
120  assert(oi);
121 
122  memset(oi, 0, sizeof(coap_opt_iterator_t));
123 
124  oi->next_option = pdu->token + pdu->token_length;
125  if (pdu->token + pdu->used_size <= oi->next_option) {
126  oi->bad = 1;
127  return NULL;
128  }
129 
130  oi->length = pdu->used_size - pdu->token_length;
131 
132  if (filter) {
133  memcpy(&oi->filter, filter, sizeof(coap_opt_filter_t));
134  oi->filtered = 1;
135  }
136  return oi;
137 }
138 
141  assert(oi);
142 
143  if (oi->bad || oi->length == 0 ||
144  !oi->next_option || *oi->next_option == COAP_PAYLOAD_START) {
145  oi->bad = 1;
146  }
147 
148  return oi->bad;
149 }
150 
151 coap_opt_t *
153  coap_option_t option;
154  coap_opt_t *current_opt = NULL;
155  size_t optsize;
156  int b; /* to store result of coap_option_getb() */
157 
158  assert(oi);
159 
160  if (opt_finished(oi))
161  return NULL;
162 
163  while (1) {
164  /* oi->option always points to the next option to deliver; as
165  * opt_finished() filters out any bad conditions, we can assume that
166  * oi->option is valid. */
167  current_opt = oi->next_option;
168 
169  /* Advance internal pointer to next option, skipping any option that
170  * is not included in oi->filter. */
171  optsize = coap_opt_parse(oi->next_option, oi->length, &option);
172  if (optsize) {
173  assert(optsize <= oi->length);
174 
175  oi->next_option += optsize;
176  oi->length -= optsize;
177 
178  oi->number += option.delta;
179  } else { /* current option is malformed */
180  oi->bad = 1;
181  return NULL;
182  }
183 
184  /* Exit the while loop when:
185  * - no filtering is done at all
186  * - the filter matches for the current option
187  * - the filter is too small for the current option number
188  */
189  if (!oi->filtered ||
190  (b = coap_option_filter_get(&oi->filter, oi->number)) > 0)
191  break;
192  else if (b < 0) { /* filter too small, cannot proceed */
193  oi->bad = 1;
194  return NULL;
195  }
196  }
197 
198  return current_opt;
199 }
200 
201 coap_opt_t *
203  coap_opt_iterator_t *oi) {
205 
207  coap_option_filter_set(&f, number);
208 
209  coap_option_iterator_init(pdu, oi, &f);
210 
211  return coap_option_next(oi);
212 }
213 
214 uint32_t
216  uint32_t length;
217 
218  length = *opt & 0x0f;
219  switch (*opt & 0xf0) {
220  case 0xf0:
221  coap_log(LOG_DEBUG, "illegal option delta\n");
222  return 0;
223  case 0xe0:
224  ++opt;
225  /* fall through */
226  /* to skip another byte */
227  case 0xd0:
228  ++opt;
229  /* fall through */
230  /* to skip another byte */
231  default:
232  ++opt;
233  }
234 
235  switch (length) {
236  case 0x0f:
237  coap_log(LOG_DEBUG, "illegal option length\n");
238  return 0;
239  case 0x0e:
240  length = (*opt++ << 8) + 269;
241  /* fall through */
242  case 0x0d:
243  length += *opt++;
244  break;
245  default:
246  ;
247  }
248  return length;
249 }
250 
251 const uint8_t *
253  size_t ofs = 1;
254 
255  switch (*opt & 0xf0) {
256  case 0xf0:
257  coap_log(LOG_DEBUG, "illegal option delta\n");
258  return 0;
259  case 0xe0:
260  ++ofs;
261  /* fall through */
262  case 0xd0:
263  ++ofs;
264  break;
265  default:
266  ;
267  }
268 
269  switch (*opt & 0x0f) {
270  case 0x0f:
271  coap_log(LOG_DEBUG, "illegal option length\n");
272  return 0;
273  case 0x0e:
274  ++ofs;
275  /* fall through */
276  case 0x0d:
277  ++ofs;
278  break;
279  default:
280  ;
281  }
282 
283  return (const uint8_t *)opt + ofs;
284 }
285 
286 size_t
288  coap_option_t option;
289 
290  /* we must assume that opt is encoded correctly */
291  return coap_opt_parse(opt, (size_t)-1, &option);
292 }
293 
294 size_t
295 coap_opt_setheader(coap_opt_t *opt, size_t maxlen,
296  uint16_t delta, size_t length) {
297  size_t skip = 0;
298 
299  assert(opt);
300 
301  if (maxlen == 0) /* need at least one byte */
302  return 0;
303 
304  if (delta < 13) {
305  opt[0] = (coap_opt_t)(delta << 4);
306  } else if (delta < 269) {
307  if (maxlen < 2) {
308  coap_log(LOG_DEBUG, "insufficient space to encode option delta %d\n",
309  delta);
310  return 0;
311  }
312 
313  opt[0] = 0xd0;
314  opt[++skip] = (coap_opt_t)(delta - 13);
315  } else {
316  if (maxlen < 3) {
317  coap_log(LOG_DEBUG, "insufficient space to encode option delta %d\n",
318  delta);
319  return 0;
320  }
321 
322  opt[0] = 0xe0;
323  opt[++skip] = ((delta - 269) >> 8) & 0xff;
324  opt[++skip] = (delta - 269) & 0xff;
325  }
326 
327  if (length < 13) {
328  opt[0] |= length & 0x0f;
329  } else if (length < 269) {
330  if (maxlen < skip + 2) {
331  coap_log(LOG_DEBUG, "insufficient space to encode option length %zu\n",
332  length);
333  return 0;
334  }
335 
336  opt[0] |= 0x0d;
337  opt[++skip] = (coap_opt_t)(length - 13);
338  } else {
339  if (maxlen < skip + 3) {
340  coap_log(LOG_DEBUG, "insufficient space to encode option delta %d\n",
341  delta);
342  return 0;
343  }
344 
345  opt[0] |= 0x0e;
346  opt[++skip] = ((length - 269) >> 8) & 0xff;
347  opt[++skip] = (length - 269) & 0xff;
348  }
349 
350  return skip + 1;
351 }
352 
353 size_t
354 coap_opt_encode_size(uint16_t delta, size_t length) {
355  size_t n = 1;
356 
357  if (delta >= 13) {
358  if (delta < 269)
359  n += 1;
360  else
361  n += 2;
362  }
363 
364  if (length >= 13) {
365  if (length < 269)
366  n += 1;
367  else
368  n += 2;
369  }
370 
371  return n + length;
372 }
373 
374 size_t
375 coap_opt_encode(coap_opt_t *opt, size_t maxlen, uint16_t delta,
376  const uint8_t *val, size_t length) {
377  size_t l = 1;
378 
379  l = coap_opt_setheader(opt, maxlen, delta, length);
380  assert(l <= maxlen);
381 
382  if (!l) {
383  coap_log(LOG_DEBUG, "coap_opt_encode: cannot set option header\n");
384  return 0;
385  }
386 
387  maxlen -= l;
388  opt += l;
389 
390  if (maxlen < length) {
391  coap_log(LOG_DEBUG, "coap_opt_encode: option too large for buffer\n");
392  return 0;
393  }
394 
395  if (val) /* better be safe here */
396  memcpy(opt, val, length);
397 
398  return l + length;
399 }
400 
401 #define LONG_MASK ((1 << COAP_OPT_FILTER_LONG) - 1)
402 #define SHORT_MASK \
403  (~LONG_MASK & ((1 << (COAP_OPT_FILTER_LONG + COAP_OPT_FILTER_SHORT)) - 1))
404 
407 is_long_option(coap_option_num_t number) { return number > 255; }
408 
411 
431 static int
433  coap_option_num_t number,
434  enum filter_op_t op) {
435  size_t lindex = 0;
436  coap_opt_filter_t *of = filter;
437  uint16_t nr, mask = 0;
438 
439  if (is_long_option(number)) {
440  mask = LONG_MASK;
441 
442  for (nr = 1; lindex < COAP_OPT_FILTER_LONG; nr <<= 1, lindex++) {
443 
444  if (((of->mask & nr) > 0) && (of->long_opts[lindex] == number)) {
445  if (op == FILTER_CLEAR) {
446  of->mask &= ~nr;
447  }
448 
449  return 1;
450  }
451  }
452  } else {
453  mask = SHORT_MASK;
454 
455  for (nr = 1 << COAP_OPT_FILTER_LONG; lindex < COAP_OPT_FILTER_SHORT;
456  nr <<= 1, lindex++) {
457 
458  if (((of->mask & nr) > 0) && (of->short_opts[lindex] == (number & 0xff))) {
459  if (op == FILTER_CLEAR) {
460  of->mask &= ~nr;
461  }
462 
463  return 1;
464  }
465  }
466  }
467 
468  /* number was not found, so there is nothing to do if op is CLEAR or GET */
469  if ((op == FILTER_CLEAR) || (op == FILTER_GET)) {
470  return 0;
471  }
472 
473  /* handle FILTER_SET: */
474 
475  lindex = coap_fls(~of->mask & mask);
476  if (!lindex) {
477  return 0;
478  }
479 
480  if (is_long_option(number)) {
481  of->long_opts[lindex - 1] = number;
482  } else {
483  of->short_opts[lindex - COAP_OPT_FILTER_LONG - 1] = (uint8_t)number;
484  }
485 
486  of->mask |= 1 << (lindex - 1);
487 
488  return 1;
489 }
490 
491 void
493  memset(filter, 0, sizeof(coap_opt_filter_t));
494 }
495 
496 int
498  return coap_option_filter_op(filter, option, FILTER_SET);
499 }
500 
501 int
503  return coap_option_filter_op(filter, option, FILTER_CLEAR);
504 }
505 
506 int
508  return coap_option_filter_op(filter, option, FILTER_GET);
509 }
510 
512 coap_new_optlist(uint16_t number,
513  size_t length,
514  const uint8_t *data
515 ) {
516  coap_optlist_t *node;
517 
518 #ifdef WITH_LWIP
519  if (length > MEMP_LEN_COAPOPTLIST) {
521  "coap_new_optlist: size too large (%zu > MEMP_LEN_COAPOPTLIST)\n",
522  length);
523  return NULL;
524  }
525 #endif /* WITH_LWIP */
526  node = coap_malloc_type(COAP_OPTLIST, sizeof(coap_optlist_t) + length);
527 
528  if (node) {
529  memset(node, 0, (sizeof(coap_optlist_t) + length));
530  node->number = number;
531  node->length = length;
532  node->data = (uint8_t *)&node[1];
533  memcpy(node->data, data, length);
534  } else {
535  coap_log(LOG_WARNING, "coap_new_optlist: malloc failure\n");
536  }
537 
538  return node;
539 }
540 
541 static int
542 order_opts(void *a, void *b) {
543  coap_optlist_t *o1 = (coap_optlist_t *)a;
544  coap_optlist_t *o2 = (coap_optlist_t *)b;
545 
546  if (!a || !b)
547  return a < b ? -1 : 1;
548 
549  return (int)(o1->number - o2->number);
550 }
551 
552 int
554  coap_optlist_t *opt;
555 
556  if (options && *options) {
557  if (pdu->data) {
559  "coap_add_optlist_pdu: PDU already contains data\n");
560  return 0;
561  }
562  /* sort options for delta encoding */
563  LL_SORT((*options), order_opts);
564 
565  LL_FOREACH((*options), opt) {
566  coap_add_option_internal(pdu, opt->number, opt->length, opt->data);
567  }
568  return 1;
569  }
570  return 0;
571 }
572 
573 int
575  if (!node) {
576  coap_log(LOG_DEBUG, "optlist not provided\n");
577  } else {
578  /* must append at the list end to avoid re-ordering of
579  * options during sort */
580  LL_APPEND((*head), node);
581  }
582 
583  return node != NULL;
584 }
585 
586 static int
588  if (node) {
590  }
591  return 1;
592 }
593 
594 void
596  coap_optlist_t *elt, *tmp;
597 
598  if (!queue)
599  return;
600 
601  LL_FOREACH_SAFE(queue, elt, tmp) {
603  }
604 }
605 
Pulls together all the internal only header files.
size_t coap_opt_parse(const coap_opt_t *opt, size_t length, coap_option_t *result)
Parses the option pointed to by opt into result.
Definition: coap_option.c:41
filter_op_t
Operation specifiers for coap_filter_op().
Definition: coap_option.c:410
@ FILTER_CLEAR
Definition: coap_option.c:410
@ FILTER_GET
Definition: coap_option.c:410
@ FILTER_SET
Definition: coap_option.c:410
#define ADVANCE_OPT_CHECK(o, e, step)
Definition: coap_option.c:34
#define SHORT_MASK
Definition: coap_option.c:402
size_t coap_opt_size(const coap_opt_t *opt)
Returns the size of the given option, taking into account a possible option jump.
Definition: coap_option.c:287
COAP_STATIC_INLINE int opt_finished(coap_opt_iterator_t *oi)
Definition: coap_option.c:140
static int coap_internal_delete(coap_optlist_t *node)
Definition: coap_option.c:587
#define ADVANCE_OPT(o, e, step)
Definition: coap_option.c:22
static int coap_option_filter_op(coap_opt_filter_t *filter, coap_option_num_t number, enum filter_op_t op)
Applies op on filter with respect to number.
Definition: coap_option.c:432
COAP_STATIC_INLINE int is_long_option(coap_option_num_t number)
Returns true iff number denotes an option number larger than 255.
Definition: coap_option.c:407
static int order_opts(void *a, void *b)
Definition: coap_option.c:542
#define LONG_MASK
Definition: coap_option.c:401
uint16_t coap_option_num_t
Definition: coap_option.h:20
uint8_t coap_opt_t
Use byte-oriented access methods here because sliding a complex struct coap_opt_t over the data buffe...
Definition: coap_option.h:26
int coap_fls(unsigned int i)
Definition: encode.c:20
#define LOG_DEBUG
Definition: coap_debug.h:81
#define LOG_CRIT
Definition: coap_debug.h:66
#define LOG_WARNING
Definition: coap_debug.h:72
#define coap_log(level,...)
Logging function.
Definition: coap_debug.h:165
coap_optlist_t * coap_new_optlist(uint16_t number, size_t length, const uint8_t *data)
Create a new optlist entry.
Definition: coap_option.c:512
coap_opt_t * coap_option_next(coap_opt_iterator_t *oi)
Updates the iterator oi to point to the next option.
Definition: coap_option.c:152
size_t coap_opt_encode(coap_opt_t *opt, size_t maxlen, uint16_t delta, const uint8_t *val, size_t length)
Encodes option with given delta into opt.
Definition: coap_option.c:375
uint32_t coap_opt_length(const coap_opt_t *opt)
Returns the length of the given option.
Definition: coap_option.c:215
void coap_delete_optlist(coap_optlist_t *queue)
Removes all entries from the optlist_chain, freeing off their memory usage.
Definition: coap_option.c:595
size_t coap_opt_encode_size(uint16_t delta, size_t length)
Compute storage bytes needed for an option with given delta and length.
Definition: coap_option.c:354
coap_opt_t * coap_check_option(const coap_pdu_t *pdu, coap_option_num_t number, coap_opt_iterator_t *oi)
Retrieves the first option of number number from pdu.
Definition: coap_option.c:202
#define COAP_OPT_FILTER_SHORT
The number of option types below 256 that can be stored in an option filter.
Definition: coap_option.h:78
int coap_option_filter_unset(coap_opt_filter_t *filter, coap_option_num_t option)
Clears the corresponding entry for number in filter.
Definition: coap_option.c:502
int coap_add_optlist_pdu(coap_pdu_t *pdu, coap_optlist_t **options)
The current optlist of optlist_chain is first sorted (as per RFC7272 ordering requirements) and then ...
Definition: coap_option.c:553
#define COAP_OPT_FILTER_LONG
The number of option types above 255 that can be stored in an option filter.
Definition: coap_option.h:86
void coap_option_filter_clear(coap_opt_filter_t *filter)
Clears filter filter.
Definition: coap_option.c:492
const uint8_t * coap_opt_value(const coap_opt_t *opt)
Returns a pointer to the value of the given option.
Definition: coap_option.c:252
coap_opt_iterator_t * coap_option_iterator_init(const coap_pdu_t *pdu, coap_opt_iterator_t *oi, const coap_opt_filter_t *filter)
Initializes the given option iterator oi to point to the beginning of the pdu's option list.
Definition: coap_option.c:116
int coap_insert_optlist(coap_optlist_t **head, coap_optlist_t *node)
Adds optlist to the given optlist_chain.
Definition: coap_option.c:574
int coap_option_filter_get(coap_opt_filter_t *filter, coap_option_num_t option)
Checks if number is contained in filter.
Definition: coap_option.c:507
int coap_option_filter_set(coap_opt_filter_t *filter, coap_option_num_t option)
Sets the corresponding entry for number in filter.
Definition: coap_option.c:497
size_t coap_opt_setheader(coap_opt_t *opt, size_t maxlen, uint16_t delta, size_t length)
Encodes the given delta and length values into opt.
Definition: coap_option.c:295
#define COAP_PAYLOAD_START
size_t coap_add_option_internal(coap_pdu_t *pdu, coap_option_num_t number, size_t len, const uint8_t *data)
Adds option of given number to pdu that is passed as first parameter.
Definition: pdu.c:615
#define COAP_STATIC_INLINE
Definition: libcoap.h:45
void * coap_malloc_type(coap_memory_tag_t type, size_t size)
Allocates a chunk of size bytes and returns a pointer to the newly allocated memory.
@ COAP_OPTLIST
Definition: mem.h:52
void coap_free_type(coap_memory_tag_t type, void *p)
Releases the memory that was allocated by coap_malloc_type().
uint8_t short_opts[COAP_OPT_FILTER_SHORT]
Definition: coap_option.h:104
uint16_t long_opts[COAP_OPT_FILTER_LONG]
Definition: coap_option.h:103
Iterator to run through PDU options.
Definition: coap_option.h:171
coap_opt_t * next_option
pointer to the unparsed next option
Definition: coap_option.h:176
coap_opt_filter_t filter
option filter
Definition: coap_option.h:177
unsigned int bad
iterator object is ok if not set
Definition: coap_option.h:174
size_t length
remaining length of PDU
Definition: coap_option.h:172
unsigned int filtered
denotes whether or not filter is used
Definition: coap_option.h:175
coap_option_num_t number
decoded option number
Definition: coap_option.h:173
Representation of CoAP options.
Definition: coap_option.h:32
const uint8_t * value
Definition: coap_option.h:35
uint16_t delta
Definition: coap_option.h:33
size_t length
Definition: coap_option.h:34
Representation of chained list of CoAP options to install.
Definition: coap_option.h:328
uint16_t number
the option number (no delta coding)
Definition: coap_option.h:330
size_t length
the option value length
Definition: coap_option.h:331
uint8_t * data
the option data
Definition: coap_option.h:332
structure for CoAP PDUs
uint8_t * token
first byte of token, if any, or options
uint8_t token_length
length of Token
uint8_t * data
first byte of payload, if any
size_t used_size
used bytes of storage for token, options and payload