1 /* resource.c -- generic resource handling
2  *
3  * Copyright (C) 2010--2021 Olaf Bergmann <bergmann@tzi.org>
4  *
5  * SPDX-License-Identifier: BSD-2-Clause
6  *
7  * This file is part of the CoAP library libcoap. Please see
8  * README for terms of use.
9  */
10 
11 #include "coap3/coap_internal.h"
12 
13 #include <stdio.h>
14 #include <errno.h>
15 
16 #ifdef COAP_EPOLL_SUPPORT
17 #include <sys/epoll.h>
18 #include <sys/timerfd.h>
19 #endif /* COAP_EPOLL_SUPPORT */
20 
21 #if defined(WITH_LWIP)
22 /* mem.h is only needed for the string free calls for
23  * COAP_ATTR_FLAGS_RELEASE_NAME / COAP_ATTR_FLAGS_RELEASE_VALUE /
24  * COAP_RESOURCE_FLAGS_RELEASE_URI. not sure what those lines should actually
25  * do on lwip. */
26 
27 #include <lwip/memp.h>
28 
29 #define COAP_MALLOC_TYPE(Type) \
30   ((coap_##Type##_t *)memp_malloc(MEMP_COAP_##Type))
31 #define COAP_FREE_TYPE(Type, Object) memp_free(MEMP_COAP_##Type, Object)
32 
33 #elif defined(WITH_CONTIKI)
34 #include "memb.h"
35 
36 #define COAP_MALLOC_TYPE(Type) \
37   ((coap_##Type##_t *)memb_alloc(&(Type##_storage)))
38 #define COAP_FREE_TYPE(Type, Object) memb_free(&(Type##_storage), (Object))
39 
40 MEMB(subscription_storage, coap_subscription_t, COAP_MAX_SUBSCRIBERS);
41 
42 void
coap_resources_init()43 coap_resources_init() {
44   memb_init(&subscription_storage);
45 }
46 
47 COAP_STATIC_INLINE coap_subscription_t *
coap_malloc_subscription()48 coap_malloc_subscription() {
49   return memb_alloc(&subscription_storage);
50 }
51 
52 COAP_STATIC_INLINE void
coap_free_subscription(coap_subscription_t * subscription)53 coap_free_subscription(coap_subscription_t *subscription) {
54   memb_free(&subscription_storage, subscription);
55 }
56 
57 #else
58 #define COAP_MALLOC_TYPE(Type) \
59   ((coap_##Type##_t *)coap_malloc(sizeof(coap_##Type##_t)))
60 #define COAP_FREE_TYPE(Type, Object) coap_free(Object)
61 #endif
62 
63 #define COAP_PRINT_STATUS_MAX (~COAP_PRINT_STATUS_MASK)
64 
65 #ifndef min
66 #define min(a,b) ((a) < (b) ? (a) : (b))
67 #endif
68 
69 /* Helper functions for conditional output of character sequences into
70  * a given buffer. The first Offset characters are skipped.
71  */
72 
73 /**
74  * Adds Char to Buf if Offset is zero. Otherwise, Char is not written
75  * and Offset is decremented.
76  */
77 #define PRINT_WITH_OFFSET(Buf,Offset,Char)                \
78   if ((Offset) == 0) {                                        \
79     (*(Buf)++) = (Char);                                \
80   } else {                                                \
81     (Offset)--;                                                \
82   }                                                        \
83 
84 /**
85  * Adds Char to Buf if Offset is zero and Buf is less than Bufend.
86  */
87 #define PRINT_COND_WITH_OFFSET(Buf,Bufend,Offset,Char,Result) {                \
88     if ((Buf) < (Bufend)) {                                                \
89       PRINT_WITH_OFFSET(Buf,Offset,Char);                                \
90     }                                                                        \
91     (Result)++;                                                                \
92   }
93 
94 /**
95  * Copies at most Length characters of Str to Buf. The first Offset
96  * characters are skipped. Output may be truncated to Bufend - Buf
97  * characters.
98  */
99 #define COPY_COND_WITH_OFFSET(Buf,Bufend,Offset,Str,Length,Result) {        \
100     size_t i;                                                                \
101     for (i = 0; i < (Length); i++) {                                        \
102       PRINT_COND_WITH_OFFSET((Buf), (Bufend), (Offset), (Str)[i], (Result)); \
103     }                                                                        \
104   }
105 
106 static int
match(const coap_str_const_t * text,const coap_str_const_t * pattern,int match_prefix,int match_substring)107 match(const coap_str_const_t *text, const coap_str_const_t *pattern, int match_prefix,
108   int match_substring
109 ) {
110   assert(text); assert(pattern);
111 
112   if (text->length < pattern->length)
113     return 0;
114 
115   if (match_substring) {
116     const uint8_t *next_token = text->s;
117     size_t remaining_length = text->length;
118     while (remaining_length) {
119       size_t token_length;
120       const uint8_t *token = next_token;
121       next_token = (unsigned char *)memchr(token, ' ', remaining_length);
122 
123       if (next_token) {
124         token_length = next_token - token;
125         remaining_length -= (token_length + 1);
126         next_token++;
127       } else {
128         token_length = remaining_length;
129         remaining_length = 0;
130       }
131 
132       if ((match_prefix || pattern->length == token_length) &&
133             memcmp(token, pattern->s, pattern->length) == 0)
134         return 1;
135     }
136     return 0;
137   }
138 
139   return (match_prefix || pattern->length == text->length) &&
140     memcmp(text->s, pattern->s, pattern->length) == 0;
141 }
142 
143 /**
144  * Prints the names of all known resources to @p buf. This function
145  * sets @p buflen to the number of bytes actually written and returns
146  * @c 1 on succes. On error, the value in @p buflen is undefined and
147  * the return value will be @c 0.
148  *
149  * @param context The context with the resource map.
150  * @param buf     The buffer to write the result.
151  * @param buflen  Must be initialized to the maximum length of @p buf and will be
152  *                set to the length of the well-known response on return.
153  * @param offset  The offset in bytes where the output shall start and is
154  *                shifted accordingly with the characters that have been
155  *                processed. This parameter is used to support the block
156  *                option.
157  * @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>
158  *
159  * @return COAP_PRINT_STATUS_ERROR on error. Otherwise, the lower 28 bits are
160  *         set to the number of bytes that have actually been written to
161  *         @p buf. COAP_PRINT_STATUS_TRUNC is set when the output has been
162  *         truncated.
163  */
164 #if defined(__GNUC__) && defined(WITHOUT_QUERY_FILTER)
165 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 COAP_UNUSED)166 coap_print_wellknown(coap_context_t *context, unsigned char *buf, size_t *buflen,
167                 size_t offset,
168                 coap_opt_t *query_filter COAP_UNUSED) {
169 #else /* not a GCC */
170 coap_print_status_t
171 coap_print_wellknown(coap_context_t *context, unsigned char *buf, size_t *buflen,
172                 size_t offset, coap_opt_t *query_filter) {
173 #endif /* GCC */
174   size_t output_length = 0;
175   unsigned char *p = buf;
176   const uint8_t *bufend = buf + *buflen;
177   size_t left, written = 0;
178   coap_print_status_t result;
179   const size_t old_offset = offset;
180   int subsequent_resource = 0;
181 #ifndef WITHOUT_QUERY_FILTER
182   coap_str_const_t resource_param = { 0, NULL }, query_pattern = { 0, NULL };
183   int flags = 0; /* MATCH_SUBSTRING, MATCH_PREFIX, MATCH_URI */
184 #define MATCH_URI       0x01
185 #define MATCH_PREFIX    0x02
186 #define MATCH_SUBSTRING 0x04
187   static const coap_str_const_t _rt_attributes[] = {
188     {2, (const uint8_t *)"rt"},
189     {2, (const uint8_t *)"if"},
190     {3, (const uint8_t *)"rel"},
191     {0, NULL}};
192 #endif /* WITHOUT_QUERY_FILTER */
193 
194 #ifndef WITHOUT_QUERY_FILTER
195   /* split query filter, if any */
196   if (query_filter) {
197     resource_param.s = coap_opt_value(query_filter);
198     while (resource_param.length < coap_opt_length(query_filter)
199            && resource_param.s[resource_param.length] != '=')
200       resource_param.length++;
201 
202     if (resource_param.length < coap_opt_length(query_filter)) {
203       const coap_str_const_t *rt_attributes;
204       if (resource_param.length == 4 &&
205           memcmp(resource_param.s, "href", 4) == 0)
206         flags |= MATCH_URI;
207 
208       for (rt_attributes = _rt_attributes; rt_attributes->s; rt_attributes++) {
209         if (resource_param.length == rt_attributes->length &&
210             memcmp(resource_param.s, rt_attributes->s, rt_attributes->length) == 0) {
211           flags |= MATCH_SUBSTRING;
212           break;
213         }
214       }
215 
216       /* rest is query-pattern */
217       query_pattern.s =
218         coap_opt_value(query_filter) + resource_param.length + 1;
219 
220       assert((resource_param.length + 1) <= coap_opt_length(query_filter));
221       query_pattern.length =
222         coap_opt_length(query_filter) - (resource_param.length + 1);
223 
224      if ((query_pattern.s[0] == '/') && ((flags & MATCH_URI) == MATCH_URI)) {
225        query_pattern.s++;
226        query_pattern.length--;
227       }
228 
229       if (query_pattern.length &&
230           query_pattern.s[query_pattern.length-1] == '*') {
231         query_pattern.length--;
232         flags |= MATCH_PREFIX;
233       }
234     }
235   }
236 #endif /* WITHOUT_QUERY_FILTER */
237 
238   RESOURCES_ITER(context->resources, r) {
239 
240 #ifndef WITHOUT_QUERY_FILTER
241     if (resource_param.length) { /* there is a query filter */
242 
243       if (flags & MATCH_URI) {        /* match resource URI */
244         if (!match(r->uri_path, &query_pattern, (flags & MATCH_PREFIX) != 0,
245             (flags & MATCH_SUBSTRING) != 0))
246           continue;
247       } else {                        /* match attribute */
248         coap_attr_t *attr;
249         coap_str_const_t unquoted_val;
250         attr = coap_find_attr(r, &resource_param);
251         if (!attr || !attr->value) continue;
252         unquoted_val = *attr->value;
253         if (attr->value->s[0] == '"') {          /* if attribute has a quoted value, remove double quotes */
254           unquoted_val.length -= 2;
255           unquoted_val.s += 1;
256         }
257         if (!(match(&unquoted_val, &query_pattern,
258                     (flags & MATCH_PREFIX) != 0,
259                     (flags & MATCH_SUBSTRING) != 0)))
260           continue;
261       }
262     }
263 #endif /* WITHOUT_QUERY_FILTER */
264 
265     if (!subsequent_resource) {        /* this is the first resource  */
266       subsequent_resource = 1;
267     } else {
268       PRINT_COND_WITH_OFFSET(p, bufend, offset, ',', written);
269     }
270 
271     left = bufend - p; /* calculate available space */
272     result = coap_print_link(r, p, &left, &offset);
273 
274     if (result & COAP_PRINT_STATUS_ERROR) {
275       break;
276     }
277 
278     /* coap_print_link() returns the number of characters that
279      * where actually written to p. Now advance to its end. */
280     p += COAP_PRINT_OUTPUT_LENGTH(result);
281     written += left;
282   }
283 
284   *buflen = written;
285   output_length = p - buf;
286 
287   if (output_length > COAP_PRINT_STATUS_MAX) {
288     return COAP_PRINT_STATUS_ERROR;
289   }
290 
291   result = (coap_print_status_t)output_length;
292 
293   if (result + old_offset - offset < *buflen) {
294     result |= COAP_PRINT_STATUS_TRUNC;
295   }
296   return result;
297 }
298 
299 static coap_str_const_t null_path_value = {0, (const uint8_t*)""};
300 static coap_str_const_t *null_path = &null_path_value;
301 
302 coap_resource_t *
303 coap_resource_init(coap_str_const_t *uri_path, int flags) {
304   coap_resource_t *r;
305 
306   r = (coap_resource_t *)coap_malloc_type(COAP_RESOURCE, sizeof(coap_resource_t));
307   if (r) {
308     memset(r, 0, sizeof(coap_resource_t));
309 
310     if (!(flags & COAP_RESOURCE_FLAGS_RELEASE_URI)) {
311       /* Need to take a copy if caller is not providing a release request */
312       if (uri_path)
313         uri_path = coap_new_str_const(uri_path->s, uri_path->length);
314       else
315         uri_path = coap_new_str_const(null_path->s, null_path->length);
316     }
317     else if (!uri_path) {
318       /* Do not expecte this, but ... */
319       uri_path = coap_new_str_const(null_path->s, null_path->length);
320     }
321 
322     if (uri_path)
323       r->uri_path = uri_path;
324 
325     r->flags = flags;
326   } else {
327     coap_log(LOG_DEBUG, "coap_resource_init: no memory left\n");
328   }
329 
330   return r;
331 }
332 
333 static const uint8_t coap_unknown_resource_uri[] =
334                        "- Unknown -";
335 
336 coap_resource_t *
337 coap_resource_unknown_init(coap_method_handler_t put_handler) {
338   coap_resource_t *r;
339 
340   r = (coap_resource_t *)coap_malloc_type(COAP_RESOURCE, sizeof(coap_resource_t));
341   if (r) {
342     memset(r, 0, sizeof(coap_resource_t));
343     r->is_unknown = 1;
344     /* Something unlikely to be used, but it shows up in the logs */
345     r->uri_path = coap_new_str_const(coap_unknown_resource_uri, sizeof(coap_unknown_resource_uri)-1);
346     coap_register_handler(r, COAP_REQUEST_PUT, put_handler);
347   } else {
348     coap_log(LOG_DEBUG, "coap_resource_unknown_init: no memory left\n");
349   }
350 
351   return r;
352 }
353 
354 static const uint8_t coap_proxy_resource_uri[] =
355                        "- Proxy URI -";
356 
357 coap_resource_t *
358 coap_resource_proxy_uri_init(coap_method_handler_t handler,
359                       size_t host_name_count, const char *host_name_list[]) {
360   coap_resource_t *r;
361 
362   if (host_name_count == 0) {
363     coap_log(LOG_ERR,
364           "coap_resource_proxy_uri_init: Must have one or more host names defined\n");
365     return NULL;
366   }
367   r = (coap_resource_t *)coap_malloc_type(COAP_RESOURCE, sizeof(coap_resource_t));
368   if (r) {
369     size_t i;
370     memset(r, 0, sizeof(coap_resource_t));
371     r->is_proxy_uri = 1;
372     /* Something unlikely to be used, but it shows up in the logs */
373     r->uri_path = coap_new_str_const(coap_proxy_resource_uri, sizeof(coap_proxy_resource_uri)-1);
374     /* Preset all the handlers */
375     for (i = 0; i < (sizeof(r->handler) / sizeof(r->handler[0])); i++) {
376       r->handler[i] = handler;
377     }
378     if (host_name_count) {
379       r->proxy_name_list = coap_malloc(host_name_count *
380                                        sizeof(coap_str_const_t*));
381       if (r->proxy_name_list) {
382         for (i = 0; i < host_name_count; i++) {
383           r->proxy_name_list[i] =
384               coap_new_str_const((const uint8_t*)host_name_list[i],
385                                  strlen(host_name_list[i]));
386           if (!r->proxy_name_list[i]) {
387             coap_log(LOG_ERR,
388                      "coap_resource_proxy_uri_init: unable to add host name\n");
389             if (i == 0) {
390               coap_free(r->proxy_name_list);
391               r->proxy_name_list = NULL;
392             }
393             break;
394           }
395         }
396         r->proxy_name_count = i;
397       }
398     }
399   } else {
400     coap_log(LOG_DEBUG, "coap_resource_proxy_uri_init: no memory left\n");
401   }
402 
403   return r;
404 }
405 
406 coap_attr_t *
407 coap_add_attr(coap_resource_t *resource,
408               coap_str_const_t *name,
409               coap_str_const_t *val,
410               int flags) {
411   coap_attr_t *attr;
412 
413   if (!resource || !name)
414     return NULL;
415 
416   attr = (coap_attr_t *)coap_malloc_type(COAP_RESOURCEATTR, sizeof(coap_attr_t));
417 
418   if (attr) {
419     if (!(flags & COAP_ATTR_FLAGS_RELEASE_NAME)) {
420       /* Need to take a copy if caller is not providing a release request */
421       name = coap_new_str_const(name->s, name->length);
422     }
423     attr->name = name;
424     if (val) {
425       if (!(flags & COAP_ATTR_FLAGS_RELEASE_VALUE)) {
426         /* Need to take a copy if caller is not providing a release request */
427         val = coap_new_str_const(val->s, val->length);
428       }
429     }
430     attr->value = val;
431 
432     attr->flags = flags;
433 
434     /* add attribute to resource list */
435     LL_PREPEND(resource->link_attr, attr);
436   } else {
437     coap_log(LOG_DEBUG, "coap_add_attr: no memory left\n");
438   }
439 
440   return attr;
441 }
442 
443 coap_attr_t *
444 coap_find_attr(coap_resource_t *resource,
445                coap_str_const_t *name) {
446   coap_attr_t *attr;
447 
448   if (!resource || !name)
449     return NULL;
450 
451   LL_FOREACH(resource->link_attr, attr) {
452     if (attr->name->length == name->length &&
453         memcmp(attr->name->s, name->s, name->length) == 0)
454       return attr;
455   }
456 
457   return NULL;
458 }
459 
460 coap_str_const_t *
461 coap_attr_get_value(coap_attr_t *attr) {
462   if (attr)
463     return attr->value;
464   return NULL;
465 }
466 
467 void
468 coap_delete_attr(coap_attr_t *attr) {
469   if (!attr)
470     return;
471   coap_delete_str_const(attr->name);
472   if (attr->value) {
473     coap_delete_str_const(attr->value);
474   }
475 
476 #ifdef WITH_LWIP
477   memp_free(MEMP_COAP_RESOURCEATTR, attr);
478 #endif
479 #ifndef WITH_LWIP
480   coap_free_type(COAP_RESOURCEATTR, attr);
481 #endif
482 }
483 
484 typedef enum coap_deleting_resource_t {
485   COAP_DELETING_RESOURCE,
486   COAP_NOT_DELETING_RESOURCE
487 } coap_deleting_resource_t;
488 
489 static void coap_notify_observers(coap_context_t *context, coap_resource_t *r,
490                                   coap_deleting_resource_t deleting);
491 
492 static void
493 coap_free_resource(coap_resource_t *resource) {
494   coap_attr_t *attr, *tmp;
495   coap_subscription_t *obs, *otmp;
496 
497   assert(resource);
498 
499   coap_resource_notify_observers(resource, NULL);
500   coap_notify_observers(resource->context, resource, COAP_DELETING_RESOURCE);
501 
502   if (resource->context->release_userdata && resource->user_data)
503     resource->context->release_userdata(resource->user_data);
504 
505   /* delete registered attributes */
506   LL_FOREACH_SAFE(resource->link_attr, attr, tmp) coap_delete_attr(attr);
507 
508   /* Either the application provided or libcoap copied - need to delete it */
509   coap_delete_str_const(resource->uri_path);
510 
511   /* free all elements from resource->subscribers */
512   LL_FOREACH_SAFE( resource->subscribers, obs, otmp ) {
513     coap_session_release( obs->session );
514     coap_delete_pdu(obs->pdu);
515     coap_delete_cache_key(obs->cache_key);
516     COAP_FREE_TYPE( subscription, obs );
517   }
518   if (resource->proxy_name_count && resource->proxy_name_list) {
519     size_t i;
520 
521     for (i = 0; i < resource->proxy_name_count; i++) {
522       coap_delete_str_const(resource->proxy_name_list[i]);
523     }
524     coap_free(resource->proxy_name_list);
525   }
526 
527 #ifdef WITH_LWIP
528   memp_free(MEMP_COAP_RESOURCE, resource);
529 #endif
530 #ifndef WITH_LWIP
531   coap_free_type(COAP_RESOURCE, resource);
532 #endif /* WITH_CONTIKI */
533 }
534 
535 void
536 coap_add_resource(coap_context_t *context, coap_resource_t *resource) {
537   if (resource->is_unknown) {
538     if (context->unknown_resource)
539       coap_free_resource(context->unknown_resource);
540     context->unknown_resource = resource;
541   }
542   else if (resource->is_proxy_uri) {
543     if (context->proxy_uri_resource)
544       coap_free_resource(context->proxy_uri_resource);
545     context->proxy_uri_resource = resource;
546   }
547   else {
548     coap_resource_t *r = coap_get_resource_from_uri_path(context,
549                                                          resource->uri_path);
550 
551     if (r) {
552       coap_log(LOG_WARNING,
553         "coap_add_resource: Duplicate uri_path '%*.*s', old resource deleted\n",
554               (int)resource->uri_path->length, (int)resource->uri_path->length,
555               resource->uri_path->s);
556       coap_delete_resource(context, r);
557     }
558     RESOURCES_ADD(context->resources, resource);
559   }
560   assert(resource->context == NULL);
561   resource->context = context;
562 }
563 
564 int
565 coap_delete_resource(coap_context_t *context, coap_resource_t *resource) {
566   if (!context || !resource)
567     return 0;
568 
569   if (resource->is_unknown && (context->unknown_resource == resource)) {
570     coap_free_resource(context->unknown_resource);
571     context->unknown_resource = NULL;
572     return 1;
573   }
574   if (resource->is_proxy_uri && (context->proxy_uri_resource == resource)) {
575     coap_free_resource(context->proxy_uri_resource);
576     context->proxy_uri_resource = NULL;
577     return 1;
578   }
579 
580   /* remove resource from list */
581   RESOURCES_DELETE(context->resources, resource);
582 
583   /* and free its allocated memory */
584   coap_free_resource(resource);
585 
586   return 1;
587 }
588 
589 void
590 coap_delete_all_resources(coap_context_t *context) {
591   coap_resource_t *res;
592   coap_resource_t *rtmp;
593 
594   /* Cannot call RESOURCES_ITER because coap_free_resource() releases
595    * the allocated storage. */
596 
597   HASH_ITER(hh, context->resources, res, rtmp) {
598     HASH_DELETE(hh, context->resources, res);
599     coap_free_resource(res);
600   }
601 
602   context->resources = NULL;
603 
604   if (context->unknown_resource) {
605     coap_free_resource(context->unknown_resource);
606     context->unknown_resource = NULL;
607   }
608   if (context->proxy_uri_resource) {
609     coap_free_resource(context->proxy_uri_resource);
610     context->proxy_uri_resource = NULL;
611   }
612 }
613 
614 coap_resource_t *
615 coap_get_resource_from_uri_path(coap_context_t *context, coap_str_const_t *uri_path) {
616   coap_resource_t *result;
617 
618   RESOURCES_FIND(context->resources, uri_path, result);
619 
620   return result;
621 }
622 
623 coap_print_status_t
624 coap_print_link(const coap_resource_t *resource,
625                 unsigned char *buf, size_t *len, size_t *offset) {
626   unsigned char *p = buf;
627   const uint8_t *bufend = buf + *len;
628   coap_attr_t *attr;
629   coap_print_status_t result = 0;
630   size_t output_length = 0;
631   const size_t old_offset = *offset;
632 
633   *len = 0;
634   PRINT_COND_WITH_OFFSET(p, bufend, *offset, '<', *len);
635   PRINT_COND_WITH_OFFSET(p, bufend, *offset, '/', *len);
636 
637   COPY_COND_WITH_OFFSET(p, bufend, *offset,
638                         resource->uri_path->s, resource->uri_path->length, *len);
639 
640   PRINT_COND_WITH_OFFSET(p, bufend, *offset, '>', *len);
641 
642   LL_FOREACH(resource->link_attr, attr) {
643 
644     PRINT_COND_WITH_OFFSET(p, bufend, *offset, ';', *len);
645 
646     COPY_COND_WITH_OFFSET(p, bufend, *offset,
647                           attr->name->s, attr->name->length, *len);
648 
649     if (attr->value && attr->value->s) {
650       PRINT_COND_WITH_OFFSET(p, bufend, *offset, '=', *len);
651 
652       COPY_COND_WITH_OFFSET(p, bufend, *offset,
653                             attr->value->s, attr->value->length, *len);
654     }
655 
656   }
657   if (resource->observable) {
658     COPY_COND_WITH_OFFSET(p, bufend, *offset, ";obs", 4, *len);
659   }
660 
661   output_length = p - buf;
662 
663   if (output_length > COAP_PRINT_STATUS_MAX) {
664     return COAP_PRINT_STATUS_ERROR;
665   }
666 
667   result = (coap_print_status_t)output_length;
668 
669   if (result + old_offset - *offset < *len) {
670     result |= COAP_PRINT_STATUS_TRUNC;
671   }
672 
673   return result;
674 }
675 
676 void
677 coap_register_handler(coap_resource_t *resource,
678                       coap_request_t method,
679                       coap_method_handler_t handler) {
680   assert(resource);
681   assert(method > 0 && (size_t)(method-1) < sizeof(resource->handler)/sizeof(coap_method_handler_t));
682   resource->handler[method-1] = handler;
683 }
684 
685 coap_subscription_t *
686 coap_find_observer(coap_resource_t *resource, coap_session_t *session,
687                      const coap_binary_t *token) {
688   coap_subscription_t *s;
689 
690   assert(resource);
691   assert(session);
692 
693   LL_FOREACH(resource->subscribers, s) {
694     if (s->session == session
695         && (!token || (token->length == s->pdu->token_length
696                        && memcmp(token->s, s->pdu->token, token->length) == 0)))
697       return s;
698   }
699 
700   return NULL;
701 }
702 
703 static coap_subscription_t *
704 coap_find_observer_cache_key(coap_resource_t *resource, coap_session_t *session,
705                      const coap_cache_key_t *cache_key) {
706   coap_subscription_t *s;
707 
708   assert(resource);
709   assert(session);
710 
711   LL_FOREACH(resource->subscribers, s) {
712     if (s->session == session
713         && (memcmp(cache_key, &s->cache_key, sizeof(s->cache_key)) == 0))
714       return s;
715   }
716 
717   return NULL;
718 }
719 
720 coap_subscription_t *
721 coap_add_observer(coap_resource_t *resource,
722                   coap_session_t *session,
723                   const coap_binary_t *token,
724                   const coap_pdu_t *request) {
725   coap_subscription_t *s;
726   coap_cache_key_t *cache_key = NULL;
727   size_t len;
728   const uint8_t *data;
729 /* https://tools.ietf.org/html/rfc7641#section-3.6 */
730 static const uint16_t cache_ignore_options[] = { COAP_OPTION_ETAG };
731 
732   assert( session );
733 
734   /* Check if there is already a subscription for this peer. */
735   s = coap_find_observer(resource, session, token);
736   if (!s) {
737     /*
738      * Cannot allow a duplicate to be created for the same query as application
739      * may not be cleaning up duplicates.  If duplicate found, then original
740      * observer is deleted and a new one created with the new token
741      */
742     cache_key = coap_cache_derive_key_w_ignore(session, request,
743                                                COAP_CACHE_IS_SESSION_BASED,
744                                                cache_ignore_options,
745                   sizeof(cache_ignore_options)/sizeof(cache_ignore_options[0]));
746     s = coap_find_observer_cache_key(resource, session, cache_key);
747     if (s) {
748       /* Delete old entry with old token */
749       coap_binary_t tmp_token = { s->pdu->token_length, s->pdu->token };
750       coap_delete_observer(resource, session, &tmp_token);
751       s = NULL;
752     }
753   }
754 
755   /* We are done if subscription was found. */
756   if (s) {
757     return s;
758   }
759 
760   /* Create a new subscription */
761   s = COAP_MALLOC_TYPE(subscription);
762 
763   if (!s) {
764     coap_delete_cache_key(cache_key);
765     return NULL;
766   }
767 
768   coap_subscription_init(s);
769   s->pdu = coap_pdu_duplicate(request, session, request->token_length,
770                               request->token, NULL);
771   if (s->pdu == NULL) {
772     coap_delete_cache_key(cache_key);
773     COAP_FREE_TYPE(subscription, s);
774     return NULL;
775   }
776   if (coap_get_data(request, &len, &data)) {
777     coap_add_data(s->pdu, len, data);
778   }
779   if (cache_key == NULL) {
780     cache_key = coap_cache_derive_key_w_ignore(session, request,
781                                                COAP_CACHE_IS_SESSION_BASED,
782                                                cache_ignore_options,
783                   sizeof(cache_ignore_options)/sizeof(cache_ignore_options[0]));
784     if (cache_key == NULL) {
785       coap_delete_pdu(s->pdu);
786       coap_delete_cache_key(cache_key);
787       COAP_FREE_TYPE(subscription, s);
788       return NULL;
789     }
790   }
791   s->cache_key = cache_key;
792   s->session = coap_session_reference(session);
793 
794   /* add subscriber to resource */
795   LL_PREPEND(resource->subscribers, s);
796 
797   coap_log(LOG_DEBUG, "create new subscription\n");
798 
799   return s;
800 }
801 
802 void
803 coap_touch_observer(coap_context_t *context, coap_session_t *session,
804                     const coap_binary_t *token) {
805   coap_subscription_t *s;
806 
807   RESOURCES_ITER(context->resources, r) {
808     s = coap_find_observer(r, session, token);
809     if (s) {
810       s->fail_cnt = 0;
811     }
812   }
813 }
814 
815 int
816 coap_delete_observer(coap_resource_t *resource, coap_session_t *session,
817                      const coap_binary_t *token) {
818   coap_subscription_t *s;
819 
820   s = coap_find_observer(resource, session, token);
821 
822   if ( s && coap_get_log_level() >= LOG_DEBUG ) {
823     char outbuf[2 * 8 + 1] = "";
824     unsigned int i;
825     for ( i = 0; i < s->pdu->token_length; i++ )
826       snprintf( &outbuf[2 * i], 3, "%02x", s->pdu->token[i] );
827     coap_log(LOG_DEBUG, "removed observer with token '%s'\n", outbuf);
828   }
829 
830   if (resource->subscribers && s) {
831     LL_DELETE(resource->subscribers, s);
832     coap_session_release( session );
833     coap_delete_pdu(s->pdu);
834     coap_delete_cache_key(s->cache_key);
835     COAP_FREE_TYPE(subscription,s);
836   }
837 
838   return s != NULL;
839 }
840 
841 void
842 coap_delete_observers(coap_context_t *context, coap_session_t *session) {
843   RESOURCES_ITER(context->resources, resource) {
844     coap_subscription_t *s, *tmp;
845     LL_FOREACH_SAFE(resource->subscribers, s, tmp) {
846       if (s->session == session) {
847         LL_DELETE(resource->subscribers, s);
848         coap_session_release(session);
849         coap_delete_pdu(s->pdu);
850         coap_delete_cache_key(s->cache_key);
851         COAP_FREE_TYPE(subscription, s);
852       }
853     }
854   }
855 }
856 
857 static void
858 coap_notify_observers(coap_context_t *context, coap_resource_t *r,
859                       coap_deleting_resource_t deleting) {
860   coap_method_handler_t h;
861   coap_subscription_t *obs;
862   coap_binary_t token;
863   coap_pdu_t *response;
864   uint8_t buf[4];
865   coap_string_t *query;
866   coap_block_t block;
867 
868   if (r->observable && (r->dirty || r->partiallydirty)) {
869     r->partiallydirty = 0;
870 
871     LL_FOREACH(r->subscribers, obs) {
872       if (r->dirty == 0 && obs->dirty == 0) {
873         /*
874          * running this resource due to partiallydirty, but this observation's
875          * notification was already enqueued
876          */
877         context->observe_pending = 1;
878         continue;
879       }
880       if (obs->session->con_active >= COAP_DEFAULT_NSTART &&
881           ((r->flags & COAP_RESOURCE_FLAGS_NOTIFY_CON) ||
882            (obs->non_cnt >= COAP_OBS_MAX_NON))) {
883         r->partiallydirty = 1;
884         obs->dirty = 1;
885         context->observe_pending = 1;
886         continue;
887       }
888 
889       coap_mid_t mid = COAP_INVALID_MID;
890       obs->dirty = 0;
891       /* initialize response */
892       response = coap_pdu_init(COAP_MESSAGE_CON, 0, 0, coap_session_max_pdu_size(obs->session));
893       if (!response) {
894         obs->dirty = 1;
895         r->partiallydirty = 1;
896         context->observe_pending = 1;
897         coap_log(LOG_DEBUG,
898                  "coap_check_notify: pdu init failed, resource stays "
899                  "partially dirty\n");
900         continue;
901       }
902 
903       if (!coap_add_token(response, obs->pdu->token_length, obs->pdu->token)) {
904         obs->dirty = 1;
905         r->partiallydirty = 1;
906         context->observe_pending = 1;
907         coap_log(LOG_DEBUG,
908                  "coap_check_notify: cannot add token, resource stays "
909                  "partially dirty\n");
910         coap_delete_pdu(response);
911         continue;
912       }
913 
914       token.length = obs->pdu->token_length;
915       token.s = obs->pdu->token;
916 
917       obs->pdu->mid = response->mid = coap_new_message_id(obs->session);
918       if ((r->flags & COAP_RESOURCE_FLAGS_NOTIFY_CON) == 0 &&
919           ((r->flags & COAP_RESOURCE_FLAGS_NOTIFY_NON_ALWAYS) ||
920            obs->non_cnt < COAP_OBS_MAX_NON)) {
921         response->type = COAP_MESSAGE_NON;
922       } else {
923         response->type = COAP_MESSAGE_CON;
924       }
925       switch (deleting) {
926       case COAP_NOT_DELETING_RESOURCE:
927         /* fill with observer-specific data */
928         coap_add_option(response, COAP_OPTION_OBSERVE,
929                         coap_encode_var_safe(buf, sizeof (buf),
930                                              r->observe),
931                         buf);
932         if (coap_get_block(obs->pdu, COAP_OPTION_BLOCK2, &block)) {
933           /* Will get updated later (e.g. M bit) if appropriate */
934           coap_add_option(response, COAP_OPTION_BLOCK2,
935                           coap_encode_var_safe(buf, sizeof(buf),
936                                                ((0 << 4) |
937                                                 (0 << 3) |
938                                                 block.szx)),
939                           buf);
940         }
941 
942         h = r->handler[obs->pdu->code - 1];
943         assert(h);      /* we do not allow subscriptions if no
944                          * GET/FETCH handler is defined */
945         query = coap_get_query(obs->pdu);
946         h(r, obs->session, obs->pdu, query, response);
947         /* Check if lg_xmit generated and update PDU code if so */
948         coap_check_code_lg_xmit(obs->session, response, r, query);
949         coap_delete_string(query);
950         if (COAP_RESPONSE_CLASS(response->code) != 2) {
951           coap_remove_option(response, COAP_OPTION_OBSERVE);
952         }
953         if (COAP_RESPONSE_CLASS(response->code) > 2) {
954           coap_delete_observer(r, obs->session, &token);
955         }
956         break;
957       case COAP_DELETING_RESOURCE:
958       default:
959         response->type = COAP_MESSAGE_NON;
960         response->code = COAP_RESPONSE_CODE(404);
961         break;
962       }
963 
964       if (response->type == COAP_MESSAGE_CON ||
965           (r->flags & COAP_RESOURCE_FLAGS_NOTIFY_NON_ALWAYS)) {
966         obs->non_cnt = 0;
967       } else {
968         obs->non_cnt++;
969       }
970 
971       mid = coap_send_internal( obs->session, response );
972 
973       if (COAP_INVALID_MID == mid) {
974         coap_log(LOG_DEBUG,
975                  "coap_check_notify: sending failed, resource stays "
976                  "partially dirty\n");
977         obs->dirty = 1;
978         r->partiallydirty = 1;
979         context->observe_pending = 1;
980       }
981 
982     }
983   }
984   r->dirty = 0;
985 }
986 
987 int
988 coap_resource_set_dirty(coap_resource_t *r, const coap_string_t *query) {
989   return coap_resource_notify_observers(r, query);
990 }
991 
992 int
993 coap_resource_notify_observers(coap_resource_t *r,
994                                const coap_string_t *query COAP_UNUSED) {
995   if (!r->observable)
996     return 0;
997   if ( !r->subscribers )
998     return 0;
999   r->dirty = 1;
1000 
1001   /* Increment value for next Observe use. Observe value must be < 2^24 */
1002   r->observe = (r->observe + 1) & 0xFFFFFF;
1003 
1004   assert(r->context);
1005   r->context->observe_pending = 1;
1006 #ifdef COAP_EPOLL_SUPPORT
1007   if (r->context->eptimerfd != -1) {
1008     /* Need to immediately trigger any epoll_wait() */
1009     struct itimerspec new_value;
1010     int ret;
1011 
1012     memset(&new_value, 0, sizeof(new_value));
1013     new_value.it_value.tv_nsec = 1; /* small that is not zero */
1014     ret = timerfd_settime(r->context->eptimerfd, 0, &new_value, NULL);
1015     if (ret == -1) {
1016       coap_log(LOG_ERR,
1017                 "%s: timerfd_settime failed: %s (%d)\n",
1018                 "coap_resource_notify_observers",
1019                 coap_socket_strerror(), errno);
1020     }
1021   }
1022 #endif /* COAP_EPOLL_SUPPORT */
1023   return 1;
1024 }
1025 
1026 void
1027 coap_resource_set_mode(coap_resource_t *resource, int mode) {
1028   resource->flags = (resource->flags &
1029     ~(COAP_RESOURCE_FLAGS_NOTIFY_CON|COAP_RESOURCE_FLAGS_NOTIFY_NON)) |
1030     (mode & (COAP_RESOURCE_FLAGS_NOTIFY_CON|COAP_RESOURCE_FLAGS_NOTIFY_NON));
1031 }
1032 
1033 void
1034 coap_resource_set_userdata(coap_resource_t *resource, void *data) {
1035   resource->user_data = data;
1036 }
1037 
1038 void *
1039 coap_resource_get_userdata(coap_resource_t *resource) {
1040   return resource->user_data;
1041 }
1042 
1043 void
1044 coap_resource_release_userdata_handler(coap_context_t *context,
1045                           coap_resource_release_userdata_handler_t callback) {
1046   context->release_userdata = callback;
1047 }
1048 
1049 void
1050 coap_resource_set_get_observable(coap_resource_t *resource, int mode) {
1051   resource->observable = mode ? 1 : 0;
1052 }
1053 
1054 coap_str_const_t*
1055 coap_resource_get_uri_path(coap_resource_t *resource) {
1056   if (resource)
1057     return resource->uri_path;
1058   return NULL;
1059 }
1060 
1061 void
1062 coap_check_notify(coap_context_t *context) {
1063 
1064   if (context->observe_pending) {
1065     context->observe_pending = 0;
1066     RESOURCES_ITER(context->resources, r) {
1067       coap_notify_observers(context, r, COAP_NOT_DELETING_RESOURCE);
1068     }
1069   }
1070 }
1071 
1072 /**
1073  * Checks the failure counter for (peer, token) and removes peer from
1074  * the list of observers for the given resource when COAP_OBS_MAX_FAIL
1075  * is reached.
1076  *
1077  * @param context  The CoAP context to use
1078  * @param resource The resource to check for (peer, token)
1079  * @param session  The observer's session
1080  * @param token    The token that has been used for subscription.
1081  */
1082 static void
1083 coap_remove_failed_observers(coap_context_t *context,
1084                              coap_resource_t *resource,
1085                              coap_session_t *session,
1086                              const coap_binary_t *token) {
1087   coap_subscription_t *obs, *otmp;
1088 
1089   LL_FOREACH_SAFE(resource->subscribers, obs, otmp) {
1090     if ( obs->session == session &&
1091         token->length == obs->pdu->token_length &&
1092         memcmp(token->s, obs->pdu->token, token->length) == 0) {
1093 
1094       /* count failed notifies and remove when
1095        * COAP_MAX_FAILED_NOTIFY is reached */
1096       if (obs->fail_cnt < COAP_OBS_MAX_FAIL)
1097         obs->fail_cnt++;
1098       else {
1099         LL_DELETE(resource->subscribers, obs);
1100         obs->fail_cnt = 0;
1101 
1102         if (LOG_DEBUG <= coap_get_log_level()) {
1103 #ifndef INET6_ADDRSTRLEN
1104 #define INET6_ADDRSTRLEN 40
1105 #endif
1106           unsigned char addr[INET6_ADDRSTRLEN+8];
1107 
1108           if (coap_print_addr(&obs->session->addr_info.remote,
1109                               addr, INET6_ADDRSTRLEN+8))
1110             coap_log(LOG_DEBUG, "** removed observer %s\n", addr);
1111         }
1112         coap_cancel_all_messages(context, obs->session,
1113                                  obs->pdu->token, obs->pdu->token_length);
1114         coap_session_release( obs->session );
1115         coap_delete_pdu(obs->pdu);
1116         coap_delete_cache_key(obs->cache_key);
1117         COAP_FREE_TYPE(subscription, obs);
1118       }
1119       break;                        /* break loop if observer was found */
1120     }
1121   }
1122 }
1123 
1124 void
1125 coap_handle_failed_notify(coap_context_t *context,
1126                           coap_session_t *session,
1127                           const coap_binary_t *token) {
1128 
1129   RESOURCES_ITER(context->resources, r) {
1130         coap_remove_failed_observers(context, r, session, token);
1131   }
1132 }
1133