Coverage Report

Created: 2025-10-13 06:25

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