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