libcoap  4.0.3
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
resource.c
Go to the documentation of this file.
1 /* resource.c -- generic resource handling
2  *
3  * Copyright (C) 2010--2013 Olaf Bergmann <bergmann@tzi.org>
4  *
5  * This file is part of the CoAP library libcoap. Please see
6  * README for terms of use.
7  */
8 
9 #include "config.h"
10 #include "net.h"
11 #include "debug.h"
12 #include "resource.h"
13 #include "subscribe.h"
14 
15 #ifndef WITH_CONTIKI
16 #include "utlist.h"
17 #include "mem.h"
18 
19 #define COAP_MALLOC_TYPE(Type) \
20  ((coap_##Type##_t *)coap_malloc(sizeof(coap_##Type##_t)))
21 #define COAP_FREE_TYPE(Type, Object) coap_free(Object)
22 
23 #else /* WITH_CONTIKI */
24 #include "memb.h"
25 
26 MEMB(resource_storage, coap_resource_t, COAP_MAX_RESOURCES);
27 MEMB(attribute_storage, coap_attr_t, COAP_MAX_ATTRIBUTES);
28 MEMB(subscription_storage, coap_subscription_t, COAP_MAX_SUBSCRIBERS);
29 
30 void
31 coap_resources_init() {
32  memb_init(&resource_storage);
33  memb_init(&attribute_storage);
34  memb_init(&subscription_storage);
35 }
36 
37 static inline coap_subscription_t *
38 coap_malloc_subscription() {
39  return memb_alloc(&subscription_storage);
40 }
41 
42 static inline void
43 coap_free_subscription(coap_subscription_t *subscription) {
44  memb_free(&subscription_storage, subscription);
45 }
46 #endif /* WITH_CONTIKI */
47 
48 #define min(a,b) ((a) < (b) ? (a) : (b))
49 
50 int
51 match(const str *text, const str *pattern, int match_prefix, int match_substring) {
52  assert(text); assert(pattern);
53 
54  if (text->length < pattern->length)
55  return 0;
56 
57  if (match_substring) {
58  unsigned char *next_token = text->s;
59  size_t remaining_length = text->length;
60  while (remaining_length) {
61  size_t token_length;
62  unsigned char *token = next_token;
63  next_token = memchr(token, ' ', remaining_length);
64 
65  if (next_token) {
66  token_length = next_token - token;
67  remaining_length -= (token_length + 1);
68  next_token++;
69  } else {
70  token_length = remaining_length;
71  remaining_length = 0;
72  }
73 
74  if ((match_prefix || pattern->length == token_length) &&
75  memcmp(token, pattern->s, pattern->length) == 0)
76  return 1;
77  }
78  return 0;
79  }
80 
81  return (match_prefix || pattern->length == text->length) &&
82  memcmp(text->s, pattern->s, pattern->length) == 0;
83 }
84 
99 #if defined(__GNUC__) && defined(WITHOUT_QUERY_FILTER)
100 int
101 print_wellknown(coap_context_t *context, unsigned char *buf, size_t *buflen,
102  coap_opt_t *query_filter __attribute__ ((unused))) {
103 #else /* not a GCC */
104 int
105 print_wellknown(coap_context_t *context, unsigned char *buf, size_t *buflen,
106  coap_opt_t *query_filter) {
107 #endif /* GCC */
108  coap_resource_t *r;
109  unsigned char *p = buf;
110  size_t left, written = 0;
111  coap_resource_t *tmp;
112 #ifndef WITHOUT_QUERY_FILTER
113  str resource_param = { 0, NULL }, query_pattern = { 0, NULL };
114  int flags = 0; /* MATCH_SUBSTRING, MATCH_PREFIX, MATCH_URI */
115 #define MATCH_URI 0x01
116 #define MATCH_PREFIX 0x02
117 #define MATCH_SUBSTRING 0x04
118  static const str _rt_attributes[] = {
119  {2, (unsigned char *)"rt"},
120  {2, (unsigned char *)"if"},
121  {3, (unsigned char *)"rel"},
122  {0, NULL}};
123 #endif /* WITHOUT_QUERY_FILTER */
124 
125 #ifdef WITH_CONTIKI
126  int i;
127 #endif /* WITH_CONTIKI */
128 
129 #ifndef WITHOUT_QUERY_FILTER
130  /* split query filter, if any */
131  if (query_filter) {
132  resource_param.s = COAP_OPT_VALUE(query_filter);
133  while (resource_param.length < COAP_OPT_LENGTH(query_filter)
134  && resource_param.s[resource_param.length] != '=')
135  resource_param.length++;
136 
137  if (resource_param.length < COAP_OPT_LENGTH(query_filter)) {
138  const str *rt_attributes;
139  if (resource_param.length == 4 &&
140  memcmp(resource_param.s, "href", 4) == 0)
141  flags |= MATCH_URI;
142 
143  for (rt_attributes = _rt_attributes; rt_attributes->s; rt_attributes++) {
144  if (resource_param.length == rt_attributes->length &&
145  memcmp(resource_param.s, rt_attributes->s, rt_attributes->length) == 0) {
146  flags |= MATCH_SUBSTRING;
147  break;
148  }
149  }
150 
151  /* rest is query-pattern */
152  query_pattern.s =
153  COAP_OPT_VALUE(query_filter) + resource_param.length + 1;
154 
155  assert((resource_param.length + 1) <= COAP_OPT_LENGTH(query_filter));
156  query_pattern.length =
157  COAP_OPT_LENGTH(query_filter) - (resource_param.length + 1);
158 
159  if ((query_pattern.s[0] == '/') && ((flags & MATCH_URI) == MATCH_URI)) {
160  query_pattern.s++;
161  query_pattern.length--;
162  }
163 
164  if (query_pattern.length &&
165  query_pattern.s[query_pattern.length-1] == '*') {
166  query_pattern.length--;
167  flags |= MATCH_PREFIX;
168  }
169  }
170  }
171 #endif /* WITHOUT_QUERY_FILTER */
172 
173 #ifndef WITH_CONTIKI
174 
175  HASH_ITER(hh, context->resources, r, tmp) {
176 #else /* WITH_CONTIKI */
177  r = (coap_resource_t *)resource_storage.mem;
178  for (i = 0; i < resource_storage.num; ++i, ++r) {
179  if (!resource_storage.count[i])
180  continue;
181 #endif /* WITH_CONTIKI */
182 
183 #ifndef WITHOUT_QUERY_FILTER
184  if (resource_param.length) { /* there is a query filter */
185 
186  if (flags & MATCH_URI) { /* match resource URI */
187  if (!match(&r->uri, &query_pattern, (flags & MATCH_PREFIX) != 0, (flags & MATCH_SUBSTRING) != 0))
188  continue;
189  } else { /* match attribute */
190  coap_attr_t *attr;
191  str unquoted_val;
192  attr = coap_find_attr(r, resource_param.s, resource_param.length);
193  if (!attr) continue;
194  if (attr->value.s[0] == '"') { /* if attribute has a quoted value, remove double quotes */
195  unquoted_val.length = attr->value.length - 2;
196  unquoted_val.s = attr->value.s + 1;
197  } else {
198  unquoted_val = attr->value;
199  }
200  if (!(match(&unquoted_val, &query_pattern,
201  (flags & MATCH_PREFIX) != 0,
202  (flags & MATCH_SUBSTRING) != 0)))
203  continue;
204  }
205  }
206 #endif /* WITHOUT_QUERY_FILTER */
207 
208  left = *buflen - written;
209 
210  if (left < *buflen) { /* this is not the first resource */
211  *p++ = ',';
212  --left;
213  }
214 
215  if (!coap_print_link(r, p, &left))
216  return 0;
217 
218  p += left;
219  written += left;
220  }
221  *buflen = p - buf;
222  return 1;
223 }
224 
226 coap_resource_init(const unsigned char *uri, size_t len, int flags) {
227  coap_resource_t *r;
228 
229 #ifndef WITH_CONTIKI
231 #else /* WITH_CONTIKI */
232  r = (coap_resource_t *)memb_alloc(&resource_storage);
233 
234 #endif /* WITH_CONTIKI */
235  if (r) {
236  memset(r, 0, sizeof(coap_resource_t));
237 
238 #ifdef WITH_CONTIKI
239  LIST_STRUCT_INIT(r, link_attr);
240 #endif /* WITH_CONTIKI */
241  LIST_STRUCT_INIT(r, subscribers);
242 
243  r->uri.s = (unsigned char *)uri;
244  r->uri.length = len;
245 
246  coap_hash_path(r->uri.s, r->uri.length, r->key);
247 
248  r->flags = flags;
249  } else {
250  debug("coap_resource_init: no memory left\n");
251  }
252 
253  return r;
254 }
255 
256 coap_attr_t *
258  const unsigned char *name, size_t nlen,
259  const unsigned char *val, size_t vlen,
260  int flags) {
261  coap_attr_t *attr;
262 
263  if (!resource || !name)
264  return NULL;
265 
266 #ifndef WITH_CONTIKI
267  attr = (coap_attr_t *)coap_malloc(sizeof(coap_attr_t));
268 #else /* WITH_CONTIKI */
269  attr = (coap_attr_t *)memb_alloc(&attribute_storage);
270 #endif /* WITH_CONTIKI */
271 
272  if (attr) {
273  attr->name.length = nlen;
274  attr->value.length = val ? vlen : 0;
275 
276  attr->name.s = (unsigned char *)name;
277  attr->value.s = (unsigned char *)val;
278 
279  attr->flags = flags;
280 
281  /* add attribute to resource list */
282 #ifndef WITH_CONTIKI
283  LL_PREPEND(resource->link_attr, attr);
284 #else /* WITH_CONTIKI */
285  list_add(resource->link_attr, attr);
286 #endif /* WITH_CONTIKI */
287  } else {
288  debug("coap_add_attr: no memory left\n");
289  }
290 
291  return attr;
292 }
293 
294 coap_attr_t *
296  const unsigned char *name, size_t nlen) {
297  coap_attr_t *attr;
298 
299  if (!resource || !name)
300  return NULL;
301 
302 #ifndef WITH_CONTIKI
303  LL_FOREACH(resource->link_attr, attr) {
304 #else /* WITH_CONTIKI */
305  for (attr = list_head(resource->link_attr); attr;
306  attr = list_item_next(attr)) {
307 #endif /* WITH_CONTIKI */
308  if (attr->name.length == nlen &&
309  memcmp(attr->name.s, name, nlen) == 0)
310  return attr;
311  }
312 
313  return NULL;
314 }
315 
316 void
318  if (!attr)
319  return;
321  coap_free(attr->name.s);
323  coap_free(attr->value.s);
324  coap_free(attr);
325 }
326 
327 void
329  coap_opt_iterator_t opt_iter;
330  coap_opt_filter_t filter;
331  coap_opt_t *option;
332 
333  memset(key, 0, sizeof(coap_key_t));
334 
335  coap_option_filter_clear(filter);
337 
338  coap_option_iterator_init((coap_pdu_t *)request, &opt_iter, filter);
339  while ((option = coap_option_next(&opt_iter)))
340  coap_hash(COAP_OPT_VALUE(option), COAP_OPT_LENGTH(option), key);
341 }
342 
343 void
345 #ifndef WITH_CONTIKI
346  HASH_ADD(hh, context->resources, key, sizeof(coap_key_t), resource);
347 #endif /* WITH_CONTIKI */
348 }
349 
350 int
353  coap_attr_t *attr, *tmp;
354 #ifdef WITH_CONTIKI
355  coap_subscription_t *obs;
356 #endif
357 
358  if (!context)
359  return 0;
360 
361  resource = coap_get_resource_from_key(context, key);
362 
363  if (!resource)
364  return 0;
365 
366 #ifndef WITH_CONTIKI
367  HASH_DELETE(hh, context->resources, resource);
368 
369  /* delete registered attributes */
370  LL_FOREACH_SAFE(resource->link_attr, attr, tmp) coap_delete_attr(attr);
371 
372  if (resource->flags & COAP_RESOURCE_FLAGS_RELEASE_URI)
373  coap_free(resource->uri.s);
374 
375  coap_free(resource);
376 #else /* WITH_CONTIKI */
377  /* delete registered attributes */
378  while ( (attr = list_pop(resource->link_attr)) )
379  memb_free(&attribute_storage, attr);
380 
381  /* delete subscribers */
382  while ( (obs = list_pop(resource->subscribers)) ) {
383  /* FIXME: notify observer that its subscription has been removed */
384  memb_free(&subscription_storage, obs);
385  }
386 
387  memb_free(&resource_storage, resource);
388 #endif /* WITH_CONTIKI */
389 
390  return 1;
391 }
392 
395 #ifndef WITH_CONTIKI
397  HASH_FIND(hh, context->resources, key, sizeof(coap_key_t), resource);
398 
399  return resource;
400 #else /* WITH_CONTIKI */
401  int i;
402  coap_resource_t *ptr2;
403 
404  /* the search function is basically taken from memb.c */
405  ptr2 = (coap_resource_t *)resource_storage.mem;
406  for (i = 0; i < resource_storage.num; ++i) {
407  if (resource_storage.count[i] &&
408  (memcmp(ptr2->key, key, sizeof(coap_key_t)) == 0))
409  return (coap_resource_t *)ptr2;
410  ++ptr2;
411  }
412 
413  return NULL;
414 #endif /* WITH_CONTIKI */
415 }
416 
417 int
419  unsigned char *buf, size_t *len) {
420  unsigned char *p = buf;
421  coap_attr_t *attr;
422 
423  size_t written = resource->uri.length + 3;
424  if (*len < written)
425  return 0;
426 
427  *p++ = '<';
428  *p++ = '/';
429  memcpy(p, resource->uri.s, resource->uri.length);
430  p += resource->uri.length;
431  *p++ = '>';
432 
433 #ifndef WITH_CONTIKI
434  LL_FOREACH(resource->link_attr, attr) {
435 #else /* WITH_CONTIKI */
436  for (attr = list_head(resource->link_attr); attr;
437  attr = list_item_next(attr)) {
438 #endif /* WITH_CONTIKI */
439  written += attr->name.length + 1;
440  if (*len < written)
441  return 0;
442 
443  *p++ = ';';
444  memcpy(p, attr->name.s, attr->name.length);
445  p += attr->name.length;
446 
447  if (attr->value.s) {
448  written += attr->value.length + 1;
449  if (*len < written)
450  return 0;
451 
452  *p++ = '=';
453  memcpy(p, attr->value.s, attr->value.length);
454  p += attr->value.length;
455  }
456  }
457  if (resource->observable && written + 4 <= *len) {
458  memcpy(p, ";obs", 4);
459  written += 4;
460  }
461 
462  *len = written;
463  return 1;
464 }
465 
466 #ifndef WITHOUT_OBSERVE
469  const str *token) {
471 
472  assert(resource);
473  assert(peer);
474 
475  for (s = list_head(resource->subscribers); s; s = list_item_next(s)) {
476  if (coap_address_equals(&s->subscriber, peer)
477  && (!token || (token->length == s->token_length
478  && memcmp(token->s, s->token, token->length) == 0)))
479  return s;
480  }
481 
482  return NULL;
483 }
484 
487  const coap_address_t *observer,
488  const str *token) {
490 
491  assert(observer);
492 
493  /* Check if there is already a subscription for this peer. */
494  s = coap_find_observer(resource, observer, token);
495 
496  /* We are done if subscription was found. */
497  if (s)
498  return s;
499 
500  /* s points to a different subscription, so we have to create
501  * another one. */
502  s = COAP_MALLOC_TYPE(subscription);
503 
504  if (!s)
505  return NULL;
506 
508  memcpy(&s->subscriber, observer, sizeof(coap_address_t));
509 
510  if (token && token->length) {
511  s->token_length = token->length;
512  memcpy(s->token, token->s, min(s->token_length, 8));
513  }
514 
515  /* add subscriber to resource */
516  list_add(resource->subscribers, s);
517 
518  return s;
519 }
520 
521 void
523  const str *token) {
524  coap_resource_t *r, *tmp;
526 
527 #ifndef WITH_CONTIKI
528  HASH_ITER(hh, context->resources, r, tmp) {
529  s = coap_find_observer(r, observer, token);
530  if (s) {
531  s->fail_cnt = 0;
532  }
533  }
534 #else /* WITH_CONTIKI */
535  r = (coap_resource_t *)resource_storage.mem;
536  for (i = 0; i < resource_storage.num; ++i, ++r) {
537  if (resource_storage.count[i]) {
538  s = coap_find_observer(r, observer, token);
539  if (s) {
540  s->fail_cnt = 0;
541  }
542  }
543  }
544 #endif /* WITH_CONTIKI */
545 }
546 
547 void
549  const str *token) {
551 
552  s = coap_find_observer(resource, observer, token);
553 
554  if (s) {
555  list_remove(resource->subscribers, s);
556 
557  COAP_FREE_TYPE(subscription,s);
558  }
559 }
560 
561 static void
564  coap_subscription_t *obs;
565  str token;
566  coap_pdu_t *response;
567 
568  /* retrieve GET handler, prepare response */
569  h = r->handler[COAP_REQUEST_GET - 1];
570  assert(h); /* we do not allow subscriptions if no
571  * GET handler is defined */
572 
573  if (r->observable && r->dirty) {
574  for (obs = list_head(r->subscribers); obs; obs = list_item_next(obs)) {
576  /* initialize response */
578  if (!response) {
579  debug("coap_check_notify: pdu init failed\n");
580  continue;
581  }
582 
583  if (!coap_add_token(response, obs->token_length, obs->token)) {
584  debug("coap_check_notify: cannot add token\n");
585  coap_delete_pdu(response);
586  continue;
587  }
588 
589  token.length = obs->token_length;
590  token.s = obs->token;
591 
592  response->hdr->id = coap_new_message_id(context);
593  if (obs->non && obs->non_cnt < COAP_OBS_MAX_NON) {
594  response->hdr->type = COAP_MESSAGE_NON;
595  } else {
596  response->hdr->type = COAP_MESSAGE_CON;
597  }
598  /* fill with observer-specific data */
599  h(context, r, &obs->subscriber, NULL, &token, response);
600 
601  if (response->hdr->type == COAP_MESSAGE_CON) {
602  tid = coap_send_confirmed(context, &obs->subscriber, response);
603  obs->non_cnt = 0;
604  } else {
605  tid = coap_send(context, &obs->subscriber, response);
606  obs->non_cnt++;
607  }
608 
609  if (COAP_INVALID_TID == tid || response->hdr->type != COAP_MESSAGE_CON)
610  coap_delete_pdu(response);
611  }
612 
613  /* Increment value for next Observe use. */
614  context->observe++;
615  }
616  r->dirty = 0;
617 }
618 
619 void
621  coap_resource_t *r;
622 #ifndef WITH_CONTIKI
623  coap_resource_t *tmp;
624 
625  HASH_ITER(hh, context->resources, r, tmp) {
626  coap_notify_observers(context, r);
627  }
628 #else /* WITH_CONTIKI */
629  int i;
630 
631  r = (coap_resource_t *)resource_storage.mem;
632  for (i = 0; i < resource_storage.num; ++i, ++r) {
633  if (resource_storage.count[i]) {
634  coap_notify_observers(context, r);
635  }
636  }
637 #endif /* WITH_CONTIKI */
638 }
639 
650 static void
652  coap_resource_t *resource,
653  const coap_address_t *peer,
654  const str *token) {
655  coap_subscription_t *obs;
656 
657  for (obs = list_head(resource->subscribers); obs;
658  obs = list_item_next(obs)) {
659  if (coap_address_equals(peer, &obs->subscriber) &&
660  token->length == obs->token_length &&
661  memcmp(token->s, obs->token, token->length) == 0) {
662 
663  /* count failed notifies and remove when
664  * COAP_MAX_FAILED_NOTIFY is reached */
665  if (obs->fail_cnt < COAP_OBS_MAX_FAIL)
666  obs->fail_cnt++;
667  else {
668  list_remove(resource->subscribers, obs);
669  obs->fail_cnt = 0;
670 
671 #ifndef NDEBUG
672  if (LOG_DEBUG <= coap_get_log_level()) {
673 #ifndef INET6_ADDRSTRLEN
674 #define INET6_ADDRSTRLEN 40
675 #endif
676  unsigned char addr[INET6_ADDRSTRLEN+8];
677 
678  if (coap_print_addr(&obs->subscriber, addr, INET6_ADDRSTRLEN+8))
679  debug("** removed observer %s\n", addr);
680  }
681 #endif
682  coap_cancel_all_messages(context, &obs->subscriber,
683  obs->token, obs->token_length);
684 
685  COAP_FREE_TYPE(subscription, obs);
686  }
687  }
688  break; /* break loop if observer was found */
689  }
690 }
691 
692 void
694  const coap_address_t *peer,
695  const str *token) {
696  coap_resource_t *r;
697 
698 #ifndef WITH_CONTIKI
699  coap_resource_t *tmp;
700 
701  HASH_ITER(hh, context->resources, r, tmp) {
702  coap_remove_failed_observers(context, r, peer, token);
703  }
704 #else /* WITH_CONTIKI */
705  int i;
706 
707  r = (coap_resource_t *)resource_storage.mem;
708  for (i = 0; i < resource_storage.num; ++i, ++r) {
709  if (resource_storage.count[i]) {
710  coap_remove_failed_observers(context, r, peer, token);
711  }
712  }
713 #endif /* WITH_CONTIKI */
714 }
715 #endif /* WITHOUT_NOTIFY */