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