1 /* resource.c -- generic resource handling
2  *
3  * Copyright (C) 2010--2015 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 "coap_config.h"
10 #include "coap.h"
11 #include "debug.h"
12 #include "mem.h"
13 #include "net.h"
14 #include "resource.h"
15 #include "subscribe.h"
16 #include "utlist.h"
17 
18 #ifdef WITH_LWIP
19 /* mem.h is only needed for the string free calls for
20  * COAP_ATTR_FLAGS_RELEASE_NAME / COAP_ATTR_FLAGS_RELEASE_VALUE /
21  * COAP_RESOURCE_FLAGS_RELEASE_URI. not sure what those lines should actually
22  * do on lwip. */
23 
24 #include <lwip/memp.h>
25 
26 #define COAP_MALLOC_TYPE(Type) \
27   ((coap_##Type##_t *)memp_malloc(MEMP_COAP_##Type))
28 #define COAP_FREE_TYPE(Type, Object) memp_free(MEMP_COAP_##Type, Object)
29 
30 #endif
31 
32 #ifdef WITH_POSIX
33 
34 #define COAP_MALLOC_TYPE(Type) \
35   ((coap_##Type##_t *)coap_malloc(sizeof(coap_##Type##_t)))
36 #define COAP_FREE_TYPE(Type, Object) coap_free(Object)
37 
38 #endif /* WITH_POSIX */
39 #ifdef WITH_CONTIKI
40 #include "memb.h"
41 
42 #define COAP_MALLOC_TYPE(Type) \
43   ((coap_##Type##_t *)memb_alloc(&(Type##_storage)))
44 #define COAP_FREE_TYPE(Type, Object) memb_free(&(Type##_storage), (Object))
45 
46 MEMB(subscription_storage, coap_subscription_t, COAP_MAX_SUBSCRIBERS);
47 
48 void
coap_resources_init()49 coap_resources_init() {
50   memb_init(&subscription_storage);
51 }
52 
53 static inline coap_subscription_t *
coap_malloc_subscription()54 coap_malloc_subscription() {
55   return memb_alloc(&subscription_storage);
56 }
57 
58 static inline void
coap_free_subscription(coap_subscription_t * subscription)59 coap_free_subscription(coap_subscription_t *subscription) {
60   memb_free(&subscription_storage, subscription);
61 }
62 
63 #endif /* WITH_CONTIKI */
64 
65 #define min(a,b) ((a) < (b) ? (a) : (b))
66 
67 /* Helper functions for conditional output of character sequences into
68  * a given buffer. The first Offset characters are skipped.
69  */
70 
71 /**
72  * Adds Char to Buf if Offset is zero. Otherwise, Char is not written
73  * and Offset is decremented.
74  */
75 #define PRINT_WITH_OFFSET(Buf,Offset,Char)		\
76   if ((Offset) == 0) {					\
77     (*(Buf)++) = (Char);				\
78   } else {						\
79     (Offset)--;						\
80   }							\
81 
82 /**
83  * Adds Char to Buf if Offset is zero and Buf is less than Bufend.
84  */
85 #define PRINT_COND_WITH_OFFSET(Buf,Bufend,Offset,Char,Result) {		\
86     if ((Buf) < (Bufend)) {						\
87       PRINT_WITH_OFFSET(Buf,Offset,Char);				\
88     }									\
89     (Result)++;								\
90   }
91 
92 /**
93  * Copies at most Length characters of Str to Buf. The first Offset
94  * characters are skipped. Output may be truncated to Bufend - Buf
95  * characters.
96  */
97 #define COPY_COND_WITH_OFFSET(Buf,Bufend,Offset,Str,Length,Result) {	\
98     size_t i;								\
99     for (i = 0; i < (Length); i++) {					\
100       PRINT_COND_WITH_OFFSET((Buf), (Bufend), (Offset), (Str)[i], (Result)); \
101     }									\
102   }
103 
104 static int
match(const str * text,const str * pattern,int match_prefix,int match_substring)105 match(const str *text, const str *pattern, int match_prefix, int match_substring) {
106   assert(text); assert(pattern);
107 
108   if (text->length < pattern->length)
109     return 0;
110 
111   if (match_substring) {
112     unsigned char *next_token = text->s;
113     size_t remaining_length = text->length;
114     while (remaining_length) {
115       size_t token_length;
116       unsigned char *token = next_token;
117       next_token = memchr(token, ' ', remaining_length);
118 
119       if (next_token) {
120         token_length = next_token - token;
121         remaining_length -= (token_length + 1);
122         next_token++;
123       } else {
124         token_length = remaining_length;
125         remaining_length = 0;
126       }
127 
128       if ((match_prefix || pattern->length == token_length) &&
129             memcmp(token, pattern->s, pattern->length) == 0)
130         return 1;
131     }
132     return 0;
133   }
134 
135   return (match_prefix || pattern->length == text->length) &&
136     memcmp(text->s, pattern->s, pattern->length) == 0;
137 }
138 
139 /**
140  * Prints the names of all known resources to @p buf. This function
141  * sets @p buflen to the number of bytes actually written and returns
142  * @c 1 on succes. On error, the value in @p buflen is undefined and
143  * the return value will be @c 0.
144  *
145  * @param context The context with the resource map.
146  * @param buf     The buffer to write the result.
147  * @param buflen  Must be initialized to the maximum length of @p buf and will be
148  *                set to the length of the well-known response on return.
149  * @param offset  The offset in bytes where the output shall start and is
150  *                shifted accordingly with the characters that have been
151  *                processed. This parameter is used to support the block
152  *                option.
153  * @param query_filter A filter query according to <a href="http://tools.ietf.org/html/draft-ietf-core-link-format-11#section-4.1">Link Format</a>
154  *
155  * @return COAP_PRINT_STATUS_ERROR on error. Otherwise, the lower 28 bits are
156  *         set to the number of bytes that have actually been written to
157  *         @p buf. COAP_PRINT_STATUS_TRUNC is set when the output has been
158  *         truncated.
159  */
160 #if defined(__GNUC__) && defined(WITHOUT_QUERY_FILTER)
161 coap_print_status_t
coap_print_wellknown(coap_context_t * context,unsigned char * buf,size_t * buflen,size_t offset,coap_opt_t * query_filter)162 coap_print_wellknown(coap_context_t *context, unsigned char *buf, size_t *buflen,
163 		size_t offset,
164 		coap_opt_t *query_filter __attribute__ ((unused))) {
165 #else /* not a GCC */
166 coap_print_status_t
167 coap_print_wellknown(coap_context_t *context, unsigned char *buf, size_t *buflen,
168 		size_t offset, coap_opt_t *query_filter) {
169 #endif /* GCC */
170   unsigned char *p = buf;
171   const unsigned char *bufend = buf + *buflen;
172   size_t left, written = 0;
173   coap_print_status_t result;
174   const size_t old_offset = offset;
175   int subsequent_resource = 0;
176 #ifndef WITHOUT_QUERY_FILTER
177   str resource_param = { 0, NULL }, query_pattern = { 0, NULL };
178   int flags = 0; /* MATCH_SUBSTRING, MATCH_PREFIX, MATCH_URI */
179 #define MATCH_URI       0x01
180 #define MATCH_PREFIX    0x02
181 #define MATCH_SUBSTRING 0x04
182   static const str _rt_attributes[] = {
183     {2, (unsigned char *)"rt"},
184     {2, (unsigned char *)"if"},
185     {3, (unsigned char *)"rel"},
186     {0, NULL}};
187 #endif /* WITHOUT_QUERY_FILTER */
188 
189 #ifndef WITHOUT_QUERY_FILTER
190   /* split query filter, if any */
191   if (query_filter) {
192     resource_param.s = COAP_OPT_VALUE(query_filter);
193     while (resource_param.length < COAP_OPT_LENGTH(query_filter)
194 	   && resource_param.s[resource_param.length] != '=')
195       resource_param.length++;
196 
197     if (resource_param.length < COAP_OPT_LENGTH(query_filter)) {
198       const str *rt_attributes;
199       if (resource_param.length == 4 &&
200 	  memcmp(resource_param.s, "href", 4) == 0)
201 	flags |= MATCH_URI;
202 
203       for (rt_attributes = _rt_attributes; rt_attributes->s; rt_attributes++) {
204         if (resource_param.length == rt_attributes->length &&
205             memcmp(resource_param.s, rt_attributes->s, rt_attributes->length) == 0) {
206           flags |= MATCH_SUBSTRING;
207           break;
208         }
209       }
210 
211       /* rest is query-pattern */
212       query_pattern.s =
213 	COAP_OPT_VALUE(query_filter) + resource_param.length + 1;
214 
215       assert((resource_param.length + 1) <= COAP_OPT_LENGTH(query_filter));
216       query_pattern.length =
217 	COAP_OPT_LENGTH(query_filter) - (resource_param.length + 1);
218 
219      if ((query_pattern.s[0] == '/') && ((flags & MATCH_URI) == MATCH_URI)) {
220        query_pattern.s++;
221        query_pattern.length--;
222       }
223 
224       if (query_pattern.length &&
225 	  query_pattern.s[query_pattern.length-1] == '*') {
226 	query_pattern.length--;
227 	flags |= MATCH_PREFIX;
228       }
229     }
230   }
231 #endif /* WITHOUT_QUERY_FILTER */
232 
233   RESOURCES_ITER(context->resources, r) {
234 
235 #ifndef WITHOUT_QUERY_FILTER
236     if (resource_param.length) { /* there is a query filter */
237 
238       if (flags & MATCH_URI) {	/* match resource URI */
239 	if (!match(&r->uri, &query_pattern, (flags & MATCH_PREFIX) != 0, (flags & MATCH_SUBSTRING) != 0))
240 	  continue;
241       } else {			/* match attribute */
242 	coap_attr_t *attr;
243         str unquoted_val;
244 	attr = coap_find_attr(r, resource_param.s, resource_param.length);
245         if (!attr) continue;
246         if (attr->value.s[0] == '"') {          /* if attribute has a quoted value, remove double quotes */
247           unquoted_val.length = attr->value.length - 2;
248           unquoted_val.s = attr->value.s + 1;
249         } else {
250           unquoted_val = attr->value;
251         }
252 	if (!(match(&unquoted_val, &query_pattern,
253                     (flags & MATCH_PREFIX) != 0,
254                     (flags & MATCH_SUBSTRING) != 0)))
255 	  continue;
256       }
257     }
258 #endif /* WITHOUT_QUERY_FILTER */
259 
260     if (!subsequent_resource) {	/* this is the first resource  */
261       subsequent_resource = 1;
262     } else {
263       PRINT_COND_WITH_OFFSET(p, bufend, offset, ',', written);
264     }
265 
266     left = bufend - p; /* calculate available space */
267     result = coap_print_link(r, p, &left, &offset);
268 
269     if (result & COAP_PRINT_STATUS_ERROR) {
270       break;
271     }
272 
273     /* coap_print_link() returns the number of characters that
274      * where actually written to p. Now advance to its end. */
275     p += COAP_PRINT_OUTPUT_LENGTH(result);
276     written += left;
277   }
278 
279   *buflen = written;
280   result = p - buf;
281   if (result + old_offset - offset < *buflen) {
282     result |= COAP_PRINT_STATUS_TRUNC;
283   }
284   return result;
285 }
286 
287 coap_resource_t *
288 coap_resource_init(const unsigned char *uri, size_t len, int flags) {
289   coap_resource_t *r;
290 
291 #ifdef WITH_LWIP
292   r = (coap_resource_t *)memp_malloc(MEMP_COAP_RESOURCE);
293 #endif
294 #ifndef WITH_LWIP
295   r = (coap_resource_t *)coap_malloc_type(COAP_RESOURCE, sizeof(coap_resource_t));
296 #endif
297   if (r) {
298     memset(r, 0, sizeof(coap_resource_t));
299 
300     r->uri.s = (unsigned char *)uri;
301     r->uri.length = len;
302 
303     coap_hash_path(r->uri.s, r->uri.length, r->key);
304 
305     r->flags = flags;
306   } else {
307     debug("coap_resource_init: no memory left\n");
308   }
309 
310   return r;
311 }
312 
313 coap_attr_t *
314 coap_add_attr(coap_resource_t *resource,
315 	      const unsigned char *name, size_t nlen,
316 	      const unsigned char *val, size_t vlen,
317               int flags) {
318   coap_attr_t *attr;
319 
320   if (!resource || !name)
321     return NULL;
322 
323 #ifdef WITH_LWIP
324   attr = (coap_attr_t *)memp_malloc(MEMP_COAP_RESOURCEATTR);
325 #endif
326 #ifndef WITH_LWIP
327   attr = (coap_attr_t *)coap_malloc_type(COAP_RESOURCEATTR, sizeof(coap_attr_t));
328 #endif
329 
330   if (attr) {
331     attr->name.length = nlen;
332     attr->value.length = val ? vlen : 0;
333 
334     attr->name.s = (unsigned char *)name;
335     attr->value.s = (unsigned char *)val;
336 
337     attr->flags = flags;
338 
339     /* add attribute to resource list */
340     LL_PREPEND(resource->link_attr, attr);
341   } else {
342     debug("coap_add_attr: no memory left\n");
343   }
344 
345   return attr;
346 }
347 
348 coap_attr_t *
349 coap_find_attr(coap_resource_t *resource,
350 	       const unsigned char *name, size_t nlen) {
351   coap_attr_t *attr;
352 
353   if (!resource || !name)
354     return NULL;
355 
356   LL_FOREACH(resource->link_attr, attr) {
357     if (attr->name.length == nlen &&
358 	memcmp(attr->name.s, name, nlen) == 0)
359       return attr;
360   }
361 
362   return NULL;
363 }
364 
365 void
366 coap_delete_attr(coap_attr_t *attr) {
367   if (!attr)
368     return;
369   if (attr->flags & COAP_ATTR_FLAGS_RELEASE_NAME)
370     coap_free(attr->name.s);
371   if (attr->flags & COAP_ATTR_FLAGS_RELEASE_VALUE)
372     coap_free(attr->value.s);
373 
374 #ifdef WITH_LWIP
375   memp_free(MEMP_COAP_RESOURCEATTR, attr);
376 #endif
377 #ifndef WITH_LWIP
378   coap_free_type(COAP_RESOURCEATTR, attr);
379 #endif
380 }
381 
382 void
383 coap_hash_request_uri(const coap_pdu_t *request, coap_key_t key) {
384   coap_opt_iterator_t opt_iter;
385   coap_opt_filter_t filter;
386   coap_opt_t *option;
387 
388   memset(key, 0, sizeof(coap_key_t));
389 
390   coap_option_filter_clear(filter);
391   coap_option_setb(filter, COAP_OPTION_URI_PATH);
392 
393   coap_option_iterator_init((coap_pdu_t *)request, &opt_iter, filter);
394   while ((option = coap_option_next(&opt_iter)))
395     coap_hash(COAP_OPT_VALUE(option), COAP_OPT_LENGTH(option), key);
396 }
397 
398 void
399 coap_add_resource(coap_context_t *context, coap_resource_t *resource) {
400   RESOURCES_ADD(context->resources, resource);
401 }
402 
403 static void
404 coap_free_resource(coap_resource_t *resource) {
405   coap_attr_t *attr, *tmp;
406   coap_subscription_t *obs, *otmp;
407 
408   assert(resource);
409 
410   /* delete registered attributes */
411   LL_FOREACH_SAFE(resource->link_attr, attr, tmp) coap_delete_attr(attr);
412 
413   if (resource->flags & COAP_RESOURCE_FLAGS_RELEASE_URI)
414     coap_free(resource->uri.s);
415 
416   /* free all elements from resource->subscribers */
417   LL_FOREACH_SAFE(resource->subscribers, obs, otmp) COAP_FREE_TYPE(subscription, obs);
418 
419 #ifdef WITH_LWIP
420   memp_free(MEMP_COAP_RESOURCE, resource);
421 #endif
422 #ifndef WITH_LWIP
423   coap_free_type(COAP_RESOURCE, resource);
424 #endif /* WITH_CONTIKI */
425 }
426 
427 int
428 coap_delete_resource(coap_context_t *context, coap_key_t key) {
429   coap_resource_t *resource;
430 
431   if (!context)
432     return 0;
433 
434   resource = coap_get_resource_from_key(context, key);
435 
436   if (!resource)
437     return 0;
438 
439   /* remove resource from list */
440   RESOURCES_DELETE(context->resources, resource);
441 
442   /* and free its allocated memory */
443   coap_free_resource(resource);
444 
445   return 1;
446 }
447 
448 void
449 coap_delete_all_resources(coap_context_t *context) {
450   coap_resource_t *res;
451   coap_resource_t *rtmp;
452 
453   /* Cannot call RESOURCES_ITER because coap_free_resource() releases
454    * the allocated storage. */
455 
456 #ifdef COAP_RESOURCES_NOHASH
457   LL_FOREACH_SAFE(context->resources, res, rtmp) {
458 #else
459   HASH_ITER(hh, context->resources, res, rtmp) {
460 #endif
461     coap_free_resource(res);
462   }
463 
464   context->resources = NULL;
465 }
466 
467 coap_resource_t *
468 coap_get_resource_from_key(coap_context_t *context, coap_key_t key) {
469   coap_resource_t *result;
470 
471   RESOURCES_FIND(context->resources, key, result);
472 
473   return result;
474 }
475 
476 coap_print_status_t
477 coap_print_link(const coap_resource_t *resource,
478 		unsigned char *buf, size_t *len, size_t *offset) {
479   unsigned char *p = buf;
480   const unsigned char *bufend = buf + *len;
481   coap_attr_t *attr;
482   coap_print_status_t result = 0;
483   const size_t old_offset = *offset;
484 
485   *len = 0;
486   PRINT_COND_WITH_OFFSET(p, bufend, *offset, '<', *len);
487   PRINT_COND_WITH_OFFSET(p, bufend, *offset, '/', *len);
488 
489   COPY_COND_WITH_OFFSET(p, bufend, *offset,
490 			resource->uri.s, resource->uri.length, *len);
491 
492   PRINT_COND_WITH_OFFSET(p, bufend, *offset, '>', *len);
493 
494   LL_FOREACH(resource->link_attr, attr) {
495 
496     PRINT_COND_WITH_OFFSET(p, bufend, *offset, ';', *len);
497 
498     COPY_COND_WITH_OFFSET(p, bufend, *offset,
499 			  attr->name.s, attr->name.length, *len);
500 
501     if (attr->value.s) {
502       PRINT_COND_WITH_OFFSET(p, bufend, *offset, '=', *len);
503 
504       COPY_COND_WITH_OFFSET(p, bufend, *offset,
505 			    attr->value.s, attr->value.length, *len);
506     }
507 
508   }
509   if (resource->observable) {
510     COPY_COND_WITH_OFFSET(p, bufend, *offset, ";obs", 4, *len);
511   }
512 
513   result = p - buf;
514   if (result + old_offset - *offset < *len) {
515     result |= COAP_PRINT_STATUS_TRUNC;
516   }
517 
518   return result;
519 }
520 
521 #ifndef WITHOUT_OBSERVE
522 coap_subscription_t *
523 coap_find_observer(coap_resource_t *resource, const coap_address_t *peer,
524 		     const str *token) {
525   coap_subscription_t *s;
526 
527   assert(resource);
528   assert(peer);
529 
530   LL_FOREACH(resource->subscribers, s) {
531     if (coap_address_equals(&s->subscriber, peer)
532 	&& (!token || (token->length == s->token_length
533 		       && memcmp(token->s, s->token, token->length) == 0)))
534       return s;
535   }
536 
537   return NULL;
538 }
539 
540 coap_subscription_t *
541 coap_add_observer(coap_resource_t *resource,
542 		  const coap_endpoint_t *local_interface,
543 		  const coap_address_t *observer,
544 		  const str *token) {
545   coap_subscription_t *s;
546 
547   assert(observer);
548 
549   /* Check if there is already a subscription for this peer. */
550   s = coap_find_observer(resource, observer, token);
551 
552   /* We are done if subscription was found. */
553   if (s)
554     return s;
555 
556   /* s points to a different subscription, so we have to create
557    * another one. */
558   s = COAP_MALLOC_TYPE(subscription);
559 
560   if (!s)
561     return NULL;
562 
563   coap_subscription_init(s);
564   s->local_if = *local_interface;
565   memcpy(&s->subscriber, observer, sizeof(coap_address_t));
566 
567   if (token && token->length) {
568     s->token_length = token->length;
569     memcpy(s->token, token->s, min(s->token_length, 8));
570   }
571 
572   /* add subscriber to resource */
573   LL_PREPEND(resource->subscribers, s);
574 
575   return s;
576 }
577 
578 void
579 coap_touch_observer(coap_context_t *context, const coap_address_t *observer,
580 		    const str *token) {
581   coap_subscription_t *s;
582 
583   RESOURCES_ITER(context->resources, r) {
584     s = coap_find_observer(r, observer, token);
585     if (s) {
586       s->fail_cnt = 0;
587     }
588   }
589 }
590 
591 int
592 coap_delete_observer(coap_resource_t *resource, const coap_address_t *observer,
593 		     const str *token) {
594   coap_subscription_t *s;
595 
596   s = coap_find_observer(resource, observer, token);
597 
598   if (resource->subscribers && s) {
599     LL_DELETE(resource->subscribers, s);
600 
601     COAP_FREE_TYPE(subscription,s);
602   }
603 
604   return s != NULL;
605 }
606 
607 static void
608 coap_notify_observers(coap_context_t *context, coap_resource_t *r) {
609   coap_method_handler_t h;
610   coap_subscription_t *obs;
611   str token;
612   coap_pdu_t *response;
613 
614   if (r->observable && (r->dirty || r->partiallydirty)) {
615     r->partiallydirty = 0;
616 
617     /* retrieve GET handler, prepare response */
618     h = r->handler[COAP_REQUEST_GET - 1];
619     assert(h);		/* we do not allow subscriptions if no
620 			 * GET handler is defined */
621 
622     LL_FOREACH(r->subscribers, obs) {
623       if (r->dirty == 0 && obs->dirty == 0)
624         /* running this resource due to partiallydirty, but this observation's notification was already enqueued */
625         continue;
626 
627       coap_tid_t tid = COAP_INVALID_TID;
628       obs->dirty = 0;
629       /* initialize response */
630       response = coap_pdu_init(COAP_MESSAGE_CON, 0, 0, COAP_MAX_PDU_SIZE);
631       if (!response) {
632         obs->dirty = 1;
633         r->partiallydirty = 1;
634 	debug("coap_check_notify: pdu init failed, resource stays partially dirty\n");
635 	continue;
636       }
637 
638       if (!coap_add_token(response, obs->token_length, obs->token)) {
639         obs->dirty = 1;
640         r->partiallydirty = 1;
641 	debug("coap_check_notify: cannot add token, resource stays partially dirty\n");
642 	coap_delete_pdu(response);
643 	continue;
644       }
645 
646       token.length = obs->token_length;
647       token.s = obs->token;
648 
649       response->hdr->id = coap_new_message_id(context);
650       if ((r->flags & COAP_RESOURCE_FLAGS_NOTIFY_CON) == 0
651 	  && obs->non_cnt < COAP_OBS_MAX_NON) {
652 	response->hdr->type = COAP_MESSAGE_NON;
653       } else {
654 	response->hdr->type = COAP_MESSAGE_CON;
655       }
656       /* fill with observer-specific data */
657       h(context, r, &obs->local_if, &obs->subscriber, NULL, &token, response);
658 
659       /* TODO: do not send response and remove observer when
660        *  COAP_RESPONSE_CLASS(response->hdr->code) > 2
661        */
662       if (response->hdr->type == COAP_MESSAGE_CON) {
663 	tid = coap_send_confirmed(context, &obs->local_if, &obs->subscriber, response);
664 	obs->non_cnt = 0;
665       } else {
666 	tid = coap_send(context, &obs->local_if, &obs->subscriber, response);
667 	obs->non_cnt++;
668       }
669 
670       if (COAP_INVALID_TID == tid || response->hdr->type != COAP_MESSAGE_CON)
671 	coap_delete_pdu(response);
672       if (COAP_INVALID_TID == tid)
673       {
674 	debug("coap_check_notify: sending failed, resource stays partially dirty\n");
675         obs->dirty = 1;
676         r->partiallydirty = 1;
677       }
678 
679     }
680 
681     /* Increment value for next Observe use. */
682     context->observe++;
683   }
684   r->dirty = 0;
685 }
686 
687 void
688 coap_check_notify(coap_context_t *context) {
689 
690   RESOURCES_ITER(context->resources, r) {
691     coap_notify_observers(context, r);
692   }
693 }
694 
695 /**
696  * Checks the failure counter for (peer, token) and removes peer from
697  * the list of observers for the given resource when COAP_OBS_MAX_FAIL
698  * is reached.
699  *
700  * @param context  The CoAP context to use
701  * @param resource The resource to check for (peer, token)
702  * @param peer     The observer's address
703  * @param token    The token that has been used for subscription.
704  */
705 static void
706 coap_remove_failed_observers(coap_context_t *context,
707 			     coap_resource_t *resource,
708 			     const coap_address_t *peer,
709 			     const str *token) {
710   coap_subscription_t *obs, *otmp;
711 
712   LL_FOREACH_SAFE(resource->subscribers, obs, otmp) {
713     if (coap_address_equals(peer, &obs->subscriber) &&
714 	token->length == obs->token_length &&
715 	memcmp(token->s, obs->token, token->length) == 0) {
716 
717       /* count failed notifies and remove when
718        * COAP_MAX_FAILED_NOTIFY is reached */
719       if (obs->fail_cnt < COAP_OBS_MAX_FAIL)
720 	obs->fail_cnt++;
721       else {
722 	LL_DELETE(resource->subscribers, obs);
723 	obs->fail_cnt = 0;
724 
725 #ifndef NDEBUG
726 	if (LOG_DEBUG <= coap_get_log_level()) {
727 #ifndef INET6_ADDRSTRLEN
728 #define INET6_ADDRSTRLEN 40
729 #endif
730 	  unsigned char addr[INET6_ADDRSTRLEN+8];
731 
732 	  if (coap_print_addr(&obs->subscriber, addr, INET6_ADDRSTRLEN+8))
733 	    debug("** removed observer %s\n", addr);
734 	}
735 #endif
736 	coap_cancel_all_messages(context, &obs->subscriber,
737 				 obs->token, obs->token_length);
738 
739 	COAP_FREE_TYPE(subscription, obs);
740       }
741     }
742     break;			/* break loop if observer was found */
743   }
744 }
745 
746 void
747 coap_handle_failed_notify(coap_context_t *context,
748 			  const coap_address_t *peer,
749 			  const str *token) {
750 
751   RESOURCES_ITER(context->resources, r) {
752 	coap_remove_failed_observers(context, r, peer, token);
753   }
754 }
755 #endif /* WITHOUT_NOTIFY */
756