Coverage Report

Created: 2026-05-30 06:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libcoap/src/coap_resource.c
Line
Count
Source
1
/* coap_resource.c -- generic resource handling
2
 *
3
 * Copyright (C) 2010--2026 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_libcoap_build.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
#ifndef min
27
#define min(a,b) ((a) < (b) ? (a) : (b))
28
#endif
29
30
/* Helper functions for conditional output of character sequences into
31
 * a given buffer. The first Offset characters are skipped.
32
 */
33
34
/**
35
 * Adds Char to Buf if Offset is zero. Otherwise, Char is not written
36
 * and Offset is decremented.
37
 */
38
#define PRINT_WITH_OFFSET(Buf,Offset,Char)                \
39
0
  if ((Offset) == 0) {                                        \
40
0
    (*(Buf)++) = (Char);                                \
41
0
  } else {                                                \
42
0
    (Offset)--;                                                \
43
0
  }                                                        \
44
45
/**
46
 * Adds Char to Buf if Offset is zero and Buf is less than Bufend.
47
 */
48
0
#define PRINT_COND_WITH_OFFSET(Buf,Bufend,Offset,Char,Result) {                \
49
0
    if ((Buf) < (Bufend)) {                                                \
50
0
      PRINT_WITH_OFFSET(Buf,Offset,Char);                                \
51
0
    }                                                                        \
52
0
    (Result)++;                                                                \
53
0
  }
54
55
/**
56
 * Copies at most Length characters of Str to Buf. The first Offset
57
 * characters are skipped. Output may be truncated to Bufend - Buf
58
 * characters.
59
 */
60
0
#define COPY_COND_WITH_OFFSET(Buf,Bufend,Offset,Str,Length,Result) {        \
61
0
    size_t i;                                                                \
62
0
    for (i = 0; i < (Length); i++) {                                        \
63
0
      PRINT_COND_WITH_OFFSET((Buf), (Bufend), (Offset), (Str)[i], (Result)); \
64
0
    }                                                                        \
65
0
  }
66
67
static int
68
match(const coap_str_const_t *text, const coap_str_const_t *pattern,
69
0
      int match_prefix, int match_substring) {
70
0
  assert(text);
71
0
  assert(pattern);
72
73
0
  if (text->length < pattern->length || !pattern->s)
74
0
    return 0;
75
76
0
  if (match_substring) {
77
0
    const uint8_t *next_token = text->s;
78
0
    size_t remaining_length = text->length;
79
0
    while (remaining_length) {
80
0
      size_t token_length;
81
0
      const uint8_t *token = next_token;
82
0
      next_token = (unsigned char *)memchr(token, ' ', remaining_length);
83
84
0
      if (next_token) {
85
0
        token_length = next_token - token;
86
0
        remaining_length -= (token_length + 1);
87
0
        next_token++;
88
0
      } else {
89
0
        token_length = remaining_length;
90
0
        remaining_length = 0;
91
0
      }
92
93
0
      if ((match_prefix || pattern->length == token_length) &&
94
0
          memcmp(token, pattern->s, pattern->length) == 0)
95
0
        return 1;
96
0
    }
97
0
    return 0;
98
0
  }
99
100
0
  return (match_prefix || pattern->length == text->length) &&
101
0
         memcmp(text->s, pattern->s, pattern->length) == 0;
102
0
}
103
104
COAP_API coap_print_status_t
105
coap_print_wellknown(coap_context_t *context, unsigned char *buf,
106
                     size_t *buflen, size_t offset,
107
0
                     const coap_string_t *query_filter) {
108
0
  coap_print_status_t result;
109
0
  coap_lock_lock(return COAP_PRINT_STATUS_ERROR);
110
0
  result = coap_print_wellknown_lkd(context, buf, buflen, offset, query_filter);
111
0
  coap_lock_unlock();
112
0
  return result;
113
0
}
114
115
static coap_str_const_t coap_default_uri_wellknown = {
116
  sizeof(COAP_DEFAULT_URI_WELLKNOWN)-1,
117
  (const uint8_t *)COAP_DEFAULT_URI_WELLKNOWN
118
};
119
120
coap_print_status_t
121
coap_print_wellknown_lkd(coap_context_t *context, unsigned char *buf,
122
                         size_t *buflen, size_t offset,
123
0
                         const coap_string_t *query_filter) {
124
0
  coap_print_status_t output_length = 0;
125
0
  unsigned char *p = buf;
126
0
  const uint8_t *bufend = buf + *buflen;
127
0
  size_t left, written = 0;
128
0
  coap_print_status_t result;
129
0
  const size_t old_offset = offset;
130
0
  int subsequent_resource = 0;
131
#ifdef WITHOUT_QUERY_FILTER
132
  (void)query_filter;
133
#else
134
0
  coap_str_const_t resource_param = { 0, NULL }, query_pattern = { 0, NULL };
135
0
  int flags = 0; /* MATCH_SUBSTRING, MATCH_PREFIX, MATCH_URI */
136
0
#define MATCH_URI       0x01
137
0
#define MATCH_PREFIX    0x02
138
0
#define MATCH_SUBSTRING 0x04
139
0
  static const coap_str_const_t _rt_attributes[] = {
140
0
    {2, (const uint8_t *)"rt"},
141
0
    {2, (const uint8_t *)"if"},
142
0
    {3, (const uint8_t *)"rel"},
143
0
    {0, NULL}
144
0
  };
145
0
#endif /* WITHOUT_QUERY_FILTER */
146
147
0
  coap_lock_check_locked();
148
0
#ifndef WITHOUT_QUERY_FILTER
149
  /* split query filter, if any */
150
0
  if (query_filter) {
151
0
    resource_param.s = query_filter->s;
152
0
    while (resource_param.length < query_filter->length &&
153
0
           resource_param.s[resource_param.length] != '=')
154
0
      resource_param.length++;
155
156
0
    if (resource_param.length < query_filter->length) {
157
0
      const coap_str_const_t *rt_attributes;
158
0
      if (resource_param.length == 4 &&
159
0
          memcmp(resource_param.s, "href", 4) == 0)
160
0
        flags |= MATCH_URI;
161
162
0
      for (rt_attributes = _rt_attributes; rt_attributes->s; rt_attributes++) {
163
0
        if (resource_param.length == rt_attributes->length &&
164
0
            memcmp(resource_param.s, rt_attributes->s, rt_attributes->length) == 0) {
165
0
          flags |= MATCH_SUBSTRING;
166
0
          break;
167
0
        }
168
0
      }
169
170
      /* rest is query-pattern */
171
0
      query_pattern.s =
172
0
          query_filter->s + resource_param.length + 1;
173
174
0
      assert((resource_param.length + 1) <= query_filter->length);
175
0
      query_pattern.length =
176
0
          query_filter->length - (resource_param.length + 1);
177
178
0
      if (query_pattern.length &&
179
0
          (query_pattern.s[0] == '/') && ((flags & MATCH_URI) == MATCH_URI)) {
180
0
        query_pattern.s++;
181
0
        query_pattern.length--;
182
0
      }
183
184
0
      if (query_pattern.length &&
185
0
          query_pattern.s[query_pattern.length-1] == '*') {
186
0
        query_pattern.length--;
187
0
        flags |= MATCH_PREFIX;
188
0
      }
189
0
    }
190
0
  }
191
0
#endif /* WITHOUT_QUERY_FILTER */
192
193
0
  RESOURCES_ITER(context->resources, r) {
194
195
0
    if (coap_string_equal(r->uri_path, &coap_default_uri_wellknown)) {
196
      /* server app has defined a resource for .well-known/core - ignore */
197
0
      continue;
198
0
    }
199
0
    if (r->flags & COAP_RESOURCE_HIDE_WELLKNOWN_CORE) {
200
0
      continue;
201
0
    }
202
0
#ifndef WITHOUT_QUERY_FILTER
203
0
    if (resource_param.length) { /* there is a query filter */
204
205
0
      if (flags & MATCH_URI) {        /* match resource URI */
206
0
        if (!match(r->uri_path, &query_pattern, (flags & MATCH_PREFIX) != 0,
207
0
                   (flags & MATCH_SUBSTRING) != 0))
208
0
          continue;
209
0
      } else {                        /* match attribute */
210
0
        coap_attr_t *attr;
211
0
        coap_str_const_t unquoted_val;
212
0
        attr = coap_find_attr(r, &resource_param);
213
0
        if (!attr || !attr->value)
214
0
          continue;
215
0
        unquoted_val = *attr->value;
216
0
        if (attr->value->s[0] == '"') {          /* if attribute has a quoted value, remove double quotes */
217
0
          unquoted_val.length -= 2;
218
0
          unquoted_val.s += 1;
219
0
        }
220
0
        if (!(match(&unquoted_val, &query_pattern,
221
0
                    (flags & MATCH_PREFIX) != 0,
222
0
                    (flags & MATCH_SUBSTRING) != 0)))
223
0
          continue;
224
0
      }
225
0
    }
226
0
#endif /* WITHOUT_QUERY_FILTER */
227
228
0
    if (!subsequent_resource) {        /* this is the first resource  */
229
0
      subsequent_resource = 1;
230
0
    } else {
231
0
      PRINT_COND_WITH_OFFSET(p, bufend, offset, ',', written);
232
0
    }
233
234
0
    left = bufend - p; /* calculate available space */
235
0
    result = coap_print_link(r, p, &left, &offset);
236
237
0
    if (result & COAP_PRINT_STATUS_ERROR) {
238
0
      break;
239
0
    }
240
241
    /* coap_print_link() returns the number of characters that
242
     * where actually written to p. Now advance to its end. */
243
0
    p += COAP_PRINT_OUTPUT_LENGTH(result);
244
0
    written += left;
245
0
  }
246
247
0
  *buflen = written;
248
0
  output_length = (coap_print_status_t)(p - buf);
249
250
0
  if (output_length > COAP_PRINT_STATUS_MAX) {
251
0
    return COAP_PRINT_STATUS_ERROR;
252
0
  }
253
254
0
  result = (coap_print_status_t)output_length;
255
256
0
  if (result + old_offset - offset < *buflen) {
257
0
    result |= COAP_PRINT_STATUS_TRUNC;
258
0
  }
259
0
  return result;
260
0
}
261
262
static coap_str_const_t null_path_value = {0, (const uint8_t *)""};
263
static coap_str_const_t *null_path = &null_path_value;
264
265
coap_resource_t *
266
0
coap_resource_init(coap_str_const_t *uri_path, int flags) {
267
0
  coap_resource_t *r;
268
269
0
  r = (coap_resource_t *)coap_malloc_type(COAP_RESOURCE, sizeof(coap_resource_t));
270
0
  if (r) {
271
0
    memset(r, 0, sizeof(coap_resource_t));
272
#if COAP_THREAD_SAFE
273
    coap_lock_init(&r->lock);
274
#endif /* COAP_THREAD_SAFE */
275
276
0
    if (!(flags & COAP_RESOURCE_FLAGS_RELEASE_URI)) {
277
      /* Need to take a copy if caller is not providing a release request */
278
0
      if (uri_path)
279
0
        uri_path = coap_new_str_const(uri_path->s, uri_path->length);
280
0
      else
281
0
        uri_path = coap_new_str_const(null_path->s, null_path->length);
282
0
    } else if (!uri_path) {
283
      /* Do not expect this, but ... */
284
0
      uri_path = coap_new_str_const(null_path->s, null_path->length);
285
0
    }
286
287
0
    if (uri_path)
288
0
      r->uri_path = uri_path;
289
290
0
    r->flags = flags;
291
0
    r->observe = 2;
292
0
  } else {
293
0
    coap_log_debug("coap_resource_init: no memory left\n");
294
0
  }
295
296
0
  return r;
297
0
}
298
299
static const uint8_t coap_unknown_resource_uri[] =
300
    "- Unknown -";
301
302
coap_resource_t *
303
0
coap_resource_unknown_init2(coap_method_handler_t put_handler, int flags) {
304
0
  coap_resource_t *r;
305
306
0
  r = (coap_resource_t *)coap_malloc_type(COAP_RESOURCE, sizeof(coap_resource_t));
307
0
  if (r) {
308
0
    memset(r, 0, sizeof(coap_resource_t));
309
#if COAP_THREAD_SAFE
310
    coap_lock_init(&r->lock);
311
#endif /* COAP_THREAD_SAFE */
312
0
    r->is_unknown = 1;
313
    /* Something unlikely to be used, but it shows up in the logs */
314
0
    r->uri_path = coap_new_str_const(coap_unknown_resource_uri, sizeof(coap_unknown_resource_uri)-1);
315
0
    r->flags = flags & ~COAP_RESOURCE_FLAGS_RELEASE_URI;
316
0
    coap_register_handler(r, COAP_REQUEST_PUT, put_handler);
317
0
  } else {
318
0
    coap_log_debug("coap_resource_unknown_init2: no memory left\n");
319
0
  }
320
321
0
  return r;
322
0
}
323
324
coap_resource_t *
325
0
coap_resource_unknown_init(coap_method_handler_t put_handler) {
326
0
  return coap_resource_unknown_init2(put_handler, 0);
327
0
}
328
329
static const uint8_t coap_proxy_resource_uri[] =
330
    "- Proxy URI -";
331
332
coap_resource_t *
333
coap_resource_proxy_uri_init2(coap_method_handler_t handler,
334
                              size_t host_name_count,
335
0
                              const char *host_name_list[], int flags) {
336
0
  coap_resource_t *r;
337
338
0
  r = (coap_resource_t *)coap_malloc_type(COAP_RESOURCE, sizeof(coap_resource_t));
339
0
  if (r) {
340
0
    size_t i;
341
0
    memset(r, 0, sizeof(coap_resource_t));
342
#if COAP_THREAD_SAFE
343
    coap_lock_init(&r->lock);
344
#endif /* COAP_THREAD_SAFE */
345
0
    r->is_proxy_uri = 1;
346
    /* Something unlikely to be used, but it shows up in the logs */
347
0
    r->uri_path = coap_new_str_const(coap_proxy_resource_uri, sizeof(coap_proxy_resource_uri)-1);
348
    /* Preset all the handlers */
349
0
    for (i = 0; i < (sizeof(r->handler) / sizeof(r->handler[0])); i++) {
350
0
      r->handler[i] = handler;
351
0
    }
352
0
    if (host_name_count) {
353
0
      r->proxy_name_list = coap_malloc_type(COAP_STRING, host_name_count *
354
0
                                            sizeof(coap_str_const_t *));
355
0
      if (r->proxy_name_list) {
356
0
        for (i = 0; i < host_name_count; i++) {
357
0
          r->proxy_name_list[i] =
358
0
              coap_new_str_const((const uint8_t *)host_name_list[i],
359
0
                                 strlen(host_name_list[i]));
360
0
          if (!r->proxy_name_list[i]) {
361
0
            coap_log_err("coap_resource_proxy_uri_init: unable to add host name\n");
362
0
            if (i == 0) {
363
0
              coap_free_type(COAP_STRING, r->proxy_name_list);
364
0
              r->proxy_name_list = NULL;
365
0
            }
366
0
            break;
367
0
          }
368
0
        }
369
0
        r->proxy_name_count = i;
370
0
      }
371
0
    }
372
0
    r->flags = flags & ~COAP_RESOURCE_FLAGS_RELEASE_URI;
373
0
  } else {
374
0
    coap_log_debug("coap_resource_proxy_uri_init2: no memory left\n");
375
0
  }
376
377
0
  return r;
378
0
}
379
380
coap_resource_t *
381
coap_resource_proxy_uri_init(coap_method_handler_t handler,
382
0
                             size_t host_name_count, const char *host_name_list[]) {
383
0
  return coap_resource_proxy_uri_init2(handler, host_name_count,
384
0
                                       host_name_list, 0);
385
0
}
386
387
static const uint8_t coap_rev_proxy_resource_uri[] =
388
    "- Rev Proxy -";
389
390
coap_resource_t *
391
0
coap_resource_reverse_proxy_init(coap_method_handler_t handler, int flags) {
392
0
  coap_resource_t *r;
393
394
0
  r = (coap_resource_t *)coap_malloc_type(COAP_RESOURCE, sizeof(coap_resource_t));
395
0
  if (r) {
396
0
    memset(r, 0, sizeof(coap_resource_t));
397
#if COAP_THREAD_SAFE
398
    coap_lock_init(&r->lock);
399
#endif /* COAP_THREAD_SAFE */
400
0
    r->is_unknown = 1;
401
0
    r->is_reverse_proxy = 1;
402
    /* Something unlikely to be used, but it shows up in the logs */
403
0
    r->uri_path = coap_new_str_const(coap_rev_proxy_resource_uri,
404
0
                                     sizeof(coap_rev_proxy_resource_uri)-1);
405
0
    r->flags = flags & ~COAP_RESOURCE_FLAGS_RELEASE_URI;
406
0
    r->flags |= COAP_RESOURCE_HANDLE_WELLKNOWN_CORE;
407
0
    coap_register_handler(r, COAP_REQUEST_PUT, handler);
408
0
    coap_register_handler(r, COAP_REQUEST_GET, handler);
409
0
    coap_register_handler(r, COAP_REQUEST_POST, handler);
410
0
    coap_register_handler(r, COAP_REQUEST_DELETE, handler);
411
0
    coap_register_handler(r, COAP_REQUEST_FETCH, handler);
412
0
    coap_register_handler(r, COAP_REQUEST_PATCH, handler);
413
0
    coap_register_handler(r, COAP_REQUEST_IPATCH, handler);
414
0
  } else {
415
0
    coap_log_debug("coap_resource_rev_proxy_init: no memory left\n");
416
0
  }
417
418
0
  return r;
419
0
}
420
421
coap_attr_t *
422
coap_add_attr(coap_resource_t *resource,
423
              coap_str_const_t *name,
424
              coap_str_const_t *val,
425
0
              int flags) {
426
0
  coap_attr_t *attr;
427
428
0
  if (!resource || !name)
429
0
    return NULL;
430
0
  attr = (coap_attr_t *)coap_malloc_type(COAP_RESOURCEATTR, sizeof(coap_attr_t));
431
432
0
  if (attr) {
433
0
    if (!(flags & COAP_ATTR_FLAGS_RELEASE_NAME)) {
434
      /* Need to take a copy if caller is not providing a release request */
435
0
      name = coap_new_str_const(name->s, name->length);
436
0
    }
437
0
    attr->name = name;
438
0
    if (val) {
439
0
      if (!(flags & COAP_ATTR_FLAGS_RELEASE_VALUE)) {
440
        /* Need to take a copy if caller is not providing a release request */
441
0
        val = coap_new_str_const(val->s, val->length);
442
0
      }
443
0
    }
444
0
    attr->value = val;
445
446
0
    attr->flags = flags;
447
448
    /* add attribute to resource list */
449
0
    LL_PREPEND(resource->link_attr, attr);
450
0
  } else {
451
0
    coap_log_debug("coap_add_attr: no memory left\n");
452
0
  }
453
454
0
  return attr;
455
0
}
456
457
coap_attr_t *
458
coap_find_attr(coap_resource_t *resource,
459
0
               coap_str_const_t *name) {
460
0
  coap_attr_t *attr;
461
462
0
  if (!resource || !name)
463
0
    return NULL;
464
465
0
  LL_FOREACH(resource->link_attr, attr) {
466
0
    if (attr->name->length == name->length &&
467
0
        memcmp(attr->name->s, name->s, name->length) == 0)
468
0
      return attr;
469
0
  }
470
471
0
  return NULL;
472
0
}
473
474
coap_str_const_t *
475
0
coap_attr_get_value(coap_attr_t *attr) {
476
0
  if (attr)
477
0
    return attr->value;
478
0
  return NULL;
479
0
}
480
481
void
482
0
coap_delete_attr(coap_attr_t *attr) {
483
0
  if (!attr)
484
0
    return;
485
0
  coap_delete_str_const(attr->name);
486
0
  if (attr->value) {
487
0
    coap_delete_str_const(attr->value);
488
0
  }
489
490
0
  coap_free_type(COAP_RESOURCEATTR, attr);
491
0
}
492
493
static void coap_notify_observers(coap_context_t *context, coap_resource_t *r,
494
                                  coap_deleting_resource_t deleting);
495
496
static void
497
0
coap_free_resource(coap_resource_t *resource, coap_deleting_resource_t deleting) {
498
0
  coap_attr_t *attr, *tmp;
499
0
  coap_subscription_t *obs, *otmp;
500
0
  coap_context_t *context;
501
502
0
  assert(resource);
503
504
0
  context = resource->context;
505
0
  if (context) {
506
0
    if (!context->observe_no_clear) {
507
0
      coap_resource_notify_observers_lkd(resource, deleting);
508
0
      coap_notify_observers(context, resource, deleting);
509
0
    }
510
511
0
    if (context->resource_deleted_cb)
512
0
      coap_lock_callback(context->resource_deleted_cb(context,
513
0
                                                      resource->uri_path,
514
0
                                                      context->observe_user_data));
515
516
0
    if (context->release_userdata_cb && resource->user_data) {
517
0
      coap_lock_callback(context->release_userdata_cb(resource->user_data));
518
0
    }
519
0
  }
520
521
  /* delete registered attributes */
522
0
  LL_FOREACH_SAFE(resource->link_attr, attr, tmp) coap_delete_attr(attr);
523
524
  /* Either the application provided or libcoap copied - need to delete it */
525
0
  coap_delete_str_const(resource->uri_path);
526
527
  /* free all elements from resource->subscribers */
528
0
  LL_FOREACH_SAFE(resource->subscribers, obs, otmp) {
529
0
    coap_delete_observer_internal(resource, obs->session, obs);
530
0
  }
531
0
  if (resource->proxy_name_count && resource->proxy_name_list) {
532
0
    size_t i;
533
534
0
    for (i = 0; i < resource->proxy_name_count; i++) {
535
0
      coap_delete_str_const(resource->proxy_name_list[i]);
536
0
    }
537
0
    coap_free_type(COAP_STRING, resource->proxy_name_list);
538
0
  }
539
540
0
  coap_free_type(COAP_RESOURCE, resource);
541
0
}
542
543
COAP_API void
544
0
coap_add_resource(coap_context_t *context, coap_resource_t *resource) {
545
0
  coap_lock_lock(return);
546
0
  coap_add_resource_lkd(context, resource);
547
0
  coap_lock_unlock();
548
0
}
549
550
void
551
0
coap_add_resource_lkd(coap_context_t *context, coap_resource_t *resource) {
552
0
  coap_lock_check_locked();
553
0
  if (resource->is_unknown) {
554
0
    if (context->unknown_resource)
555
0
      coap_free_resource(context->unknown_resource, COAP_DELETING_RESOURCE);
556
0
    context->unknown_resource = resource;
557
0
  } else if (resource->is_proxy_uri) {
558
0
    if (context->proxy_uri_resource)
559
0
      coap_free_resource(context->proxy_uri_resource, COAP_DELETING_RESOURCE);
560
0
    context->proxy_uri_resource = resource;
561
0
  } else {
562
0
    coap_resource_t *r = coap_get_resource_from_uri_path_lkd(context,
563
0
                                                             resource->uri_path);
564
565
0
    if (r) {
566
0
      coap_log_warn("coap_add_resource: Duplicate uri_path '%*.*s', old resource deleted\n",
567
0
                    (int)resource->uri_path->length, (int)resource->uri_path->length,
568
0
                    resource->uri_path->s);
569
0
      coap_delete_resource_lkd(r);
570
0
    }
571
0
    RESOURCES_ADD(context->resources, resource);
572
0
#if COAP_WITH_OBSERVE_PERSIST
573
0
    if (context->unknown_pdu && context->dyn_resource_save_file &&
574
0
        context->dyn_resource_added_cb && resource->observable) {
575
0
      coap_bin_const_t raw_packet;
576
577
0
      raw_packet.s = context->unknown_pdu->token -
578
0
                     context->unknown_pdu->hdr_size;
579
0
      raw_packet.length = context->unknown_pdu->used_size +
580
0
                          context->unknown_pdu->hdr_size;
581
0
      coap_lock_callback(context->dyn_resource_added_cb(context->unknown_session,
582
0
                                                        resource->uri_path,
583
0
                                                        &raw_packet,
584
0
                                                        context->observe_user_data));
585
0
    }
586
0
#endif /* COAP_WITH_OBSERVE_PERSIST */
587
0
  }
588
0
  assert(resource->context == NULL);
589
0
  resource->context = context;
590
0
}
591
592
COAP_API int
593
0
coap_delete_resource(coap_context_t *context, coap_resource_t *resource) {
594
0
  int ret;
595
596
0
  (void)context;
597
0
  if (!resource)
598
0
    return 0;
599
600
0
  coap_lock_lock(return 0);
601
0
  ret = coap_delete_resource_lkd(resource);
602
0
  coap_lock_unlock();
603
0
  return ret;
604
0
}
605
606
/*
607
 * Input context is ignored, but param left there to keep API consistent
608
 */
609
int
610
0
coap_delete_resource_lkd(coap_resource_t *resource) {
611
0
  coap_context_t *context;
612
0
  coap_deleting_resource_t deleting;
613
614
0
  if (!resource)
615
0
    return 0;
616
617
0
  context = resource->context;
618
0
  coap_lock_check_locked();
619
620
0
  if (resource->ref) {
621
0
    resource->ref--;
622
0
    return 1;
623
0
  }
624
0
  if (context && context->context_going_away) {
625
0
    deleting = COAP_DELETING_RESOURCE_ON_EXIT;
626
0
  } else {
627
0
    deleting = COAP_DELETING_RESOURCE;
628
0
  }
629
0
  if (resource->is_unknown) {
630
0
    if (context && context->unknown_resource == resource) {
631
0
      context->unknown_resource = NULL;
632
0
    }
633
0
  } else if (resource->is_proxy_uri) {
634
0
    if (context && context->proxy_uri_resource == resource) {
635
0
      context->proxy_uri_resource = NULL;
636
0
    }
637
0
  } else if (context) {
638
    /* remove resource from list */
639
0
    RESOURCES_DELETE(context->resources, resource);
640
0
  }
641
0
  if (resource->is_dynamic) {
642
0
    if (context) {
643
0
      assert(context->dynamic_cur);
644
0
      context->dynamic_cur--;
645
0
    }
646
0
  }
647
648
  /* and free its allocated memory */
649
0
  coap_free_resource(resource, deleting);
650
651
0
  return 1;
652
0
}
653
654
void
655
0
coap_delete_all_resources(coap_context_t *context) {
656
0
  coap_resource_t *r;
657
0
  coap_resource_t *tmp;
658
659
0
  RESOURCE_ITER_SAFE(context->resources, r, tmp) {
660
0
    coap_delete_resource_lkd(r);
661
0
  }
662
663
0
  context->resources = NULL;
664
665
0
  if (context->unknown_resource) {
666
0
    coap_delete_resource_lkd(context->unknown_resource);
667
0
    context->unknown_resource = NULL;
668
0
  }
669
0
  if (context->proxy_uri_resource) {
670
0
    coap_delete_resource_lkd(context->proxy_uri_resource);
671
0
    context->proxy_uri_resource = NULL;
672
0
  }
673
0
}
674
675
COAP_API coap_resource_t *
676
0
coap_get_resource_from_uri_path(coap_context_t *context, coap_str_const_t *uri_path) {
677
0
  coap_resource_t *result;
678
679
0
  coap_lock_lock(return NULL);
680
0
  result = coap_get_resource_from_uri_path_lkd(context, uri_path);
681
0
  coap_lock_unlock();
682
683
0
  return result;
684
0
}
685
686
coap_resource_t *
687
coap_get_resource_from_uri_path_lkd(coap_context_t *context,
688
0
                                    coap_str_const_t *uri_path) {
689
0
  coap_resource_t *result;
690
691
0
  coap_lock_check_locked();
692
693
0
  RESOURCES_FIND(context->resources, uri_path, result);
694
695
0
  return result;
696
0
}
697
698
coap_print_status_t
699
coap_print_link(const coap_resource_t *resource,
700
0
                unsigned char *buf, size_t *len, size_t *offset) {
701
0
  unsigned char *p = buf;
702
0
  const uint8_t *bufend = buf + *len;
703
0
  coap_attr_t *attr;
704
0
  coap_print_status_t result = 0;
705
0
  coap_print_status_t output_length = 0;
706
0
  const size_t old_offset = *offset;
707
708
0
  *len = 0;
709
0
  PRINT_COND_WITH_OFFSET(p, bufend, *offset, '<', *len);
710
0
  PRINT_COND_WITH_OFFSET(p, bufend, *offset, '/', *len);
711
712
0
  COPY_COND_WITH_OFFSET(p, bufend, *offset,
713
0
                        resource->uri_path->s, resource->uri_path->length, *len);
714
715
0
  PRINT_COND_WITH_OFFSET(p, bufend, *offset, '>', *len);
716
717
0
  LL_FOREACH(resource->link_attr, attr) {
718
719
0
    PRINT_COND_WITH_OFFSET(p, bufend, *offset, ';', *len);
720
721
0
    COPY_COND_WITH_OFFSET(p, bufend, *offset,
722
0
                          attr->name->s, attr->name->length, *len);
723
724
0
    if (attr->value && attr->value->s) {
725
0
      PRINT_COND_WITH_OFFSET(p, bufend, *offset, '=', *len);
726
727
0
      COPY_COND_WITH_OFFSET(p, bufend, *offset,
728
0
                            attr->value->s, attr->value->length, *len);
729
0
    }
730
731
0
  }
732
0
  if (resource->observable) {
733
0
    COPY_COND_WITH_OFFSET(p, bufend, *offset, ";obs", 4, *len);
734
0
  }
735
736
0
#if COAP_OSCORE_SUPPORT
737
  /* If oscore is enabled */
738
0
  if (resource->flags & COAP_RESOURCE_FLAGS_OSCORE_ONLY)
739
0
    COPY_COND_WITH_OFFSET(p, bufend, *offset, ";osc", 4, *len);
740
0
#endif /* COAP_OSCORE_SUPPORT */
741
742
0
  output_length = (coap_print_status_t)(p - buf);
743
744
0
  if (output_length > COAP_PRINT_STATUS_MAX) {
745
0
    return COAP_PRINT_STATUS_ERROR;
746
0
  }
747
748
0
  result = (coap_print_status_t)output_length;
749
750
0
  if (result + old_offset - *offset < *len) {
751
0
    result |= COAP_PRINT_STATUS_TRUNC;
752
0
  }
753
754
0
  return result;
755
0
}
756
757
void
758
coap_register_handler(coap_resource_t *resource,
759
                      coap_request_t method,
760
0
                      coap_method_handler_t handler) {
761
0
  coap_register_request_handler(resource, method, handler);
762
0
}
763
764
void
765
coap_register_request_handler(coap_resource_t *resource,
766
                              coap_request_t method,
767
0
                              coap_method_handler_t handler) {
768
0
  assert(resource);
769
0
  assert(method > 0 && (size_t)(method-1) <
770
0
         sizeof(resource->handler)/sizeof(coap_method_handler_t));
771
0
  resource->handler[method-1] = handler;
772
0
}
773
774
coap_subscription_t *
775
coap_find_observer(coap_resource_t *resource, coap_session_t *session,
776
0
                   const coap_bin_const_t *token) {
777
0
  coap_subscription_t *s;
778
779
0
  assert(resource);
780
0
  assert(session);
781
782
0
  LL_FOREACH(resource->subscribers, s) {
783
0
    if (s->session == session &&
784
0
        (!token || coap_binary_equal(token, &s->pdu->actual_token)))
785
0
      return s;
786
0
  }
787
788
0
  return NULL;
789
0
}
790
791
static coap_subscription_t *
792
coap_find_observer_cache_key(coap_resource_t *resource, coap_session_t *session,
793
0
                             const coap_cache_key_t *cache_key) {
794
0
  coap_subscription_t *s;
795
796
0
  assert(resource);
797
0
  assert(session);
798
799
0
  LL_FOREACH(resource->subscribers, s) {
800
0
    if (s->session == session
801
0
        && (memcmp(cache_key, s->cache_key, sizeof(coap_cache_key_t)) == 0))
802
0
      return s;
803
0
  }
804
805
0
  return NULL;
806
0
}
807
808
/* https://rfc-editor.org/rfc/rfc7641#section-3.6 */
809
static const uint16_t cache_ignore_options[] = { COAP_OPTION_ETAG,
810
                                                 COAP_OPTION_OSCORE
811
                                               };
812
coap_subscription_t *
813
coap_add_observer(coap_resource_t *resource,
814
                  coap_session_t *session,
815
                  const coap_bin_const_t *token,
816
0
                  const coap_pdu_t *request) {
817
0
  coap_subscription_t *s;
818
0
  coap_cache_key_t *cache_key = NULL;
819
0
  size_t len;
820
0
  const uint8_t *data;
821
822
0
  assert(session);
823
824
  /* Check if there is already a subscription for this peer. */
825
0
  s = coap_find_observer(resource, session, token);
826
0
  if (!s) {
827
    /*
828
     * Cannot allow a duplicate to be created for the same query as application
829
     * may not be cleaning up duplicates.  If duplicate found, then original
830
     * observer is deleted and a new one created with the new token
831
     */
832
0
    cache_key = coap_cache_derive_key_w_ignore(session, request,
833
0
                                               COAP_CACHE_IS_SESSION_BASED,
834
0
                                               cache_ignore_options,
835
0
                                               sizeof(cache_ignore_options)/sizeof(cache_ignore_options[0]));
836
0
    if (cache_key) {
837
0
      s = coap_find_observer_cache_key(resource, session, cache_key);
838
0
      if (s) {
839
        /* Delete old entry with old token */
840
0
        coap_delete_observer(resource, session, &s->pdu->actual_token);
841
0
        s = NULL;
842
0
      }
843
0
    }
844
0
  }
845
846
  /* We are done if subscription was found. */
847
0
  if (s) {
848
0
    return s;
849
0
  }
850
851
  /* Check if there is already maximum number of subscribers present */
852
#if (COAP_RESOURCE_MAX_SUBSCRIBER > 0)
853
  uint32_t subscriber_count = 0;
854
  LL_COUNT(resource->subscribers, s, subscriber_count);
855
  if (subscriber_count >= COAP_RESOURCE_MAX_SUBSCRIBER) {
856
    return NULL; /* Signal error */
857
  }
858
#endif /* COAP_RESOURCE_MAX_SUBSCRIBER */
859
860
  /* Create a new subscription */
861
0
  s = coap_malloc_type(COAP_SUBSCRIPTION, sizeof(coap_subscription_t));
862
863
0
  if (!s) {
864
0
    coap_delete_cache_key(cache_key);
865
0
    return NULL;
866
0
  }
867
868
0
  coap_subscription_init(s);
869
0
  s->pdu = coap_pdu_duplicate_lkd(request, session, token->length,
870
0
                                  token->s, NULL, COAP_BOOL_FALSE);
871
0
  if (s->pdu == NULL) {
872
0
    coap_delete_cache_key(cache_key);
873
0
    coap_free_type(COAP_SUBSCRIPTION, s);
874
0
    return NULL;
875
0
  }
876
0
  if (coap_get_data(request, &len, &data)) {
877
    /* This could be a large bodied FETCH */
878
0
    s->pdu->max_size = 0;
879
0
    coap_add_data(s->pdu, len, data);
880
0
  }
881
0
  if (cache_key == NULL) {
882
0
    cache_key = coap_cache_derive_key_w_ignore(session, request,
883
0
                                               COAP_CACHE_IS_SESSION_BASED,
884
0
                                               cache_ignore_options,
885
0
                                               sizeof(cache_ignore_options)/sizeof(cache_ignore_options[0]));
886
0
    if (cache_key == NULL) {
887
0
      coap_delete_pdu_lkd(s->pdu);
888
0
      coap_delete_cache_key(cache_key);
889
0
      coap_free_type(COAP_SUBSCRIPTION, s);
890
0
      return NULL;
891
0
    }
892
0
  }
893
0
  s->cache_key = cache_key;
894
0
  s->session = coap_session_reference_lkd(session);
895
0
  session->ref_subscriptions++;
896
897
  /* add subscriber to resource */
898
0
  LL_PREPEND(resource->subscribers, s);
899
900
0
  coap_log_debug("create new subscription %p key 0x%02x%02x%02x%02x\n",
901
0
                 (void *)s, s->cache_key->key[0], s->cache_key->key[1],
902
0
                 s->cache_key->key[2], s->cache_key->key[3]);
903
904
0
  if (session->context->observe_added_cb && session->proto == COAP_PROTO_UDP &&
905
0
      !coap_is_af_unix(&session->addr_info.local)) {
906
0
    coap_bin_const_t raw_packet;
907
0
    coap_bin_const_t *oscore_info = NULL;
908
0
#if COAP_OSCORE_SUPPORT
909
0
    oscore_association_t *association;
910
911
0
    if (session->recipient_ctx && session->recipient_ctx->recipient_id) {
912
      /*
913
       * Need to track the association used for tracking this observe, done as
914
       * a CBOR array. Read in coap_persist_observe_add().
915
       *
916
       * If an entry is null, then use nil, else a set of bytes
917
       *
918
       * Currently tracking 5 items
919
       *  recipient_id
920
       *  id_context
921
       *  aad        (from oscore_association_t)
922
       *  partial_iv (from oscore_association_t)
923
       *  nonce      (from oscore_association_t)
924
       */
925
0
      uint8_t info_buffer[60];
926
0
      uint8_t *info_buf = info_buffer;
927
0
      size_t info_len = sizeof(info_buffer);
928
0
      size_t ret = 0;
929
0
      coap_bin_const_t ctoken = { token->length, token->s };
930
931
0
      ret += oscore_cbor_put_array(&info_buf, &info_len, 5);
932
0
      ret += oscore_cbor_put_bytes(&info_buf,
933
0
                                   &info_len,
934
0
                                   session->recipient_ctx->recipient_id->s,
935
0
                                   session->recipient_ctx->recipient_id->length);
936
0
      if (session->recipient_ctx->osc_ctx &&
937
0
          session->recipient_ctx->osc_ctx->id_context) {
938
0
        ret += oscore_cbor_put_bytes(&info_buf,
939
0
                                     &info_len,
940
0
                                     session->recipient_ctx->osc_ctx->id_context->s,
941
0
                                     session->recipient_ctx->osc_ctx->id_context->length);
942
0
      } else {
943
0
        ret += oscore_cbor_put_nil(&info_buf, &info_len);
944
0
      }
945
0
      association = oscore_find_association(session, &ctoken);
946
0
      if (association) {
947
0
        if (association->aad) {
948
0
          ret += oscore_cbor_put_bytes(&info_buf,
949
0
                                       &info_len,
950
0
                                       association->aad->s,
951
0
                                       association->aad->length);
952
0
        } else {
953
0
          ret += oscore_cbor_put_nil(&info_buf, &info_len);
954
0
        }
955
0
        if (association->partial_iv) {
956
0
          ret += oscore_cbor_put_bytes(&info_buf,
957
0
                                       &info_len,
958
0
                                       association->partial_iv->s,
959
0
                                       association->partial_iv->length);
960
0
        } else {
961
0
          ret += oscore_cbor_put_nil(&info_buf, &info_len);
962
0
        }
963
0
        if (association->nonce) {
964
0
          ret += oscore_cbor_put_bytes(&info_buf,
965
0
                                       &info_len,
966
0
                                       association->nonce->s,
967
0
                                       association->nonce->length);
968
0
        } else {
969
0
          ret += oscore_cbor_put_nil(&info_buf, &info_len);
970
0
        }
971
0
      } else {
972
0
        ret += oscore_cbor_put_nil(&info_buf, &info_len);
973
0
        ret += oscore_cbor_put_nil(&info_buf, &info_len);
974
0
      }
975
0
      if (ret > sizeof(info_buffer)) {
976
        /* Should have been caught by assert() inoscborput_* functions */
977
0
        coap_log_warn("coap_add_observer overrun of info_buffer (%" PRIuS ")\n", ret);
978
0
        ret = sizeof(info_buffer);
979
0
      }
980
0
      oscore_info = coap_new_bin_const(info_buffer, ret);
981
0
    }
982
0
#endif /* COAP_OSCORE_SUPPORT */
983
984
    /* s->pdu header is not currently encoded */
985
0
    memcpy(s->pdu->token - request->hdr_size,
986
0
           request->token - request->hdr_size, request->hdr_size);
987
0
    raw_packet.s = s->pdu->token - request->hdr_size;
988
0
    raw_packet.length = s->pdu->used_size + request->hdr_size;
989
0
    coap_lock_callback(session->context->observe_added_cb(session, s, session->proto,
990
0
                                                          &session->endpoint->bind_addr,
991
0
                                                          &session->addr_info,
992
0
                                                          &raw_packet,
993
0
                                                          oscore_info,
994
0
                                                          session->context->observe_user_data));
995
0
#if COAP_OSCORE_SUPPORT
996
0
    coap_delete_bin_const(oscore_info);
997
0
#endif /* COAP_OSCORE_SUPPORT */
998
0
  }
999
0
  if (resource->context->track_observe_value_cb) {
1000
    /* Track last used observe value (as app handler is called) */
1001
0
    coap_lock_callback(resource->context->track_observe_value_cb(resource->context,resource->uri_path,
1002
0
                       resource->observe,
1003
0
                       resource->context->observe_user_data));
1004
0
  }
1005
1006
0
  return s;
1007
0
}
1008
1009
void
1010
coap_touch_observer(coap_context_t *context, coap_session_t *session,
1011
0
                    const coap_bin_const_t *token) {
1012
0
  coap_subscription_t *s;
1013
1014
0
  RESOURCES_ITER(context->resources, r) {
1015
0
    s = coap_find_observer(r, session, token);
1016
0
    if (s) {
1017
0
      s->fail_cnt = 0;
1018
0
    }
1019
0
  }
1020
0
}
1021
1022
void
1023
coap_delete_observer_internal(coap_resource_t *resource, coap_session_t *session,
1024
0
                              coap_subscription_t *s) {
1025
0
  if (!s)
1026
0
    return;
1027
1028
0
  if (coap_get_log_level() >= COAP_LOG_DEBUG) {
1029
0
    char outbuf[2 * 8 + 1] = "";
1030
0
    unsigned int i;
1031
0
    coap_string_t *uri_path;
1032
0
    coap_string_t *uri_query;
1033
1034
0
    for (i = 0; i < s->pdu->actual_token.length; i++) {
1035
0
      size_t size = strlen(outbuf);
1036
1037
0
      snprintf(&outbuf[size], sizeof(outbuf)-size, "%02x",
1038
0
               s->pdu->actual_token.s[i]);
1039
0
    }
1040
0
    uri_path = coap_get_uri_path(s->pdu);
1041
0
    uri_query = coap_get_query(s->pdu);
1042
0
    coap_log_debug("removed subscription '/%*.*s%s%*.*s' (%p) with token '%s' key 0x%02x%02x%02x%02x\n",
1043
0
                   uri_path ? (int)uri_path->length : 0, uri_path ? (int)uri_path->length : 0,
1044
0
                   uri_path ? (char *)uri_path->s : "",
1045
0
                   uri_query ? "?" : "",
1046
0
                   uri_query ? (int)uri_query->length : 0, uri_query ? (int)uri_query->length : 0,
1047
0
                   uri_query ? (char *)uri_query->s : "",
1048
0
                   (void *)s, outbuf, s->cache_key->key[0], s->cache_key->key[1],
1049
0
                   s->cache_key->key[2], s-> cache_key->key[3]);
1050
0
    coap_delete_string(uri_path);
1051
0
    coap_delete_string(uri_query);
1052
0
  }
1053
0
  if (session->context->observe_deleted_cb)
1054
0
    coap_lock_callback(session->context->observe_deleted_cb(session, s,
1055
0
                                                            session->context->observe_user_data));
1056
1057
0
  if (resource->subscribers) {
1058
0
    LL_DELETE(resource->subscribers, s);
1059
0
    assert(session->ref_subscriptions > 0);
1060
0
    session->ref_subscriptions--;
1061
0
    coap_session_release_lkd(session);
1062
0
    coap_delete_pdu_lkd(s->pdu);
1063
0
    coap_delete_cache_key(s->cache_key);
1064
0
    coap_free_type(COAP_SUBSCRIPTION, s);
1065
0
  }
1066
1067
0
  return;
1068
0
}
1069
1070
int
1071
coap_delete_observer(coap_resource_t *resource, coap_session_t *session,
1072
0
                     const coap_bin_const_t *token) {
1073
0
  coap_subscription_t *s;
1074
1075
0
  s = coap_find_observer(resource, session, token);
1076
0
  if (s)
1077
0
    coap_delete_observer_internal(resource, session, s);
1078
1079
0
  return s != NULL;
1080
0
}
1081
1082
int
1083
coap_delete_observer_request(coap_resource_t *resource, coap_session_t *session,
1084
0
                             const coap_bin_const_t *token, coap_pdu_t *request) {
1085
0
  coap_subscription_t *s;
1086
0
  int ret = 0;
1087
1088
0
  s = coap_find_observer(resource, session, token);
1089
0
  if (!s) {
1090
    /*
1091
     * It is possible that the client is using the wrong token.
1092
     * An example being a large FETCH spanning multiple blocks.
1093
     */
1094
0
    coap_cache_key_t *cache_key;
1095
1096
0
    cache_key = coap_cache_derive_key_w_ignore(session, request,
1097
0
                                               COAP_CACHE_IS_SESSION_BASED,
1098
0
                                               cache_ignore_options,
1099
0
                                               sizeof(cache_ignore_options)/sizeof(cache_ignore_options[0]));
1100
0
    if (cache_key) {
1101
0
      s = coap_find_observer_cache_key(resource, session, cache_key);
1102
0
      if (s) {
1103
        /* Delete entry with setup token */
1104
0
        ret = coap_delete_observer(resource, session, &s->pdu->actual_token);
1105
0
      }
1106
0
      coap_delete_cache_key(cache_key);
1107
0
    }
1108
0
  } else {
1109
0
    coap_delete_observer_internal(resource, session, s);
1110
0
    ret = 1;
1111
0
  }
1112
0
  return ret;
1113
0
}
1114
1115
void
1116
0
coap_delete_observers(coap_context_t *context, coap_session_t *session) {
1117
0
  RESOURCES_ITER(context->resources, resource) {
1118
0
    coap_subscription_t *s, *tmp;
1119
0
    LL_FOREACH_SAFE(resource->subscribers, s, tmp) {
1120
0
      if (s->session == session) {
1121
0
        if (context->observe_deleted_cb)
1122
0
          coap_lock_callback(context->observe_deleted_cb(session, s, context->observe_user_data));
1123
0
        assert(resource->subscribers);
1124
0
        LL_DELETE(resource->subscribers, s);
1125
0
        coap_session_release_lkd(session);
1126
0
        coap_delete_pdu_lkd(s->pdu);
1127
0
        coap_delete_cache_key(s->cache_key);
1128
0
        coap_free_type(COAP_SUBSCRIPTION, s);
1129
0
      }
1130
0
    }
1131
0
  }
1132
0
}
1133
1134
static void
1135
coap_notify_observers(coap_context_t *context, coap_resource_t *r,
1136
0
                      coap_deleting_resource_t deleting) {
1137
0
  coap_method_handler_t h;
1138
0
  coap_subscription_t *obs, *otmp;
1139
0
  coap_pdu_t *response;
1140
0
  uint8_t buf[4];
1141
0
  coap_string_t *query;
1142
0
  coap_block_b_t block;
1143
0
  coap_tick_t now;
1144
1145
0
  coap_lock_check_locked();
1146
1147
0
  if (r->observable && (r->dirty || r->partiallydirty)) {
1148
0
    if (r->list_being_traversed)
1149
0
      return;
1150
0
    r->list_being_traversed = 1;
1151
1152
0
    coap_resource_reference_lkd(r);
1153
1154
0
    r->partiallydirty = 0;
1155
1156
0
    LL_FOREACH_SAFE(r->subscribers, obs, otmp) {
1157
0
      coap_session_t *obs_session;
1158
0
      coap_pdu_t *obs_pdu;
1159
0
      coap_mid_t mid = COAP_INVALID_MID;
1160
1161
0
      if ((r->dirty == 0 && obs->dirty == 0) || obs->session->is_rate_limiting) {
1162
        /*
1163
         * running this resource due to partiallydirty, but this observation's
1164
         * notification was already enqueued
1165
         */
1166
0
        context->observe_pending = 1;
1167
0
        continue;
1168
0
      }
1169
1170
      /*
1171
       * obs may get deleted in the callback, or by another running
1172
       * thread when executing the callback or when sending a response.
1173
       */
1174
0
      obs_session = obs->session;
1175
0
      obs_pdu = obs->pdu;
1176
0
      coap_session_reference_lkd(obs_session);
1177
0
      coap_pdu_reference_lkd(obs_pdu);
1178
1179
0
      if (obs->session->con_active >= COAP_NSTART(obs->session) &&
1180
0
          ((r->flags & COAP_RESOURCE_FLAGS_NOTIFY_CON) ||
1181
0
           (obs->non_cnt >= COAP_OBS_MAX_NON))) {
1182
        /* Waiting for the previous unsolicited response to finish */
1183
0
        goto next_one_fail;
1184
0
      }
1185
0
      coap_ticks(&now);
1186
0
      if (obs->session->lg_xmit && obs->session->lg_xmit->last_all_sent == 0 &&
1187
0
          obs->session->lg_xmit->last_obs &&
1188
0
          (obs->session->lg_xmit->last_obs + 2*COAP_TICKS_PER_SECOND) > now) {
1189
        /* Waiting for the previous blocked unsolicited response to finish */
1190
0
        goto next_one_fail;
1191
0
      }
1192
1193
0
      obs->dirty = 0;
1194
      /* initialize response */
1195
0
      response = coap_pdu_init(COAP_MESSAGE_CON, 0, 0,
1196
0
                               coap_session_max_pdu_size_lkd(obs->session));
1197
0
      if (!response) {
1198
0
        coap_log_debug("coap_check_notify: pdu init failed, resource stays "
1199
0
                       "partially dirty\n");
1200
0
        goto next_one_fail_no_pending;
1201
0
      }
1202
1203
0
      if (!coap_add_token(response, obs->pdu->actual_token.length,
1204
0
                          obs->pdu->actual_token.s)) {
1205
0
        coap_log_debug("coap_check_notify: cannot add token, resource stays "
1206
0
                       "partially dirty\n");
1207
0
        coap_delete_pdu_lkd(response);
1208
0
        goto next_one_fail_no_pending;
1209
0
      }
1210
1211
0
      obs->pdu->mid = response->mid = coap_new_message_id_lkd(obs->session);
1212
      /* A lot of the reliable code assumes type is CON */
1213
0
      if (COAP_PROTO_NOT_RELIABLE(obs->session->proto) &&
1214
0
          (r->flags & COAP_RESOURCE_FLAGS_NOTIFY_CON) == 0 &&
1215
0
          ((r->flags & COAP_RESOURCE_FLAGS_NOTIFY_NON_ALWAYS) ||
1216
0
           obs->non_cnt < COAP_OBS_MAX_NON)) {
1217
0
        response->type = COAP_MESSAGE_NON;
1218
0
      } else {
1219
0
        response->type = COAP_MESSAGE_CON;
1220
0
      }
1221
0
      switch (deleting) {
1222
0
      case COAP_NOT_DELETING_RESOURCE:
1223
        /* fill with observer-specific data */
1224
0
        coap_add_option_internal(response, COAP_OPTION_OBSERVE,
1225
0
                                 coap_encode_var_safe(buf, sizeof(buf),
1226
0
                                                      r->observe),
1227
0
                                 buf);
1228
0
        if (coap_get_block_b(obs->session, obs->pdu, COAP_OPTION_BLOCK2,
1229
0
                             &block)) {
1230
          /* Will get updated later (e.g. M bit) if appropriate */
1231
0
          coap_add_option_internal(response, COAP_OPTION_BLOCK2,
1232
0
                                   coap_encode_var_safe(buf, sizeof(buf),
1233
0
                                                        ((0 << 4) |
1234
0
                                                         (0 << 3) |
1235
0
                                                         block.aszx)),
1236
0
                                   buf);
1237
0
        }
1238
0
#if COAP_Q_BLOCK_SUPPORT
1239
0
        else if (coap_get_block_b(obs->session, obs->pdu, COAP_OPTION_Q_BLOCK2,
1240
0
                                  &block)) {
1241
          /* Will get updated later (e.g. M bit) if appropriate */
1242
0
          coap_add_option_internal(response, COAP_OPTION_Q_BLOCK2,
1243
0
                                   coap_encode_var_safe(buf, sizeof(buf),
1244
0
                                                        ((0 << 4) |
1245
0
                                                         (0 << 3) |
1246
0
                                                         block.szx)),
1247
0
                                   buf);
1248
0
        }
1249
0
#endif /* COAP_Q_BLOCK_SUPPORT */
1250
1251
0
        h = r->handler[obs->pdu->code - 1];
1252
0
        assert(h);      /* we do not allow subscriptions if no
1253
                         * GET/FETCH handler is defined */
1254
0
        query = coap_get_query(obs->pdu);
1255
0
        coap_log_debug("Observe PDU presented to app.\n");
1256
0
        coap_show_pdu(COAP_LOG_DEBUG, obs->pdu);
1257
0
        coap_log_debug("call custom handler for resource '%*.*s' (4)\n",
1258
0
                       (int)r->uri_path->length, (int)r->uri_path->length,
1259
0
                       r->uri_path->s);
1260
1261
        /* obs may get deleted during callback (potentially by another thread) */
1262
0
        if (r->flags & COAP_RESOURCE_SAFE_REQUEST_HANDLER) {
1263
0
          coap_lock_callback_release(h(r, obs->session, obs->pdu, query, response),
1264
                                     /* context is being freed off */
1265
0
                                     coap_delete_string(query);
1266
0
                                     coap_delete_pdu_lkd(response);
1267
0
                                     coap_session_release_lkd(obs_session);
1268
0
                                     coap_pdu_release_lkd(obs_pdu);
1269
0
                                     r->list_being_traversed = 0;
1270
0
                                     coap_resource_release_lkd(r);
1271
0
                                     return);
1272
0
        } else {
1273
0
          coap_lock_specific_callback_release(&r->lock,
1274
0
                                              h(r, obs->session, obs->pdu, query, response),
1275
                                              /* context is being freed off */
1276
0
                                              coap_delete_string(query);
1277
0
                                              coap_delete_pdu_lkd(response);
1278
0
                                              coap_session_release_lkd(obs_session);
1279
0
                                              coap_pdu_release_lkd(obs_pdu);
1280
0
                                              r->list_being_traversed = 0;
1281
0
                                              coap_resource_release_lkd(r);
1282
0
                                              return);
1283
0
        }
1284
1285
        /* Check validity of response code */
1286
0
        if (!coap_check_code_class(obs_session, response)) {
1287
0
          coap_log_warn("handle_request: Invalid PDU response code (%d.%02d)\n",
1288
0
                        COAP_RESPONSE_CLASS(response->code),
1289
0
                        response->code & 0x1f);
1290
0
          coap_delete_string(query);
1291
0
          coap_delete_pdu_lkd(response);
1292
0
          coap_session_release_lkd(obs_session);
1293
0
          coap_pdu_release_lkd(obs_pdu);
1294
0
          r->list_being_traversed = 0;
1295
0
          coap_resource_release_lkd(r);
1296
0
          return;
1297
0
        }
1298
1299
        /* Check if lg_xmit generated and update PDU code if so */
1300
0
        coap_check_code_lg_xmit(obs_session, obs_pdu, response, r, query);
1301
0
        coap_delete_string(query);
1302
0
        if (COAP_RESPONSE_CLASS(response->code) != 2) {
1303
0
          coap_remove_option(response, COAP_OPTION_OBSERVE);
1304
0
        }
1305
0
        if (COAP_RESPONSE_CLASS(response->code) > 2) {
1306
0
          coap_delete_observer(r, obs_session, &obs_pdu->actual_token);
1307
0
          obs = NULL;
1308
0
        }
1309
0
        break;
1310
0
      case COAP_DELETING_RESOURCE_ON_EXIT:
1311
        /* Don't worry if it does not get there */
1312
0
        response->type = COAP_MESSAGE_NON;
1313
0
        response->code = COAP_RESPONSE_CODE(503);
1314
0
        coap_add_option_internal(response, COAP_OPTION_MAXAGE,
1315
0
                                 coap_encode_var_safe(buf, sizeof(buf),
1316
0
                                                      30),
1317
0
                                 buf);
1318
0
        break;
1319
0
      case COAP_DELETING_RESOURCE:
1320
0
      default:
1321
        /* Don't worry if it does not get there */
1322
0
        response->type = COAP_MESSAGE_NON;
1323
0
        response->code = COAP_RESPONSE_CODE(404);
1324
0
        break;
1325
0
      }
1326
1327
0
      if (obs) {
1328
0
        coap_subscription_t *s;
1329
        /*
1330
         * obs may have been deleted in the callback, or by another running
1331
         * thread when executing the callback.
1332
         */
1333
0
        LL_FOREACH(r->subscribers, s) {
1334
0
          if (s == obs) {
1335
0
            break;
1336
0
          }
1337
0
        }
1338
0
        if (s == NULL)
1339
0
          obs = NULL;
1340
0
      }
1341
0
      if (obs) {
1342
0
        if (response->type == COAP_MESSAGE_CON ||
1343
0
            (r->flags & COAP_RESOURCE_FLAGS_NOTIFY_NON_ALWAYS)) {
1344
0
          obs->non_cnt = 0;
1345
0
        } else {
1346
0
          obs->non_cnt++;
1347
0
        }
1348
1349
0
#if COAP_Q_BLOCK_SUPPORT
1350
0
        if (response->code == COAP_RESPONSE_CODE(205) &&
1351
0
            coap_get_block_b(obs_session, response, COAP_OPTION_Q_BLOCK2,
1352
0
                             &block) &&
1353
0
            block.m) {
1354
0
          query = coap_get_query(obs_pdu);
1355
0
          mid = coap_send_q_block2(obs_session, r, query, obs_pdu->code,
1356
0
                                   block, response, 1);
1357
0
          coap_delete_string(query);
1358
0
          goto finish;
1359
0
        }
1360
0
#endif /* COAP_Q_BLOCK_SUPPORT */
1361
0
      }
1362
0
      mid = coap_send_internal(obs_session, response, NULL);
1363
1364
0
#if COAP_Q_BLOCK_SUPPORT
1365
0
finish:
1366
0
#endif /* COAP_Q_BLOCK_SUPPORT */
1367
0
      if (COAP_INVALID_MID == mid) {
1368
0
        coap_log_debug("*  %s: coap_check_notify: sending failed, resource stays "
1369
0
                       "partially dirty\n", coap_session_str(obs_session));
1370
0
        if (obs) {
1371
0
          coap_subscription_t *s;
1372
          /*
1373
           * obs may have been deleted in coap_send_internal() or
1374
           * coap_send_q_block2().
1375
           */
1376
0
          LL_FOREACH(r->subscribers, s) {
1377
0
            if (s == obs) {
1378
0
              break;
1379
0
            }
1380
0
          }
1381
0
          if (s == NULL)
1382
0
            obs = NULL;
1383
0
        }
1384
0
        if (obs)
1385
0
          obs->dirty = 1;
1386
0
        r->partiallydirty = 1;
1387
0
      }
1388
0
      goto cleanup;
1389
1390
0
next_one_fail:
1391
0
      context->observe_pending = 1;
1392
0
next_one_fail_no_pending:
1393
0
      r->partiallydirty = 1;
1394
0
      if (obs)
1395
0
        obs->dirty = 1;
1396
0
cleanup:
1397
0
      coap_session_release_lkd(obs_session);
1398
0
      coap_pdu_release_lkd(obs_pdu);
1399
0
    }
1400
0
    r->list_being_traversed = 0;
1401
0
    r->dirty = 0;
1402
0
    coap_resource_release_lkd(r);
1403
    /* r may be no more if elsewhere coap_free_resource() has been called */
1404
0
  } else {
1405
0
    r->dirty = 0;
1406
0
  }
1407
0
}
1408
1409
COAP_API int
1410
0
coap_resource_set_dirty(coap_resource_t *r, const coap_string_t *query) {
1411
0
  int ret;
1412
0
  (void)query;
1413
1414
0
  coap_lock_lock(return 0);
1415
0
  ret = coap_resource_notify_observers_lkd(r, COAP_NOT_DELETING_RESOURCE);
1416
0
  coap_lock_unlock();
1417
0
  return ret;
1418
0
}
1419
1420
COAP_API int
1421
coap_resource_notify_observers(coap_resource_t *r,
1422
0
                               const coap_string_t *query) {
1423
0
  int ret;
1424
1425
0
  (void)query;
1426
0
  coap_lock_lock(return 0);
1427
0
  ret = coap_resource_notify_observers_lkd(r, COAP_NOT_DELETING_RESOURCE);
1428
0
  coap_lock_unlock();
1429
0
  return ret;
1430
0
}
1431
1432
int
1433
coap_resource_notify_observers_lkd(coap_resource_t *r,
1434
0
                                   coap_deleting_resource_t deleting) {
1435
0
  coap_lock_check_locked();
1436
0
  if (!r->observable)
1437
0
    return 0;
1438
0
  if (!r->subscribers)
1439
0
    return 0;
1440
0
  r->dirty = 1;
1441
1442
  /* Increment value for next Observe use. Observe value must be < 2^24 */
1443
0
  r->observe = (r->observe + 1) & 0xFFFFFF;
1444
1445
0
  assert(r->context);
1446
1447
0
  if (r->context->track_observe_value_cb) {
1448
    /* Track last used observe value */
1449
0
    if ((r->observe % r->context->observe_save_freq) == 0)
1450
0
      coap_lock_callback(r->context->track_observe_value_cb(r->context, r->uri_path,
1451
0
                                                            r->observe,
1452
0
                                                            r->context->observe_user_data));
1453
0
  }
1454
1455
0
  coap_notify_observers(r->context, r, deleting);
1456
0
  return 1;
1457
0
}
1458
1459
void
1460
0
coap_resource_set_mode(coap_resource_t *resource, int mode) {
1461
0
  resource->flags = (resource->flags &
1462
0
                     ~(COAP_RESOURCE_FLAGS_NOTIFY_CON|COAP_RESOURCE_FLAGS_NOTIFY_NON)) |
1463
0
                    (mode & (COAP_RESOURCE_FLAGS_NOTIFY_CON|COAP_RESOURCE_FLAGS_NOTIFY_NON));
1464
0
}
1465
1466
void
1467
0
coap_resource_set_userdata(coap_resource_t *resource, void *data) {
1468
0
  resource->user_data = data;
1469
0
}
1470
1471
void *
1472
0
coap_resource_get_userdata(coap_resource_t *resource) {
1473
0
  return resource->user_data;
1474
0
}
1475
1476
void
1477
coap_resource_release_userdata_handler(coap_context_t *context,
1478
0
                                       coap_resource_release_userdata_handler_t callback) {
1479
0
  context->release_userdata_cb = callback;
1480
0
}
1481
1482
void
1483
0
coap_resource_set_get_observable(coap_resource_t *resource, int mode) {
1484
0
  if (resource->is_unknown || resource->is_proxy_uri) {
1485
    /* We cannot observe these */
1486
0
    coap_log_debug("coap_resource_set_get_observable: Not supported for Unknown or Proxy URIs\n");
1487
0
    resource->observable = 0;
1488
0
  } else {
1489
0
    resource->observable = mode ? 1 : 0;
1490
0
  }
1491
0
}
1492
1493
coap_str_const_t *
1494
0
coap_resource_get_uri_path(coap_resource_t *resource) {
1495
0
  if (resource)
1496
0
    return resource->uri_path;
1497
0
  return NULL;
1498
0
}
1499
1500
COAP_API void
1501
0
coap_check_notify(coap_context_t *context) {
1502
0
  coap_lock_lock(return);
1503
0
  coap_check_notify_lkd(context);
1504
0
  coap_lock_unlock();
1505
0
}
1506
1507
void
1508
0
coap_check_notify_lkd(coap_context_t *context) {
1509
1510
0
  coap_lock_check_locked();
1511
0
  if (context->observe_pending) {
1512
0
    context->observe_pending = 0;
1513
0
    RESOURCES_ITER(context->resources, r) {
1514
0
      coap_notify_observers(context, r, COAP_NOT_DELETING_RESOURCE);
1515
0
    }
1516
0
  }
1517
0
}
1518
1519
void
1520
coap_persist_set_observe_num(coap_resource_t *resource,
1521
0
                             uint32_t start_observe_no) {
1522
0
  if (!resource)
1523
0
    return;
1524
1525
0
  resource->observe = start_observe_no & 0xffffff;
1526
0
}
1527
1528
/**
1529
 * Checks the failure counter for (peer, token) and removes peer from
1530
 * the list of observers for the given resource when COAP_OBS_MAX_FAIL
1531
 * is reached.
1532
 *
1533
 * @param context  The CoAP context to use
1534
 * @param resource The resource to check for (peer, token)
1535
 * @param session  The observer's session
1536
 * @param token    The token that has been used for subscription.
1537
 */
1538
static void
1539
coap_remove_failed_observers(coap_context_t *context,
1540
                             coap_resource_t *resource,
1541
                             coap_session_t *session,
1542
0
                             const coap_bin_const_t *token) {
1543
0
  coap_subscription_t *obs, *otmp;
1544
1545
0
  LL_FOREACH_SAFE(resource->subscribers, obs, otmp) {
1546
0
    if (obs->session == session &&
1547
0
        coap_binary_equal(token, &obs->pdu->actual_token)) {
1548
      /* count failed notifies and remove when
1549
       * COAP_OBS_MAX_FAIL is reached */
1550
0
      obs->fail_cnt++;
1551
0
      if (obs->fail_cnt >= COAP_OBS_MAX_FAIL) {
1552
0
        coap_cancel_all_messages(context, obs->session,
1553
0
                                 &obs->pdu->actual_token);
1554
0
        coap_delete_observer(resource, session, token);
1555
0
      }
1556
0
      break;                        /* break loop if observer was found */
1557
0
    }
1558
0
  }
1559
0
}
1560
1561
void
1562
coap_handle_failed_notify(coap_context_t *context,
1563
                          coap_session_t *session,
1564
0
                          const coap_bin_const_t *token) {
1565
1566
0
  RESOURCES_ITER(context->resources, r) {
1567
0
    coap_remove_failed_observers(context, r, session, token);
1568
0
  }
1569
0
}
1570
1571
void
1572
0
coap_resource_reference_lkd(coap_resource_t *resource) {
1573
0
  resource->ref++;
1574
0
}
1575
1576
coap_resource_t *
1577
0
coap_add_dynamic_resource(coap_session_t *session, coap_pdu_t *pdu) {
1578
0
  coap_context_t *context = session->context;
1579
0
  coap_resource_t *resource;
1580
1581
0
  if ((context->dyn_create_handler != NULL) &&
1582
0
      (pdu->code == COAP_REQUEST_CODE_PUT || pdu->code == COAP_REQUEST_CODE_POST)) {
1583
    /* Above test must be the same as in coap_op_dyn_resource_load_disk() */
1584
0
    if (context->dynamic_cur < context->dynamic_max || context->dynamic_max == 0) {
1585
0
#if COAP_WITH_OBSERVE_PERSIST
1586
      /* If we are maintaining Observe persist */
1587
0
      context->unknown_pdu = pdu;
1588
0
      context->unknown_session = session;
1589
0
#endif /* COAP_WITH_OBSERVE_PERSIST */
1590
0
      coap_lock_callback_ret(resource, context->dyn_create_handler(session, pdu));
1591
0
#if COAP_WITH_OBSERVE_PERSIST
1592
      /* If we are maintaining Observe persist */
1593
0
      context->unknown_pdu = NULL;
1594
0
      context->unknown_session = NULL;
1595
0
#endif /* COAP_WITH_OBSERVE_PERSIST */
1596
0
      if (resource) {
1597
0
        context->dynamic_cur++;
1598
0
        resource->is_dynamic = 1;
1599
0
      }
1600
0
      return resource;
1601
0
    }
1602
0
  }
1603
0
  return NULL;
1604
0
}
1605
1606
#endif /* COAP_SERVER_SUPPORT */