9a52fb4d8a73e78b944663066c978203b9739202
[platform/upstream/iotivity.git] / resource / csdk / connectivity / lib / libcoap-4.1.1 / option.c
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 #include "config.h"
11
12 #if defined(HAVE_ASSERT_H) && !defined(assert)
13 # include <assert.h>
14 #endif
15
16 #include <stdio.h>
17 #include <string.h>
18
19 #include "option.h"
20 #include "debug.h"
21 #include "pdu.h"
22
23 coap_opt_t *
24 options_start(coap_pdu_t *pdu)
25 {
26
27     if (pdu && pdu->hdr
28             && (pdu->hdr->token + pdu->hdr->token_length < (unsigned char *) pdu->hdr + pdu->length))
29     {
30
31         coap_opt_t *opt = pdu->hdr->token + pdu->hdr->token_length;
32         return (*opt == COAP_PAYLOAD_START) ? NULL : opt;
33
34     }
35     else
36         return NULL;
37 }
38
39 size_t coap_opt_parse(const coap_opt_t *opt, size_t length, coap_option_t *result)
40 {
41
42     const coap_opt_t *opt_start = opt; /* store where parsing starts  */
43     assert(opt);
44     assert(result);
45
46 #define ADVANCE_OPT(o,e,step) if ((e) < step) {         \
47     debug("cannot advance opt past end\n");         \
48     return 0;                           \
49   } else {                          \
50     (e) -= step;                        \
51     (o) = ((unsigned char *)(o)) + step;            \
52   }
53
54     if (length < 1)
55         return 0;
56
57     result->delta = (*opt & 0xf0) >> 4;
58     result->length = *opt & 0x0f;
59
60     switch (result->delta)
61     {
62         case 15:
63             if (*opt != COAP_PAYLOAD_START)
64                 debug("ignored reserved option delta 15\n");
65             return 0;
66         case 14:
67             /* Handle two-byte value: First, the MSB + 269 is stored as delta value.
68              * After that, the option pointer is advanced to the LSB which is handled
69              * just like case delta == 13. */
70             ADVANCE_OPT(opt, length, 1);
71             result->delta = ((*opt & 0xff) << 8) + 269;
72             if (result->delta < 269)
73             {
74                 debug("delta too large\n");
75                 return 0;
76             }
77             /* fall through */
78         case 13:
79             ADVANCE_OPT(opt, length, 1);
80             result->delta += *opt & 0xff;
81             break;
82
83         default:
84             ;
85     }
86
87     switch (result->length)
88     {
89         case 15:
90             debug("found reserved option length 15\n");
91             return 0;
92         case 14:
93             /* Handle two-byte value: First, the MSB + 269 is stored as delta value.
94              * After that, the option pointer is advanced to the LSB which is handled
95              * just like case delta == 13. */
96             ADVANCE_OPT(opt, length, 1);
97             result->length = ((*opt & 0xff) << 8) + 269;
98             /* fall through */
99         case 13:
100             ADVANCE_OPT(opt, length, 1);
101             result->length += *opt & 0xff;
102             break;
103
104         default:
105             ;
106     }
107
108     ADVANCE_OPT(opt, length, 1);
109     /* opt now points to value, if present */
110
111     result->value = (unsigned char *) opt;
112     if (length < result->length)
113     {
114         debug("invalid option length\n");
115         return 0;
116     }
117     //printf("[COAP] Option - coap_opt_parse result : %s, %d\n", result->value, result->length);
118
119 #undef ADVANCE_OPT
120
121     return (opt + result->length) - opt_start;
122 }
123
124 coap_opt_iterator_t *
125 coap_option_iterator_init(coap_pdu_t *pdu, coap_opt_iterator_t *oi, const coap_opt_filter_t filter)
126 {
127     assert(pdu);
128     assert(pdu->hdr);
129     assert(oi);
130
131     memset(oi, 0, sizeof(coap_opt_iterator_t));
132
133     oi->next_option = (unsigned char *) pdu->hdr + sizeof(coap_hdr_t) + pdu->hdr->token_length;
134     if ((unsigned char *) pdu->hdr + pdu->length <= oi->next_option)
135     {
136         oi->bad = 1;
137         return NULL;
138     }
139
140     assert((sizeof(coap_hdr_t) + pdu->hdr->token_length) <= pdu->length);
141
142     oi->length = pdu->length - (sizeof(coap_hdr_t) + pdu->hdr->token_length);
143
144     if (filter)
145     {
146         memcpy(oi->filter, filter, sizeof(coap_opt_filter_t));
147         oi->filtered = 1;
148     }
149     return oi;
150 }
151
152 static inline int opt_finished(coap_opt_iterator_t *oi)
153 {
154     assert(oi);
155
156     if (oi->bad || oi->length == 0 || !oi->next_option || *oi->next_option == COAP_PAYLOAD_START)
157     {
158         oi->bad = 1;
159     }
160
161     return oi->bad;
162 }
163
164 coap_opt_t *
165 coap_option_next(coap_opt_iterator_t *oi)
166 {
167     coap_option_t option;
168     coap_opt_t *current_opt = NULL;
169     size_t optsize;
170     int b; /* to store result of coap_option_getb() */
171
172     assert(oi);
173
174     if (opt_finished(oi))
175         return NULL;
176
177     while (1)
178     {
179         /* oi->option always points to the next option to deliver; as
180          * opt_finished() filters out any bad conditions, we can assume that
181          * oi->option is valid. */
182         current_opt = oi->next_option;
183
184         /* Advance internal pointer to next option, skipping any option that
185          * is not included in oi->filter. */
186         optsize = coap_opt_parse(oi->next_option, oi->length, &option);
187         if (optsize)
188         {
189             assert(optsize <= oi->length);
190
191             oi->next_option += optsize;
192             oi->length -= optsize;
193
194             oi->type += option.delta;
195         }
196         else
197         { /* current option is malformed */
198             oi->bad = 1;
199             return NULL;
200         }
201
202         /* Exit the while loop when:
203          *   - no filtering is done at all
204          *   - the filter matches for the current option
205          *   - the filter is too small for the current option number
206          */
207         if (!oi->filtered || (b = coap_option_getb(oi->filter, oi->type)) > 0)
208             break;
209         else if (b < 0)
210         { /* filter too small, cannot proceed */
211             oi->bad = 1;
212             return NULL;
213         }
214     }
215
216     return current_opt;
217 }
218
219 coap_opt_t *
220 coap_check_option(coap_pdu_t *pdu, unsigned char type, coap_opt_iterator_t *oi)
221 {
222     coap_opt_filter_t f;
223
224     coap_option_filter_clear(f);
225     coap_option_setb(f, type);
226
227     coap_option_iterator_init(pdu, oi, f);
228
229     return coap_option_next(oi);
230 }
231
232 unsigned short coap_opt_delta(const coap_opt_t *opt)
233 {
234     unsigned short n;
235
236     n = (*opt++ & 0xf0) >> 4;
237
238     switch (n)
239     {
240         case 15: /* error */
241             warn("coap_opt_delta: illegal option delta\n");
242
243             /* This case usually should not happen, hence we do not have a
244              * proper way to indicate an error. */
245             return 0;
246         case 14:
247             /* Handle two-byte value: First, the MSB + 269 is stored as delta value.
248              * After that, the option pointer is advanced to the LSB which is handled
249              * just like case delta == 13. */
250             n = ((*opt++ & 0xff) << 8) + 269;
251             /* fall through */
252         case 13:
253             n += *opt & 0xff;
254             break;
255         default: /* n already contains the actual delta value */
256             ;
257     }
258
259     return n;
260 }
261
262 unsigned short coap_opt_length(const coap_opt_t *opt)
263 {
264     unsigned short length;
265
266     length = *opt & 0x0f;
267     switch (*opt & 0xf0)
268     {
269         case 0xf0:
270             debug("illegal option delta\n");
271             return 0;
272         case 0xe0:
273             ++opt;
274             /* fall through to skip another byte */
275         case 0xd0:
276             ++opt;
277             /* fall through to skip another byte */
278         default:
279             ++opt;
280     }
281
282     switch (length)
283     {
284         case 0x0f:
285             debug("illegal option length\n");
286             return 0;
287         case 0x0e:
288             length = (*opt++ << 8) + 269;
289             /* fall through */
290         case 0x0d:
291             length += *opt++;
292             break;
293         default:
294             ;
295     }
296     return length;
297 }
298
299 unsigned char *
300 coap_opt_value(coap_opt_t *opt)
301 {
302     size_t ofs = 1;
303
304     switch (*opt & 0xf0)
305     {
306         case 0xf0:
307             debug("illegal option delta\n");
308             return 0;
309         case 0xe0:
310             ++ofs;
311             /* fall through */
312         case 0xd0:
313             ++ofs;
314             break;
315         default:
316             ;
317     }
318
319     switch (*opt & 0x0f)
320     {
321         case 0x0f:
322             debug("illegal option length\n");
323             return 0;
324         case 0x0e:
325             ++ofs;
326             /* fall through */
327         case 0x0d:
328             ++ofs;
329             break;
330         default:
331             ;
332     }
333
334     return (unsigned char *) opt + ofs;
335 }
336
337 size_t coap_opt_size(const coap_opt_t *opt)
338 {
339     coap_option_t option;
340
341     /* we must assume that opt is encoded correctly */
342     return coap_opt_parse(opt, (size_t) - 1, &option);
343 }
344
345 size_t coap_opt_setheader(coap_opt_t *opt, size_t maxlen, unsigned short delta, size_t length)
346 {
347     size_t skip = 0;
348
349     assert(opt);
350
351     if (maxlen == 0) /* need at least one byte */
352         return 0;
353
354     if (delta < 13)
355     {
356         opt[0] = delta << 4;
357     }
358     else if (delta < 270)
359     {
360         if (maxlen < 2)
361         {
362             debug("insufficient space to encode option delta %d", delta);
363             return 0;
364         }
365
366         opt[0] = 0xd0;
367         opt[++skip] = delta - 13;
368     }
369     else
370     {
371         if (maxlen < 3)
372         {
373             debug("insufficient space to encode option delta %d", delta);
374             return 0;
375         }
376
377         opt[0] = 0xe0;
378         opt[++skip] = ((delta - 269) >> 8) & 0xff;
379         opt[++skip] = (delta - 269) & 0xff;
380     }
381
382     if (length < 13)
383     {
384         opt[0] |= length & 0x0f;
385     }
386     else if (length < 270)
387     {
388         if (maxlen < skip + 1)
389         {
390             debug("insufficient space to encode option length %d", length);
391             return 0;
392         }
393
394         opt[0] |= 0x0d;
395         opt[++skip] = length - 13;
396     }
397     else
398     {
399         if (maxlen < skip + 2)
400         {
401             debug("insufficient space to encode option delta %d", delta);
402             return 0;
403         }
404
405         opt[0] |= 0x0e;
406         opt[++skip] = ((length - 269) >> 8) & 0xff;
407         opt[++skip] = (length - 269) & 0xff;
408     }
409
410     return skip + 1;
411 }
412
413 size_t coap_opt_encode(coap_opt_t *opt, size_t maxlen, unsigned short delta,
414         const unsigned char *val, size_t length)
415 {
416     size_t l = 1;
417
418     l = coap_opt_setheader(opt, maxlen, delta, length);
419     assert(l <= maxlen);
420
421     if (!l)
422     {
423         debug("coap_opt_encode: cannot set option header\n");
424         return 0;
425     }
426
427     maxlen -= l;
428     opt += l;
429
430     if (maxlen < length)
431     {
432         debug("coap_opt_encode: option too large for buffer\n");
433         return 0;
434     }
435
436     if (val) /* better be safe here */
437         memcpy(opt, val, length);
438
439     return l + length;
440 }
441
442 static coap_option_def_t coap_option_def[] = {
443     { COAP_OPTION_IF_MATCH,       'o',  0,   8 },
444     { COAP_OPTION_URI_HOST,       's',  1, 255 },
445     { COAP_OPTION_ETAG,           'o',  1,   8 },
446     { COAP_OPTION_IF_NONE_MATCH,  'e',  0,   0 },
447     { COAP_OPTION_URI_PORT,       'u',  0,   2 },
448     { COAP_OPTION_LOCATION_PATH,  's',  0, 255 },
449     { COAP_OPTION_URI_PATH,       's',  0, 255 },
450     { COAP_OPTION_CONTENT_TYPE,   'u',  0,   2 },
451     { COAP_OPTION_MAXAGE,         'u',  0,   4 },
452     { COAP_OPTION_URI_QUERY,      's',  1, 255 },
453     { COAP_OPTION_ACCEPT,         'u',  0,   2 },
454     { COAP_OPTION_LOCATION_QUERY, 's',  0, 255 },
455     { COAP_OPTION_PROXY_URI,      's',  1,1034 },
456     { COAP_OPTION_PROXY_SCHEME,   's',  1, 255 },
457     { COAP_OPTION_SIZE1,          'u',  0,   4 },
458     { COAP_OPTION_SIZE2,          'u',  0,   4 },
459     { COAP_OPTION_OBSERVE,        'u',  0,   3 },
460     { COAP_OPTION_BLOCK2,         'u',  0,   3 },
461     { COAP_OPTION_BLOCK1,         'u',  0,   3 },
462 };
463
464
465 coap_option_def_t* coap_opt_def(unsigned short key)
466 {
467     int i;
468
469     if (COAP_MAX_OPT < key)
470     {
471         return NULL;
472     }
473     for (i = 0; i < (int)(sizeof(coap_option_def)/sizeof(coap_option_def_t)); i++)
474     {
475         if (key == coap_option_def[i].key)
476             return &(coap_option_def[i]);
477     }
478     debug("coap_opt_def: add key:[%d] to coap_is_var_bytes", key);
479     return NULL;
480 }