Coverage Report

Created: 2026-09-03 07:07

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