• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* coap_resource.c -- generic resource handling
2  *
3  * Copyright (C) 2010--2023 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 /**
12  * @file coap_resource.c
13  * @brief Server resource handling functions
14  */
15 
16 #include "coap3/coap_internal.h"
17 
18 #if COAP_SERVER_SUPPORT
19 #include <stdio.h>
20 
21 #ifdef COAP_EPOLL_SUPPORT
22 #include <sys/epoll.h>
23 #include <sys/timerfd.h>
24 #endif /* COAP_EPOLL_SUPPORT */
25 
26 #define COAP_PRINT_STATUS_MAX (~COAP_PRINT_STATUS_MASK)
27 
28 #ifndef min
29 #define min(a,b) ((a) < (b) ? (a) : (b))
30 #endif
31 
32 /* Helper functions for conditional output of character sequences into
33  * a given buffer. The first Offset characters are skipped.
34  */
35 
36 /**
37  * Adds Char to Buf if Offset is zero. Otherwise, Char is not written
38  * and Offset is decremented.
39  */
40 #define PRINT_WITH_OFFSET(Buf,Offset,Char)                \
41   if ((Offset) == 0) {                                        \
42     (*(Buf)++) = (Char);                                \
43   } else {                                                \
44     (Offset)--;                                                \
45   }                                                        \
46 
47 /**
48  * Adds Char to Buf if Offset is zero and Buf is less than Bufend.
49  */
50 #define PRINT_COND_WITH_OFFSET(Buf,Bufend,Offset,Char,Result) {                \
51     if ((Buf) < (Bufend)) {                                                \
52       PRINT_WITH_OFFSET(Buf,Offset,Char);                                \
53     }                                                                        \
54     (Result)++;                                                                \
55   }
56 
57 /**
58  * Copies at most Length characters of Str to Buf. The first Offset
59  * characters are skipped. Output may be truncated to Bufend - Buf
60  * characters.
61  */
62 #define COPY_COND_WITH_OFFSET(Buf,Bufend,Offset,Str,Length,Result) {        \
63     size_t i;                                                                \
64     for (i = 0; i < (Length); i++) {                                        \
65       PRINT_COND_WITH_OFFSET((Buf), (Bufend), (Offset), (Str)[i], (Result)); \
66     }                                                                        \
67   }
68 
69 static int
match(const coap_str_const_t * text,const coap_str_const_t * pattern,int match_prefix,int match_substring)70 match(const coap_str_const_t *text, const coap_str_const_t *pattern, int match_prefix,
71       int match_substring
72      ) {
73   assert(text);
74   assert(pattern);
75 
76   if (text->length < pattern->length)
77     return 0;
78 
79   if (match_substring) {
80     const uint8_t *next_token = text->s;
81     size_t remaining_length = text->length;
82     while (remaining_length) {
83       size_t token_length;
84       const uint8_t *token = next_token;
85       next_token = (unsigned char *)memchr(token, ' ', remaining_length);
86 
87       if (next_token) {
88         token_length = next_token - token;
89         remaining_length -= (token_length + 1);
90         next_token++;
91       } else {
92         token_length = remaining_length;
93         remaining_length = 0;
94       }
95 
96       if ((match_prefix || pattern->length == token_length) &&
97           memcmp(token, pattern->s, pattern->length) == 0)
98         return 1;
99     }
100     return 0;
101   }
102 
103   return (match_prefix || pattern->length == text->length) &&
104          memcmp(text->s, pattern->s, pattern->length) == 0;
105 }
106 
107 /**
108  * Prints the names of all known resources to @p buf. This function
109  * sets @p buflen to the number of bytes actually written and returns
110  * @c 1 on succes. On error, the value in @p buflen is undefined and
111  * the return value will be @c 0.
112  *
113  * @param context The context with the resource map.
114  * @param buf     The buffer to write the result.
115  * @param buflen  Must be initialized to the maximum length of @p buf and will be
116  *                set to the length of the well-known response on return.
117  * @param offset  The offset in bytes where the output shall start and is
118  *                shifted accordingly with the characters that have been
119  *                processed. This parameter is used to support the block
120  *                option.
121  * @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>
122  *
123  * @return COAP_PRINT_STATUS_ERROR on error. Otherwise, the lower 28 bits are
124  *         set to the number of bytes that have actually been written to
125  *         @p buf. COAP_PRINT_STATUS_TRUNC is set when the output has been
126  *         truncated.
127  */
128 #if defined(__GNUC__) && defined(WITHOUT_QUERY_FILTER)
129 coap_print_status_t
coap_print_wellknown(coap_context_t * context,unsigned char * buf,size_t * buflen,size_t offset,const coap_string_t * query_filter COAP_UNUSED)130 coap_print_wellknown(coap_context_t *context, unsigned char *buf, size_t *buflen,
131                      size_t offset,
132                      const coap_string_t *query_filter COAP_UNUSED) {
133 #else /* not a GCC */
134 coap_print_status_t
135 coap_print_wellknown(coap_context_t *context, unsigned char *buf, size_t *buflen,
136                      size_t offset, const coap_string_t *query_filter) {
137 #endif /* GCC */
138   size_t output_length = 0;
139   unsigned char *p = buf;
140   const uint8_t *bufend = buf + *buflen;
141   size_t left, written = 0;
142   coap_print_status_t result;
143   const size_t old_offset = offset;
144   int subsequent_resource = 0;
145 #ifndef WITHOUT_QUERY_FILTER
146   coap_str_const_t resource_param = { 0, NULL }, query_pattern = { 0, NULL };
147   int flags = 0; /* MATCH_SUBSTRING, MATCH_PREFIX, MATCH_URI */
148 #define MATCH_URI       0x01
149 #define MATCH_PREFIX    0x02
150 #define MATCH_SUBSTRING 0x04
151   static const coap_str_const_t _rt_attributes[] = {
152     {2, (const uint8_t *)"rt"},
153     {2, (const uint8_t *)"if"},
154     {3, (const uint8_t *)"rel"},
155     {0, NULL}
156   };
157 #endif /* WITHOUT_QUERY_FILTER */
158 
159 #ifndef WITHOUT_QUERY_FILTER
160   /* split query filter, if any */
161   if (query_filter) {
162     resource_param.s = query_filter->s;
163     while (resource_param.length < query_filter->length &&
164            resource_param.s[resource_param.length] != '=')
165       resource_param.length++;
166 
167     if (resource_param.length < query_filter->length) {
168       const coap_str_const_t *rt_attributes;
169       if (resource_param.length == 4 &&
170           memcmp(resource_param.s, "href", 4) == 0)
171         flags |= MATCH_URI;
172 
173       for (rt_attributes = _rt_attributes; rt_attributes->s; rt_attributes++) {
174         if (resource_param.length == rt_attributes->length &&
175             memcmp(resource_param.s, rt_attributes->s, rt_attributes->length) == 0) {
176           flags |= MATCH_SUBSTRING;
177           break;
178         }
179       }
180 
181       /* rest is query-pattern */
182       query_pattern.s =
183           query_filter->s + resource_param.length + 1;
184 
185       assert((resource_param.length + 1) <= query_filter->length);
186       query_pattern.length =
187           query_filter->length - (resource_param.length + 1);
188 
189       if ((query_pattern.s[0] == '/') && ((flags & MATCH_URI) == MATCH_URI)) {
190         query_pattern.s++;
191         query_pattern.length--;
192       }
193 
194       if (query_pattern.length &&
195           query_pattern.s[query_pattern.length-1] == '*') {
196         query_pattern.length--;
197         flags |= MATCH_PREFIX;
198       }
199     }
200   }
201 #endif /* WITHOUT_QUERY_FILTER */
202 
203   RESOURCES_ITER(context->resources, r) {
204 
205 #ifndef WITHOUT_QUERY_FILTER
206     if (resource_param.length) { /* there is a query filter */
207 
208       if (flags & MATCH_URI) {        /* match resource URI */
209         if (!match(r->uri_path, &query_pattern, (flags & MATCH_PREFIX) != 0,
210                    (flags & MATCH_SUBSTRING) != 0))
211           continue;
212       } else {                        /* match attribute */
213         coap_attr_t *attr;
214         coap_str_const_t unquoted_val;
215         attr = coap_find_attr(r, &resource_param);
216         if (!attr || !attr->value)
217           continue;
218         unquoted_val = *attr->value;
219         if (attr->value->s[0] == '"') {          /* if attribute has a quoted value, remove double quotes */
220           unquoted_val.length -= 2;
221           unquoted_val.s += 1;
222         }
223         if (!(match(&unquoted_val, &query_pattern,
224                     (flags & MATCH_PREFIX) != 0,
225                     (flags & MATCH_SUBSTRING) != 0)))
226           continue;
227       }
228     }
229 #endif /* WITHOUT_QUERY_FILTER */
230 
231     if (!subsequent_resource) {        /* this is the first resource  */
232       subsequent_resource = 1;
233     } else {
234       PRINT_COND_WITH_OFFSET(p, bufend, offset, ',', written);
235     }
236 
237     left = bufend - p; /* calculate available space */
238     result = coap_print_link(r, p, &left, &offset);
239 
240     if (result & COAP_PRINT_STATUS_ERROR) {
241       break;
242     }
243 
244     /* coap_print_link() returns the number of characters that
245      * where actually written to p. Now advance to its end. */
246     p += COAP_PRINT_OUTPUT_LENGTH(result);
247     written += left;
248   }
249 
250   *buflen = written;
251   output_length = p - buf;
252 
253   if (output_length > COAP_PRINT_STATUS_MAX) {
254     return COAP_PRINT_STATUS_ERROR;
255   }
256 
257   result = (coap_print_status_t)output_length;
258 
259   if (result + old_offset - offset < *buflen) {
260     result |= COAP_PRINT_STATUS_TRUNC;
261   }
262   return result;
263 }
264 
265 static coap_str_const_t null_path_value = {0, (const uint8_t *)""};
266 static coap_str_const_t *null_path = &null_path_value;
267 
268 coap_resource_t *
269 coap_resource_init(coap_str_const_t *uri_path, int flags) {
270   coap_resource_t *r;
271 
272   r = (coap_resource_t *)coap_malloc_type(COAP_RESOURCE, sizeof(coap_resource_t));
273   if (r) {
274     memset(r, 0, sizeof(coap_resource_t));
275 
276     if (!(flags & COAP_RESOURCE_FLAGS_RELEASE_URI)) {
277       /* Need to take a copy if caller is not providing a release request */
278       if (uri_path)
279         uri_path = coap_new_str_const(uri_path->s, uri_path->length);
280       else
281         uri_path = coap_new_str_const(null_path->s, null_path->length);
282     } else if (!uri_path) {
283       /* Do not expect this, but ... */
284       uri_path = coap_new_str_const(null_path->s, null_path->length);
285     }
286 
287     if (uri_path)
288       r->uri_path = uri_path;
289 
290     r->flags = flags;
291     r->observe = 2;
292   } else {
293     coap_log_debug("coap_resource_init: no memory left\n");
294   }
295 
296   return r;
297 }
298 
299 static const uint8_t coap_unknown_resource_uri[] =
300     "- Unknown -";
301 
302 coap_resource_t *
303 coap_resource_unknown_init2(coap_method_handler_t put_handler, 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     r->is_unknown = 1;
310     /* Something unlikely to be used, but it shows up in the logs */
311     r->uri_path = coap_new_str_const(coap_unknown_resource_uri, sizeof(coap_unknown_resource_uri)-1);
312     r->flags = flags & COAP_RESOURCE_FLAGS_MCAST_LIST;
313     coap_register_handler(r, COAP_REQUEST_PUT, put_handler);
314   } else {
315     coap_log_debug("coap_resource_unknown_init: no memory left\n");
316   }
317 
318   return r;
319 }
320 
321 coap_resource_t *
322 coap_resource_unknown_init(coap_method_handler_t put_handler) {
323   return coap_resource_unknown_init2(put_handler, 0);
324 }
325 
326 static const uint8_t coap_proxy_resource_uri[] =
327     "- Proxy URI -";
328 
329 coap_resource_t *
330 coap_resource_proxy_uri_init2(coap_method_handler_t handler,
331                               size_t host_name_count,
332                               const char *host_name_list[], int flags) {
333   coap_resource_t *r;
334 
335   if (host_name_count == 0) {
336     coap_log_err("coap_resource_proxy_uri_init: Must have one or more host names defined\n");
337     return NULL;
338   }
339   r = (coap_resource_t *)coap_malloc_type(COAP_RESOURCE, sizeof(coap_resource_t));
340   if (r) {
341     size_t i;
342     memset(r, 0, sizeof(coap_resource_t));
343     r->is_proxy_uri = 1;
344     /* Something unlikely to be used, but it shows up in the logs */
345     r->uri_path = coap_new_str_const(coap_proxy_resource_uri, sizeof(coap_proxy_resource_uri)-1);
346     /* Preset all the handlers */
347     for (i = 0; i < (sizeof(r->handler) / sizeof(r->handler[0])); i++) {
348       r->handler[i] = handler;
349     }
350     if (host_name_count) {
351       r->proxy_name_list = coap_malloc_type(COAP_STRING, host_name_count *
352                                             sizeof(coap_str_const_t *));
353       if (r->proxy_name_list) {
354         for (i = 0; i < host_name_count; i++) {
355           r->proxy_name_list[i] =
356               coap_new_str_const((const uint8_t *)host_name_list[i],
357                                  strlen(host_name_list[i]));
358           if (!r->proxy_name_list[i]) {
359             coap_log_err("coap_resource_proxy_uri_init: unable to add host name\n");
360             if (i == 0) {
361               coap_free_type(COAP_STRING, r->proxy_name_list);
362               r->proxy_name_list = NULL;
363             }
364             break;
365           }
366         }
367         r->proxy_name_count = i;
368       }
369     }
370     r->flags = flags & COAP_RESOURCE_FLAGS_MCAST_LIST;
371   } else {
372     coap_log_debug("coap_resource_proxy_uri_init2: no memory left\n");
373   }
374 
375   return r;
376 }
377 
378 coap_resource_t *
379 coap_resource_proxy_uri_init(coap_method_handler_t handler,
380                              size_t host_name_count, const char *host_name_list[]) {
381   return coap_resource_proxy_uri_init2(handler, host_name_count,
382                                        host_name_list, 0);
383 }
384 
385 coap_attr_t *
386 coap_add_attr(coap_resource_t *resource,
387               coap_str_const_t *name,
388               coap_str_const_t *val,
389               int flags) {
390   coap_attr_t *attr;
391 
392   if (!resource || !name)
393     return NULL;
394   attr = (coap_attr_t *)coap_malloc_type(COAP_RESOURCEATTR, sizeof(coap_attr_t));
395 
396   if (attr) {
397     if (!(flags & COAP_ATTR_FLAGS_RELEASE_NAME)) {
398       /* Need to take a copy if caller is not providing a release request */
399       name = coap_new_str_const(name->s, name->length);
400     }
401     attr->name = name;
402     if (val) {
403       if (!(flags & COAP_ATTR_FLAGS_RELEASE_VALUE)) {
404         /* Need to take a copy if caller is not providing a release request */
405         val = coap_new_str_const(val->s, val->length);
406       }
407     }
408     attr->value = val;
409 
410     attr->flags = flags;
411 
412     /* add attribute to resource list */
413     LL_PREPEND(resource->link_attr, attr);
414   } else {
415     coap_log_debug("coap_add_attr: no memory left\n");
416   }
417 
418   return attr;
419 }
420 
421 coap_attr_t *
422 coap_find_attr(coap_resource_t *resource,
423                coap_str_const_t *name) {
424   coap_attr_t *attr;
425 
426   if (!resource || !name)
427     return NULL;
428 
429   LL_FOREACH(resource->link_attr, attr) {
430     if (attr->name->length == name->length &&
431         memcmp(attr->name->s, name->s, name->length) == 0)
432       return attr;
433   }
434 
435   return NULL;
436 }
437 
438 coap_str_const_t *
439 coap_attr_get_value(coap_attr_t *attr) {
440   if (attr)
441     return attr->value;
442   return NULL;
443 }
444 
445 void
446 coap_delete_attr(coap_attr_t *attr) {
447   if (!attr)
448     return;
449   coap_delete_str_const(attr->name);
450   if (attr->value) {
451     coap_delete_str_const(attr->value);
452   }
453 
454   coap_free_type(COAP_RESOURCEATTR, attr);
455 }
456 
457 typedef enum coap_deleting_resource_t {
458   COAP_DELETING_RESOURCE,
459   COAP_NOT_DELETING_RESOURCE
460 } coap_deleting_resource_t;
461 
462 static void coap_notify_observers(coap_context_t *context, coap_resource_t *r,
463                                   coap_deleting_resource_t deleting);
464 
465 static void
466 coap_free_resource(coap_resource_t *resource) {
467   coap_attr_t *attr, *tmp;
468   coap_subscription_t *obs, *otmp;
469 
470   assert(resource);
471 
472   if (!resource->context->observe_no_clear) {
473     coap_resource_notify_observers(resource, NULL);
474     coap_notify_observers(resource->context, resource, COAP_DELETING_RESOURCE);
475   }
476 
477   if (resource->context->resource_deleted)
478     resource->context->resource_deleted(resource->context, resource->uri_path,
479                                         resource->context->observe_user_data);
480 
481   if (resource->context->release_userdata && resource->user_data)
482     resource->context->release_userdata(resource->user_data);
483 
484   /* delete registered attributes */
485   LL_FOREACH_SAFE(resource->link_attr, attr, tmp) coap_delete_attr(attr);
486 
487   /* Either the application provided or libcoap copied - need to delete it */
488   coap_delete_str_const(resource->uri_path);
489 
490   /* free all elements from resource->subscribers */
491   LL_FOREACH_SAFE(resource->subscribers, obs, otmp) {
492     if (resource->context->observe_deleted)
493       resource->context->observe_deleted(obs->session, obs,
494                                          resource->context->observe_user_data);
495     coap_session_release(obs->session);
496     coap_delete_pdu(obs->pdu);
497     coap_delete_cache_key(obs->cache_key);
498     coap_free_type(COAP_SUBSCRIPTION, obs);
499   }
500   if (resource->proxy_name_count && resource->proxy_name_list) {
501     size_t i;
502 
503     for (i = 0; i < resource->proxy_name_count; i++) {
504       coap_delete_str_const(resource->proxy_name_list[i]);
505     }
506     coap_free_type(COAP_STRING, resource->proxy_name_list);
507   }
508 
509   coap_free_type(COAP_RESOURCE, resource);
510 }
511 
512 void
513 coap_add_resource(coap_context_t *context, coap_resource_t *resource) {
514   if (resource->is_unknown) {
515     if (context->unknown_resource)
516       coap_free_resource(context->unknown_resource);
517     context->unknown_resource = resource;
518   } else if (resource->is_proxy_uri) {
519     if (context->proxy_uri_resource)
520       coap_free_resource(context->proxy_uri_resource);
521     context->proxy_uri_resource = resource;
522   } else {
523     coap_resource_t *r = coap_get_resource_from_uri_path(context,
524                                                          resource->uri_path);
525 
526     if (r) {
527       coap_log_warn("coap_add_resource: Duplicate uri_path '%*.*s', old resource deleted\n",
528                     (int)resource->uri_path->length, (int)resource->uri_path->length,
529                     resource->uri_path->s);
530       coap_delete_resource(context, r);
531     }
532     RESOURCES_ADD(context->resources, resource);
533 #if COAP_WITH_OBSERVE_PERSIST
534     if (context->unknown_pdu && context->dyn_resource_save_file &&
535         context->dyn_resource_added && resource->observable) {
536       coap_bin_const_t raw_packet;
537 
538       raw_packet.s = context->unknown_pdu->token -
539                      context->unknown_pdu->hdr_size;
540       raw_packet.length = context->unknown_pdu->used_size +
541                           context->unknown_pdu->hdr_size;
542       context->dyn_resource_added(context->unknown_session, resource->uri_path,
543                                   &raw_packet, context->observe_user_data);
544     }
545 #endif /* COAP_WITH_OBSERVE_PERSIST */
546   }
547   assert(resource->context == NULL);
548   resource->context = context;
549 }
550 
551 /*
552  * Input context is ignored, but param left there to keep API consistent
553  */
554 int
555 coap_delete_resource(coap_context_t *context, coap_resource_t *resource) {
556   if (!resource)
557     return 0;
558 
559   context = resource->context;
560 
561   if (resource->is_unknown) {
562     if (context && context->unknown_resource == resource) {
563       context->unknown_resource = NULL;
564     }
565   } else if (resource->is_proxy_uri) {
566     if (context && context->proxy_uri_resource == resource) {
567       context->proxy_uri_resource = NULL;
568     }
569   } else if (context) {
570     /* remove resource from list */
571     RESOURCES_DELETE(context->resources, resource);
572   }
573 
574   /* and free its allocated memory */
575   coap_free_resource(resource);
576 
577   return 1;
578 }
579 
580 void
581 coap_delete_all_resources(coap_context_t *context) {
582   coap_resource_t *res;
583   coap_resource_t *rtmp;
584 
585   /* Cannot call RESOURCES_ITER because coap_free_resource() releases
586    * the allocated storage. */
587 
588   HASH_ITER(hh, context->resources, res, rtmp) {
589     HASH_DELETE(hh, context->resources, res);
590     coap_free_resource(res);
591   }
592 
593   context->resources = NULL;
594 
595   if (context->unknown_resource) {
596     coap_free_resource(context->unknown_resource);
597     context->unknown_resource = NULL;
598   }
599   if (context->proxy_uri_resource) {
600     coap_free_resource(context->proxy_uri_resource);
601     context->proxy_uri_resource = NULL;
602   }
603 }
604 
605 coap_resource_t *
606 coap_get_resource_from_uri_path(coap_context_t *context, coap_str_const_t *uri_path) {
607   coap_resource_t *result;
608 
609   RESOURCES_FIND(context->resources, uri_path, result);
610 
611   return result;
612 }
613 
614 coap_print_status_t
615 coap_print_link(const coap_resource_t *resource,
616                 unsigned char *buf, size_t *len, size_t *offset) {
617   unsigned char *p = buf;
618   const uint8_t *bufend = buf + *len;
619   coap_attr_t *attr;
620   coap_print_status_t result = 0;
621   size_t output_length = 0;
622   const size_t old_offset = *offset;
623 
624   *len = 0;
625   PRINT_COND_WITH_OFFSET(p, bufend, *offset, '<', *len);
626   PRINT_COND_WITH_OFFSET(p, bufend, *offset, '/', *len);
627 
628   COPY_COND_WITH_OFFSET(p, bufend, *offset,
629                         resource->uri_path->s, resource->uri_path->length, *len);
630 
631   PRINT_COND_WITH_OFFSET(p, bufend, *offset, '>', *len);
632 
633   LL_FOREACH(resource->link_attr, attr) {
634 
635     PRINT_COND_WITH_OFFSET(p, bufend, *offset, ';', *len);
636 
637     COPY_COND_WITH_OFFSET(p, bufend, *offset,
638                           attr->name->s, attr->name->length, *len);
639 
640     if (attr->value && attr->value->s) {
641       PRINT_COND_WITH_OFFSET(p, bufend, *offset, '=', *len);
642 
643       COPY_COND_WITH_OFFSET(p, bufend, *offset,
644                             attr->value->s, attr->value->length, *len);
645     }
646 
647   }
648   if (resource->observable) {
649     COPY_COND_WITH_OFFSET(p, bufend, *offset, ";obs", 4, *len);
650   }
651 
652 #if COAP_OSCORE_SUPPORT
653   /* If oscore is enabled */
654   if (resource->flags & COAP_RESOURCE_FLAGS_OSCORE_ONLY)
655     COPY_COND_WITH_OFFSET(p, bufend, *offset, ";osc", 4, *len);
656 #endif /* COAP_OSCORE_SUPPORT */
657 
658   output_length = p - buf;
659 
660   if (output_length > COAP_PRINT_STATUS_MAX) {
661     return COAP_PRINT_STATUS_ERROR;
662   }
663 
664   result = (coap_print_status_t)output_length;
665 
666   if (result + old_offset - *offset < *len) {
667     result |= COAP_PRINT_STATUS_TRUNC;
668   }
669 
670   return result;
671 }
672 
673 void
674 coap_register_handler(coap_resource_t *resource,
675                       coap_request_t method,
676                       coap_method_handler_t handler) {
677   coap_register_request_handler(resource, method, handler);
678 }
679 
680 void
681 coap_register_request_handler(coap_resource_t *resource,
682                               coap_request_t method,
683                               coap_method_handler_t handler) {
684   assert(resource);
685   assert(method > 0 && (size_t)(method-1) <
686          sizeof(resource->handler)/sizeof(coap_method_handler_t));
687   resource->handler[method-1] = handler;
688 }
689 
690 coap_subscription_t *
691 coap_find_observer(coap_resource_t *resource, coap_session_t *session,
692                    const coap_bin_const_t *token) {
693   coap_subscription_t *s;
694 
695   assert(resource);
696   assert(session);
697 
698   LL_FOREACH(resource->subscribers, s) {
699     if (s->session == session &&
700         (!token || coap_binary_equal(token, &s->pdu->actual_token)))
701       return s;
702   }
703 
704   return NULL;
705 }
706 
707 static coap_subscription_t *
708 coap_find_observer_cache_key(coap_resource_t *resource, coap_session_t *session,
709                              const coap_cache_key_t *cache_key) {
710   coap_subscription_t *s;
711 
712   assert(resource);
713   assert(session);
714 
715   LL_FOREACH(resource->subscribers, s) {
716     if (s->session == session
717         && (memcmp(cache_key, s->cache_key, sizeof(coap_cache_key_t)) == 0))
718       return s;
719   }
720 
721   return NULL;
722 }
723 
724 coap_subscription_t *
725 coap_add_observer(coap_resource_t *resource,
726                   coap_session_t *session,
727                   const coap_bin_const_t *token,
728                   const coap_pdu_t *request) {
729   coap_subscription_t *s;
730   coap_cache_key_t *cache_key = NULL;
731   size_t len;
732   const uint8_t *data;
733   /* https://rfc-editor.org/rfc/rfc7641#section-3.6 */
734   static const uint16_t cache_ignore_options[] = { COAP_OPTION_ETAG,
735                                                    COAP_OPTION_OSCORE
736                                                  };
737 
738   assert(session);
739 
740   /* Check if there is already a subscription for this peer. */
741   s = coap_find_observer(resource, session, token);
742   if (!s) {
743     /*
744      * Cannot allow a duplicate to be created for the same query as application
745      * may not be cleaning up duplicates.  If duplicate found, then original
746      * observer is deleted and a new one created with the new token
747      */
748     cache_key = coap_cache_derive_key_w_ignore(session, request,
749                                                COAP_CACHE_IS_SESSION_BASED,
750                                                cache_ignore_options,
751                                                sizeof(cache_ignore_options)/sizeof(cache_ignore_options[0]));
752     if (cache_key) {
753       s = coap_find_observer_cache_key(resource, session, cache_key);
754       if (s) {
755         /* Delete old entry with old token */
756         coap_delete_observer(resource, session, &s->pdu->actual_token);
757         s = NULL;
758       }
759     }
760   }
761 
762   /* We are done if subscription was found. */
763   if (s) {
764     return s;
765   }
766 
767   /* Check if there is already maximum number of subscribers present */
768 #if (COAP_RESOURCE_MAX_SUBSCRIBER > 0)
769   uint32_t subscriber_count = 0;
770   LL_COUNT(resource->subscribers, s, subscriber_count);
771   if (subscriber_count >= COAP_RESOURCE_MAX_SUBSCRIBER) {
772     return NULL; /* Signal error */
773   }
774 #endif /* COAP_RESOURCE_MAX_SUBSCRIBER */
775 
776   /* Create a new subscription */
777   s = coap_malloc_type(COAP_SUBSCRIPTION, sizeof(coap_subscription_t));
778 
779   if (!s) {
780     coap_delete_cache_key(cache_key);
781     return NULL;
782   }
783 
784   coap_subscription_init(s);
785   s->pdu = coap_pdu_duplicate(request, session, token->length,
786                               token->s, NULL);
787   if (s->pdu == NULL) {
788     coap_delete_cache_key(cache_key);
789     coap_free_type(COAP_SUBSCRIPTION, s);
790     return NULL;
791   }
792   if (coap_get_data(request, &len, &data)) {
793     /* This could be a large bodied FETCH */
794     s->pdu->max_size = 0;
795     coap_add_data(s->pdu, len, data);
796   }
797   if (cache_key == NULL) {
798     cache_key = coap_cache_derive_key_w_ignore(session, request,
799                                                COAP_CACHE_IS_SESSION_BASED,
800                                                cache_ignore_options,
801                                                sizeof(cache_ignore_options)/sizeof(cache_ignore_options[0]));
802     if (cache_key == NULL) {
803       coap_delete_pdu(s->pdu);
804       coap_delete_cache_key(cache_key);
805       coap_free_type(COAP_SUBSCRIPTION, s);
806       return NULL;
807     }
808   }
809   s->cache_key = cache_key;
810   s->session = coap_session_reference(session);
811 
812   /* add subscriber to resource */
813   LL_PREPEND(resource->subscribers, s);
814 
815   coap_log_debug("create new subscription %p key 0x%02x%02x%02x%02x\n",
816                  (void *)s, s->cache_key->key[0], s->cache_key->key[1],
817                  s->cache_key->key[2], s->cache_key->key[3]);
818 
819   if (session->context->observe_added && session->proto == COAP_PROTO_UDP) {
820     coap_bin_const_t raw_packet;
821     coap_bin_const_t *oscore_info = NULL;
822 #if COAP_OSCORE_SUPPORT
823     oscore_association_t *association;
824 
825     if (session->recipient_ctx && session->recipient_ctx->recipient_id) {
826       /*
827        * Need to track the association used for tracking this observe, done as
828        * a CBOR array. Read in coap_persist_observe_add().
829        *
830        * If an entry is null, then use nil, else a set of bytes
831        *
832        * Currently tracking 5 items
833        *  recipient_id
834        *  id_context
835        *  aad        (from oscore_association_t)
836        *  partial_iv (from oscore_association_t)
837        *  nonce      (from oscore_association_t)
838        */
839       uint8_t info_buffer[60];
840       uint8_t *info_buf = info_buffer;
841       size_t info_len = sizeof(info_buffer);
842       size_t ret = 0;
843       coap_bin_const_t ctoken = { token->length, token->s };
844 
845       ret += oscore_cbor_put_array(&info_buf, &info_len, 5);
846       ret += oscore_cbor_put_bytes(&info_buf,
847                                    &info_len,
848                                    session->recipient_ctx->recipient_id->s,
849                                    session->recipient_ctx->recipient_id->length);
850       if (session->recipient_ctx->osc_ctx &&
851           session->recipient_ctx->osc_ctx->id_context) {
852         ret += oscore_cbor_put_bytes(&info_buf,
853                                      &info_len,
854                                      session->recipient_ctx->osc_ctx->id_context->s,
855                                      session->recipient_ctx->osc_ctx->id_context->length);
856       } else {
857         ret += oscore_cbor_put_nil(&info_buf, &info_len);
858       }
859       association = oscore_find_association(session, &ctoken);
860       if (association) {
861         if (association->aad) {
862           ret += oscore_cbor_put_bytes(&info_buf,
863                                        &info_len,
864                                        association->aad->s,
865                                        association->aad->length);
866         } else {
867           ret += oscore_cbor_put_nil(&info_buf, &info_len);
868         }
869         if (association->partial_iv) {
870           ret += oscore_cbor_put_bytes(&info_buf,
871                                        &info_len,
872                                        association->partial_iv->s,
873                                        association->partial_iv->length);
874         } else {
875           ret += oscore_cbor_put_nil(&info_buf, &info_len);
876         }
877         if (association->nonce) {
878           ret += oscore_cbor_put_bytes(&info_buf,
879                                        &info_len,
880                                        association->nonce->s,
881                                        association->nonce->length);
882         } else {
883           ret += oscore_cbor_put_nil(&info_buf, &info_len);
884         }
885       } else {
886         ret += oscore_cbor_put_nil(&info_buf, &info_len);
887         ret += oscore_cbor_put_nil(&info_buf, &info_len);
888       }
889       oscore_info = coap_new_bin_const(info_buffer, ret);
890     }
891 #endif /* COAP_OSCORE_SUPPORT */
892 
893     /* s->pdu header is not currently encoded */
894     memcpy(s->pdu->token - request->hdr_size,
895            request->token - request->hdr_size, request->hdr_size);
896     raw_packet.s = s->pdu->token - request->hdr_size;
897     raw_packet.length = s->pdu->used_size + request->hdr_size;
898     session->context->observe_added(session, s, session->proto,
899                                     &session->endpoint->bind_addr,
900                                     &session->addr_info,
901                                     &raw_packet,
902                                     oscore_info,
903                                     session->context->observe_user_data);
904 #if COAP_OSCORE_SUPPORT
905     coap_delete_bin_const(oscore_info);
906 #endif /* COAP_OSCORE_SUPPORT */
907   }
908   if (resource->context->track_observe_value) {
909     /* Track last used observe value (as app handler is called) */
910     resource->context->track_observe_value(resource->context,resource->uri_path,
911                                            resource->observe,
912                                            resource->context->observe_user_data);
913   }
914 
915   return s;
916 }
917 
918 void
919 coap_touch_observer(coap_context_t *context, coap_session_t *session,
920                     const coap_bin_const_t *token) {
921   coap_subscription_t *s;
922 
923   RESOURCES_ITER(context->resources, r) {
924     s = coap_find_observer(r, session, token);
925     if (s) {
926       s->fail_cnt = 0;
927     }
928   }
929 }
930 
931 int
932 coap_delete_observer(coap_resource_t *resource, coap_session_t *session,
933                      const coap_bin_const_t *token) {
934   coap_subscription_t *s;
935 
936   s = coap_find_observer(resource, session, token);
937 
938   if (s && coap_get_log_level() >= COAP_LOG_DEBUG) {
939     char outbuf[2 * 8 + 1] = "";
940     unsigned int i;
941 
942     for (i = 0; i < s->pdu->actual_token.length; i++) {
943       size_t size = strlen(outbuf);
944 
945       snprintf(&outbuf[size], sizeof(outbuf)-size, "%02x",
946                s->pdu->actual_token.s[i]);
947     }
948     coap_log_debug("removed subscription %p with token '%s' key 0x%02x%02x%02x%02x\n",
949                    (void *)s, outbuf, s->cache_key->key[0], s->cache_key->key[1],
950                    s->cache_key->key[2], s-> cache_key->key[3]);
951   }
952   if (s && session->context->observe_deleted)
953     session->context->observe_deleted(session, s,
954                                       session->context->observe_user_data);
955 
956   if (resource->subscribers && s) {
957     LL_DELETE(resource->subscribers, s);
958     coap_session_release(session);
959     coap_delete_pdu(s->pdu);
960     coap_delete_cache_key(s->cache_key);
961     coap_free_type(COAP_SUBSCRIPTION, s);
962   }
963 
964   return s != NULL;
965 }
966 
967 void
968 coap_delete_observers(coap_context_t *context, coap_session_t *session) {
969   RESOURCES_ITER(context->resources, resource) {
970     coap_subscription_t *s, *tmp;
971     LL_FOREACH_SAFE(resource->subscribers, s, tmp) {
972       if (s->session == session) {
973         if (context->observe_deleted)
974           context->observe_deleted(session, s, context->observe_user_data);
975         LL_DELETE(resource->subscribers, s);
976         coap_session_release(session);
977         coap_delete_pdu(s->pdu);
978         coap_delete_cache_key(s->cache_key);
979         coap_free_type(COAP_SUBSCRIPTION, s);
980       }
981     }
982   }
983 }
984 
985 static void
986 coap_notify_observers(coap_context_t *context, coap_resource_t *r,
987                       coap_deleting_resource_t deleting) {
988   coap_method_handler_t h;
989   coap_subscription_t *obs, *otmp;
990   coap_pdu_t *response;
991   uint8_t buf[4];
992   coap_string_t *query;
993   coap_block_b_t block;
994   coap_tick_t now;
995   coap_session_t *obs_session;
996 
997   if (r->observable && (r->dirty || r->partiallydirty)) {
998     r->partiallydirty = 0;
999 
1000     LL_FOREACH_SAFE(r->subscribers, obs, otmp) {
1001       obs_session = obs->session;
1002       if (r->dirty == 0 && obs->dirty == 0) {
1003         /*
1004          * running this resource due to partiallydirty, but this observation's
1005          * notification was already enqueued
1006          */
1007         context->observe_pending = 1;
1008         continue;
1009       }
1010       if (obs->session->con_active >= COAP_NSTART(obs->session) &&
1011           ((r->flags & COAP_RESOURCE_FLAGS_NOTIFY_CON) ||
1012            (obs->non_cnt >= COAP_OBS_MAX_NON))) {
1013         /* Waiting for the previous unsolicited response to finish */
1014         r->partiallydirty = 1;
1015         obs->dirty = 1;
1016         context->observe_pending = 1;
1017         continue;
1018       }
1019       coap_ticks(&now);
1020       if (obs->session->lg_xmit && obs->session->lg_xmit->last_all_sent == 0 &&
1021           obs->session->lg_xmit->last_obs &&
1022           (obs->session->lg_xmit->last_obs + 2*COAP_TICKS_PER_SECOND) > now) {
1023         /* Waiting for the previous blocked unsolicited response to finish */
1024         r->partiallydirty = 1;
1025         obs->dirty = 1;
1026         context->observe_pending = 1;
1027         continue;
1028       }
1029 
1030       coap_mid_t mid = COAP_INVALID_MID;
1031       obs->dirty = 0;
1032       /* initialize response */
1033       response = coap_pdu_init(COAP_MESSAGE_CON, 0, 0, coap_session_max_pdu_size(obs->session));
1034       if (!response) {
1035         obs->dirty = 1;
1036         r->partiallydirty = 1;
1037         context->observe_pending = 1;
1038         coap_log_debug("coap_check_notify: pdu init failed, resource stays "
1039                        "partially dirty\n");
1040         continue;
1041       }
1042 
1043       if (!coap_add_token(response, obs->pdu->actual_token.length,
1044                           obs->pdu->actual_token.s)) {
1045         obs->dirty = 1;
1046         r->partiallydirty = 1;
1047         context->observe_pending = 1;
1048         coap_log_debug("coap_check_notify: cannot add token, resource stays "
1049                        "partially dirty\n");
1050         coap_delete_pdu(response);
1051         continue;
1052       }
1053 
1054       obs->pdu->mid = response->mid = coap_new_message_id(obs->session);
1055       /* A lot of the reliable code assumes type is CON */
1056       if (COAP_PROTO_NOT_RELIABLE(obs->session->proto) &&
1057           (r->flags & COAP_RESOURCE_FLAGS_NOTIFY_CON) == 0 &&
1058           ((r->flags & COAP_RESOURCE_FLAGS_NOTIFY_NON_ALWAYS) ||
1059            obs->non_cnt < COAP_OBS_MAX_NON)) {
1060         response->type = COAP_MESSAGE_NON;
1061       } else {
1062         response->type = COAP_MESSAGE_CON;
1063       }
1064       switch (deleting) {
1065       case COAP_NOT_DELETING_RESOURCE:
1066         /* fill with observer-specific data */
1067         coap_add_option_internal(response, COAP_OPTION_OBSERVE,
1068                                  coap_encode_var_safe(buf, sizeof(buf),
1069                                                       r->observe),
1070                                  buf);
1071         if (coap_get_block_b(obs->session, obs->pdu, COAP_OPTION_BLOCK2,
1072                              &block)) {
1073           /* Will get updated later (e.g. M bit) if appropriate */
1074           coap_add_option_internal(response, COAP_OPTION_BLOCK2,
1075                                    coap_encode_var_safe(buf, sizeof(buf),
1076                                                         ((0 << 4) |
1077                                                          (0 << 3) |
1078                                                          block.aszx)),
1079                                    buf);
1080         }
1081 #if COAP_Q_BLOCK_SUPPORT
1082         else if (coap_get_block_b(obs->session, obs->pdu, COAP_OPTION_Q_BLOCK2,
1083                                   &block)) {
1084           /* Will get updated later (e.g. M bit) if appropriate */
1085           coap_add_option_internal(response, COAP_OPTION_Q_BLOCK2,
1086                                    coap_encode_var_safe(buf, sizeof(buf),
1087                                                         ((0 << 4) |
1088                                                          (0 << 3) |
1089                                                          block.szx)),
1090                                    buf);
1091         }
1092 #endif /* COAP_Q_BLOCK_SUPPORT */
1093 
1094         h = r->handler[obs->pdu->code - 1];
1095         assert(h);      /* we do not allow subscriptions if no
1096                          * GET/FETCH handler is defined */
1097         query = coap_get_query(obs->pdu);
1098         coap_log_debug("Observe PDU presented to app.\n");
1099         coap_show_pdu(COAP_LOG_DEBUG, obs->pdu);
1100         coap_log_debug("call custom handler for resource '%*.*s' (4)\n",
1101                        (int)r->uri_path->length, (int)r->uri_path->length,
1102                        r->uri_path->s);
1103         h(r, obs->session, obs->pdu, query, response);
1104         /* Check if lg_xmit generated and update PDU code if so */
1105         coap_check_code_lg_xmit(obs->session, obs->pdu, response, r, query);
1106         coap_delete_string(query);
1107         if (COAP_RESPONSE_CLASS(response->code) != 2) {
1108           coap_remove_option(response, COAP_OPTION_OBSERVE);
1109         }
1110         if (COAP_RESPONSE_CLASS(response->code) > 2) {
1111           coap_delete_observer(r, obs->session, &obs->pdu->actual_token);
1112           obs = NULL;
1113         }
1114         break;
1115       case COAP_DELETING_RESOURCE:
1116       default:
1117         /* Don't worry if it does not get there */
1118         response->type = COAP_MESSAGE_NON;
1119         response->code = COAP_RESPONSE_CODE(404);
1120         break;
1121       }
1122 
1123       if (obs) {
1124         if (response->type == COAP_MESSAGE_CON ||
1125             (r->flags & COAP_RESOURCE_FLAGS_NOTIFY_NON_ALWAYS)) {
1126           obs->non_cnt = 0;
1127         } else {
1128           obs->non_cnt++;
1129         }
1130 
1131 #if COAP_Q_BLOCK_SUPPORT
1132         if (response->code == COAP_RESPONSE_CODE(205) &&
1133             coap_get_block_b(obs->session, response, COAP_OPTION_Q_BLOCK2,
1134                              &block) &&
1135             block.m) {
1136           query = coap_get_query(obs->pdu);
1137           mid = coap_send_q_block2(obs->session, r, query, obs->pdu->code,
1138                                    block, response, 1);
1139           coap_delete_string(query);
1140           goto finish;
1141         }
1142 #endif /* COAP_Q_BLOCK_SUPPORT */
1143       }
1144       mid = coap_send_internal(obs_session, response);
1145 
1146 #if COAP_Q_BLOCK_SUPPORT
1147 finish:
1148 #endif /* COAP_Q_BLOCK_SUPPORT */
1149       if (COAP_INVALID_MID == mid && obs) {
1150         coap_subscription_t *s;
1151         coap_log_debug("coap_check_notify: sending failed, resource stays "
1152                        "partially dirty\n");
1153         LL_FOREACH(r->subscribers, s) {
1154           if (s == obs) {
1155             /* obs not deleted during coap_send_internal() */
1156             obs->dirty = 1;
1157             break;
1158           }
1159         }
1160         r->partiallydirty = 1;
1161         context->observe_pending = 1;
1162       }
1163     }
1164   }
1165   r->dirty = 0;
1166 }
1167 
1168 int
1169 coap_resource_set_dirty(coap_resource_t *r, const coap_string_t *query) {
1170   return coap_resource_notify_observers(r, query);
1171 }
1172 
1173 int
1174 coap_resource_notify_observers(coap_resource_t *r,
1175                                const coap_string_t *query COAP_UNUSED) {
1176   if (!r->observable)
1177     return 0;
1178   if (!r->subscribers)
1179     return 0;
1180   r->dirty = 1;
1181 
1182   /* Increment value for next Observe use. Observe value must be < 2^24 */
1183   r->observe = (r->observe + 1) & 0xFFFFFF;
1184 
1185   assert(r->context);
1186 
1187   if (r->context->track_observe_value) {
1188     /* Track last used observe value */
1189     if ((r->observe % r->context->observe_save_freq) == 0)
1190       r->context->track_observe_value(r->context, r->uri_path,
1191                                       r->observe,
1192                                       r->context->observe_user_data);
1193   }
1194 
1195   r->context->observe_pending = 1;
1196 #ifdef COAP_EPOLL_SUPPORT
1197   coap_update_epoll_timer(r->context, 0);
1198 #endif /* COAP_EPOLL_SUPPORT */
1199   return 1;
1200 }
1201 
1202 void
1203 coap_resource_set_mode(coap_resource_t *resource, int mode) {
1204   resource->flags = (resource->flags &
1205                      ~(COAP_RESOURCE_FLAGS_NOTIFY_CON|COAP_RESOURCE_FLAGS_NOTIFY_NON)) |
1206                     (mode & (COAP_RESOURCE_FLAGS_NOTIFY_CON|COAP_RESOURCE_FLAGS_NOTIFY_NON));
1207 }
1208 
1209 void
1210 coap_resource_set_userdata(coap_resource_t *resource, void *data) {
1211   resource->user_data = data;
1212 }
1213 
1214 void *
1215 coap_resource_get_userdata(coap_resource_t *resource) {
1216   return resource->user_data;
1217 }
1218 
1219 void
1220 coap_resource_release_userdata_handler(coap_context_t *context,
1221                                        coap_resource_release_userdata_handler_t callback) {
1222   context->release_userdata = callback;
1223 }
1224 
1225 void
1226 coap_resource_set_get_observable(coap_resource_t *resource, int mode) {
1227   resource->observable = mode ? 1 : 0;
1228 }
1229 
1230 coap_str_const_t *
1231 coap_resource_get_uri_path(coap_resource_t *resource) {
1232   if (resource)
1233     return resource->uri_path;
1234   return NULL;
1235 }
1236 
1237 void
1238 coap_check_notify(coap_context_t *context) {
1239 
1240   if (context->observe_pending) {
1241     context->observe_pending = 0;
1242     RESOURCES_ITER(context->resources, r) {
1243       coap_notify_observers(context, r, COAP_NOT_DELETING_RESOURCE);
1244     }
1245   }
1246 }
1247 
1248 void
1249 coap_persist_set_observe_num(coap_resource_t *resource,
1250                              uint32_t start_observe_no) {
1251   if (!resource)
1252     return;
1253 
1254   resource->observe = start_observe_no & 0xffffff;
1255 }
1256 
1257 /**
1258  * Checks the failure counter for (peer, token) and removes peer from
1259  * the list of observers for the given resource when COAP_OBS_MAX_FAIL
1260  * is reached.
1261  *
1262  * @param context  The CoAP context to use
1263  * @param resource The resource to check for (peer, token)
1264  * @param session  The observer's session
1265  * @param token    The token that has been used for subscription.
1266  */
1267 static void
1268 coap_remove_failed_observers(coap_context_t *context,
1269                              coap_resource_t *resource,
1270                              coap_session_t *session,
1271                              const coap_bin_const_t *token) {
1272   coap_subscription_t *obs, *otmp;
1273 
1274   LL_FOREACH_SAFE(resource->subscribers, obs, otmp) {
1275     if (obs->session == session &&
1276         coap_binary_equal(token, &obs->pdu->actual_token)) {
1277       /* count failed notifies and remove when
1278        * COAP_OBS_MAX_FAIL is reached */
1279       obs->fail_cnt++;
1280       if (obs->fail_cnt >= COAP_OBS_MAX_FAIL) {
1281         coap_cancel_all_messages(context, obs->session,
1282                                  &obs->pdu->actual_token);
1283         coap_delete_observer(resource, session, token);
1284       }
1285       break;                        /* break loop if observer was found */
1286     }
1287   }
1288 }
1289 
1290 void
1291 coap_handle_failed_notify(coap_context_t *context,
1292                           coap_session_t *session,
1293                           const coap_bin_const_t *token) {
1294 
1295   RESOURCES_ITER(context->resources, r) {
1296     coap_remove_failed_observers(context, r, session, token);
1297   }
1298 }
1299 
1300 #endif /* ! COAP_SERVER_SUPPORT */
1301