1 /* coap_subscribe.c -- subscription handling for CoAP
2 * see RFC7641
3 *
4 * Copyright (C) 2010-2019,2022-2023 Olaf Bergmann <bergmann@tzi.org>
5 *
6 * SPDX-License-Identifier: BSD-2-Clause
7 *
8 * This file is part of the CoAP library libcoap. Please see
9 * README for terms of use.
10 */
11
12 /**
13 * @file coap_subscribe.c
14 * @brief Subscription handling functions
15 */
16
17 #include "coap3/coap_internal.h"
18
19 #ifndef min
20 #define min(a,b) ((a) < (b) ? (a) : (b))
21 #endif
22
23 #if COAP_SERVER_SUPPORT
24 void
coap_subscription_init(coap_subscription_t * s)25 coap_subscription_init(coap_subscription_t *s) {
26 assert(s);
27 memset(s, 0, sizeof(coap_subscription_t));
28 }
29
30 void
coap_persist_track_funcs(coap_context_t * context,coap_observe_added_t observe_added,coap_observe_deleted_t observe_deleted,coap_track_observe_value_t track_observe_value,coap_dyn_resource_added_t dyn_resource_added,coap_resource_deleted_t resource_deleted,uint32_t save_freq,void * user_data)31 coap_persist_track_funcs(coap_context_t *context,
32 coap_observe_added_t observe_added,
33 coap_observe_deleted_t observe_deleted,
34 coap_track_observe_value_t track_observe_value,
35 coap_dyn_resource_added_t dyn_resource_added,
36 coap_resource_deleted_t resource_deleted,
37 uint32_t save_freq,
38 void *user_data) {
39 context->observe_added = observe_added;
40 context->observe_deleted = observe_deleted;
41 context->observe_user_data = user_data;
42 context->observe_save_freq = save_freq ? save_freq : 1;
43 context->track_observe_value = track_observe_value;
44 context->dyn_resource_added = dyn_resource_added;
45 context->resource_deleted = resource_deleted;
46 }
47
48 coap_subscription_t *
coap_persist_observe_add(coap_context_t * context,coap_proto_t e_proto,const coap_address_t * e_listen_addr,const coap_addr_tuple_t * s_addr_info,const coap_bin_const_t * raw_packet,const coap_bin_const_t * oscore_info)49 coap_persist_observe_add(coap_context_t *context,
50 coap_proto_t e_proto,
51 const coap_address_t *e_listen_addr,
52 const coap_addr_tuple_t *s_addr_info,
53 const coap_bin_const_t *raw_packet,
54 const coap_bin_const_t *oscore_info) {
55 coap_session_t *session = NULL;
56 const uint8_t *data;
57 size_t data_len;
58 coap_pdu_t *pdu = NULL;
59 #if COAP_CONSTRAINED_STACK
60 /* e_packet protected by mutex m_persist_add */
61 static coap_packet_t e_packet;
62 #else /* ! COAP_CONSTRAINED_STACK */
63 coap_packet_t e_packet;
64 #endif /* ! COAP_CONSTRAINED_STACK */
65 coap_packet_t *packet = &e_packet;
66 coap_tick_t now;
67 coap_string_t *uri_path = NULL;
68 coap_opt_iterator_t opt_iter;
69 coap_opt_t *observe;
70 int observe_action;
71 coap_resource_t *r;
72 coap_subscription_t *s;
73 coap_endpoint_t *ep;
74
75 if (e_listen_addr == NULL || s_addr_info == NULL || raw_packet == NULL)
76 return NULL;
77
78 /* Will be creating a local 'open' session */
79 if (e_proto != COAP_PROTO_UDP)
80 return NULL;
81
82 ep = context->endpoint;
83 while (ep) {
84 if (ep->proto == e_proto &&
85 memcmp(e_listen_addr, &ep->bind_addr, sizeof(ep->bind_addr)) == 0)
86 break;
87 ep = ep->next;
88 }
89 if (!ep)
90 return NULL;
91
92 #if COAP_CONSTRAINED_STACK
93 coap_mutex_lock(&m_persist_add);
94 #endif /* COAP_CONSTRAINED_STACK */
95
96 /* Build up packet */
97 memcpy(&packet->addr_info, s_addr_info, sizeof(packet->addr_info));
98 packet->ifindex = 0;
99 memcpy(&packet->payload, &raw_packet->s, sizeof(packet->payload));
100 packet->length = raw_packet->length;
101
102 data = raw_packet->s;
103 data_len = raw_packet->length;
104 if (data_len < 4)
105 goto malformed;
106
107 /* Get the session */
108
109 coap_ticks(&now);
110 session = coap_endpoint_get_session(ep, packet, now);
111 if (session == NULL)
112 goto fail;
113 /* Need max space incase PDU is updated with updated token, huge size etc. */
114 pdu = coap_pdu_init(0, 0, 0, 0);
115 if (!pdu)
116 goto fail;
117
118 if (!coap_pdu_parse(session->proto, data, data_len, pdu)) {
119 goto malformed;
120 }
121 pdu->max_size = pdu->used_size;
122
123 if (pdu->code != COAP_REQUEST_CODE_GET &&
124 pdu->code != COAP_REQUEST_CODE_FETCH)
125 goto malformed;
126
127 observe = coap_check_option(pdu, COAP_OPTION_OBSERVE, &opt_iter);
128 if (observe == NULL)
129 goto malformed;
130 observe_action = coap_decode_var_bytes(coap_opt_value(observe),
131 coap_opt_length(observe));
132 if (observe_action != COAP_OBSERVE_ESTABLISH)
133 goto malformed;
134
135 /* Get the resource */
136
137 uri_path = coap_get_uri_path(pdu);
138 if (!uri_path)
139 goto malformed;
140
141 r = coap_get_resource_from_uri_path(session->context,
142 (coap_str_const_t *)uri_path);
143 if (r == NULL) {
144 coap_log_warn("coap_persist_observe_add: resource '%s' not defined\n",
145 uri_path->s);
146 goto fail;
147 }
148 if (!r->observable) {
149 coap_log_warn("coap_persist_observe_add: resource '%s' not observable\n",
150 uri_path->s);
151 goto fail;
152 }
153 coap_delete_string(uri_path);
154 uri_path = NULL;
155
156 /* Create / update subscription for observing */
157 /* Now set up the subscription */
158 s = coap_add_observer(r, session, &pdu->actual_token, pdu);
159 if (s == NULL)
160 goto fail;
161
162 #if COAP_OSCORE_SUPPORT
163 if (oscore_info) {
164 coap_log_debug("persist: OSCORE association being updated\n");
165 /*
166 * Need to track the association used for tracking this observe, done as
167 * a CBOR array. Written in coap_add_observer().
168 *
169 * If an entry is null, then use nil, else a set of bytes
170 *
171 * Currently tracking 5 items
172 * recipient_id
173 * id_context
174 * aad (from oscore_association_t)
175 * partial_iv (from oscore_association_t)
176 * nonce (from oscore_association_t)
177 */
178 oscore_ctx_t *osc_ctx;
179 const uint8_t *info_buf = oscore_info->s;
180 size_t info_buf_len = oscore_info->length;
181 size_t ret = 0;
182 coap_bin_const_t oscore_key_id;
183 coap_bin_const_t partial_iv;
184 coap_bin_const_t aad;
185 coap_bin_const_t id_context;
186 coap_bin_const_t nonce;
187 int have_aad = 0;
188 int have_partial_iv = 0;
189 int have_id_context = 0;
190 int have_nonce = 0;
191
192 ret = oscore_cbor_get_next_element(&info_buf, &info_buf_len);
193 if (ret != CBOR_ARRAY)
194 goto oscore_fail;
195 if (oscore_cbor_get_element_size(&info_buf, &info_buf_len) != 5)
196 goto oscore_fail;
197
198 /* recipient_id */
199 ret = oscore_cbor_get_next_element(&info_buf, &info_buf_len);
200 if (ret != CBOR_BYTE_STRING)
201 goto oscore_fail;
202 oscore_key_id.length = oscore_cbor_get_element_size(&info_buf,
203 &info_buf_len);
204 oscore_key_id.s = info_buf;
205 info_buf += oscore_key_id.length;
206
207 /* id_context */
208 ret = oscore_cbor_get_next_element(&info_buf, &info_buf_len);
209 if (ret == CBOR_BYTE_STRING) {
210 id_context.length = oscore_cbor_get_element_size(&info_buf,
211 &info_buf_len);
212 id_context.s = info_buf;
213 info_buf += id_context.length;
214 have_id_context = 1;
215 } else if (ret == CBOR_SIMPLE_VALUE &&
216 oscore_cbor_get_element_size(&info_buf,
217 &info_buf_len) == CBOR_NULL) {
218 } else
219 goto oscore_fail;
220
221 /* aad */
222 ret = oscore_cbor_get_next_element(&info_buf, &info_buf_len);
223 if (ret == CBOR_BYTE_STRING) {
224 aad.length = oscore_cbor_get_element_size(&info_buf, &info_buf_len);
225 aad.s = info_buf;
226 info_buf += aad.length;
227 have_aad = 1;
228 } else if (ret == CBOR_SIMPLE_VALUE &&
229 oscore_cbor_get_element_size(&info_buf,
230 &info_buf_len) == CBOR_NULL) {
231 } else
232 goto oscore_fail;
233
234 /* partial_iv */
235 ret = oscore_cbor_get_next_element(&info_buf, &info_buf_len);
236 if (ret == CBOR_BYTE_STRING) {
237 partial_iv.length = oscore_cbor_get_element_size(&info_buf,
238 &info_buf_len);
239 partial_iv.s = info_buf;
240 info_buf += partial_iv.length;
241 have_partial_iv = 1;
242 } else if (ret == CBOR_SIMPLE_VALUE &&
243 oscore_cbor_get_element_size(&info_buf,
244 &info_buf_len) == CBOR_NULL) {
245 } else
246 goto oscore_fail;
247
248 /* nonce */
249 ret = oscore_cbor_get_next_element(&info_buf, &info_buf_len);
250 if (ret == CBOR_BYTE_STRING) {
251 nonce.length = oscore_cbor_get_element_size(&info_buf,
252 &info_buf_len);
253 nonce.s = info_buf;
254 info_buf += nonce.length;
255 have_nonce = 1;
256 } else if (ret == CBOR_SIMPLE_VALUE &&
257 oscore_cbor_get_element_size(&info_buf,
258 &info_buf_len) == CBOR_NULL) {
259 } else
260 goto oscore_fail;
261
262 osc_ctx = oscore_find_context(session->context, oscore_key_id,
263 have_id_context ? &id_context : NULL, NULL,
264 &session->recipient_ctx);
265 if (osc_ctx) {
266 session->oscore_encryption = 1;
267 oscore_new_association(session, pdu, &pdu->actual_token,
268 session->recipient_ctx,
269 have_aad ? &aad : NULL,
270 have_nonce ? &nonce : NULL,
271 have_partial_iv ? &partial_iv : NULL,
272 1);
273 coap_log_debug("persist: OSCORE association added\n");
274 oscore_log_hex_value(COAP_LOG_OSCORE, "partial_iv",
275 have_partial_iv ? &partial_iv : NULL);
276 }
277 }
278 oscore_fail:
279 #else /* ! COAP_OSCORE_SUPPORT */
280 (void)oscore_info;
281 #endif /* ! COAP_OSCORE_SUPPORT */
282 coap_delete_pdu(pdu);
283 #if COAP_CONSTRAINED_STACK
284 coap_mutex_unlock(&m_persist_add);
285 #endif /* COAP_CONSTRAINED_STACK */
286 return s;
287
288 malformed:
289 coap_log_warn("coap_persist_observe_add: discard malformed PDU\n");
290 fail:
291 #if COAP_CONSTRAINED_STACK
292 coap_mutex_unlock(&m_persist_add);
293 #endif /* COAP_CONSTRAINED_STACK */
294 coap_delete_string(uri_path);
295 coap_delete_pdu(pdu);
296 return NULL;
297 }
298
299 #if COAP_WITH_OBSERVE_PERSIST
300 #include <stdio.h>
301
302 /*
303 * read in active observe entry.
304 */
305 static int
coap_op_observe_read(FILE * fp,coap_subscription_t ** observe_key,coap_proto_t * e_proto,coap_address_t * e_listen_addr,coap_addr_tuple_t * s_addr_info,coap_bin_const_t ** raw_packet,coap_bin_const_t ** oscore_info)306 coap_op_observe_read(FILE *fp, coap_subscription_t **observe_key,
307 coap_proto_t *e_proto, coap_address_t *e_listen_addr,
308 coap_addr_tuple_t *s_addr_info,
309 coap_bin_const_t **raw_packet, coap_bin_const_t **oscore_info) {
310 ssize_t size;
311 coap_binary_t *scratch = NULL;
312
313 assert(fp && observe_key && e_proto && e_listen_addr && s_addr_info &&
314 raw_packet && oscore_info);
315
316 *raw_packet = NULL;
317 *oscore_info = NULL;
318
319 if (fread(observe_key, sizeof(*observe_key), 1, fp) == 1) {
320 /* New record 'key proto listen addr_info len raw_packet len oscore' */
321 if (fread(e_proto, sizeof(*e_proto), 1, fp) != 1)
322 goto fail;
323 if (fread(e_listen_addr, sizeof(*e_listen_addr), 1, fp) != 1)
324 goto fail;
325 if (fread(s_addr_info, sizeof(*s_addr_info), 1, fp) != 1)
326 goto fail;
327 if (fread(&size, sizeof(size), 1, fp) != 1)
328 goto fail;
329 if (size < 0 || size > 0x10000)
330 goto fail;
331 scratch = coap_new_binary(size);
332 if ((scratch) == NULL)
333 goto fail;
334 if (fread(scratch->s, scratch->length, 1, fp) != 1)
335 goto fail;
336 *raw_packet = (coap_bin_const_t *)scratch;
337 scratch = NULL;
338 if (fread(&size, sizeof(size), 1, fp) != 1)
339 goto fail;
340 /* If size == -1, then no oscore information */
341 if (size == -1)
342 return 1;
343 else if (size < 0 || size > 0x10000)
344 goto fail;
345 else {
346 scratch = coap_new_binary(size);
347 if (scratch == NULL)
348 goto fail;
349 if (fread(scratch->s, scratch->length, 1, fp) != 1)
350 goto fail;
351 *oscore_info = (coap_bin_const_t *)scratch;
352 }
353 return 1;
354 }
355 fail:
356 coap_delete_bin_const(*raw_packet);
357 coap_delete_binary(scratch);
358
359 *observe_key = NULL;
360 memset(e_proto, 0, sizeof(*e_proto));
361 memset(e_listen_addr, 0, sizeof(*e_listen_addr));
362 memset(s_addr_info, 0, sizeof(*s_addr_info));
363 *raw_packet = NULL;
364 return 0;
365 }
366
367 /*
368 * write out active observe entry.
369 */
370 static int
coap_op_observe_write(FILE * fp,coap_subscription_t * observe_key,coap_proto_t e_proto,coap_address_t e_listen_addr,coap_addr_tuple_t s_addr_info,coap_bin_const_t * raw_packet,coap_bin_const_t * oscore_info)371 coap_op_observe_write(FILE *fp, coap_subscription_t *observe_key,
372 coap_proto_t e_proto, coap_address_t e_listen_addr,
373 coap_addr_tuple_t s_addr_info,
374 coap_bin_const_t *raw_packet, coap_bin_const_t *oscore_info) {
375 if (fwrite(&observe_key, sizeof(observe_key), 1, fp) != 1)
376 goto fail;
377 if (fwrite(&e_proto, sizeof(e_proto), 1, fp) != 1)
378 goto fail;
379 if (fwrite(&e_listen_addr, sizeof(e_listen_addr),
380 1, fp) != 1)
381 goto fail;
382 if (fwrite(&s_addr_info, sizeof(s_addr_info), 1, fp) != 1)
383 goto fail;
384 if (fwrite(&raw_packet->length, sizeof(raw_packet->length), 1, fp) != 1)
385 goto fail;
386 if (fwrite(raw_packet->s, raw_packet->length, 1, fp) != 1)
387 goto fail;
388 if (oscore_info) {
389 if (fwrite(&oscore_info->length, sizeof(oscore_info->length), 1, fp) != 1)
390 goto fail;
391 if (fwrite(oscore_info->s, oscore_info->length, 1, fp) != 1)
392 goto fail;
393 } else {
394 ssize_t not_defined = -1;
395
396 if (fwrite(¬_defined, sizeof(not_defined), 1, fp) != 1)
397 goto fail;
398 }
399 return 1;
400 fail:
401 return 0;
402 }
403
404 /*
405 * This should be called before coap_persist_track_funcs() to prevent
406 * coap_op_observe_added() getting unnecessarily called.
407 * It should be called after init_resources() and coap_op_resource_load_disk()
408 * so that all the resources are in place.
409 */
410 static void
coap_op_observe_load_disk(coap_context_t * ctx)411 coap_op_observe_load_disk(coap_context_t *ctx) {
412 FILE *fp_orig = fopen((const char *)ctx->observe_save_file->s, "r");
413 FILE *fp_new = NULL;
414 coap_subscription_t *observe_key = NULL;
415 coap_proto_t e_proto;
416 coap_address_t e_listen_addr;
417 coap_addr_tuple_t s_addr_info;
418 coap_bin_const_t *raw_packet = NULL;
419 coap_bin_const_t *oscore_info = NULL;
420 char *new = NULL;
421
422 if (fp_orig == NULL)
423 goto fail;
424
425 new = coap_malloc_type(COAP_STRING, ctx->observe_save_file->length + 5);
426 if (!new)
427 goto fail;
428
429 strcpy(new, (const char *)ctx->observe_save_file->s);
430 strcat(new, ".tmp");
431 fp_new = fopen(new, "w+");
432 if (fp_new == NULL)
433 goto fail;
434
435 /* Go through and load oscore entry, updating key on the way */
436 while (1) {
437 if (!coap_op_observe_read(fp_orig, &observe_key, &e_proto, &e_listen_addr,
438 &s_addr_info, &raw_packet, &oscore_info))
439 break;
440 coap_log_debug("persist: New session/observe being created\n");
441 observe_key = coap_persist_observe_add(ctx, e_proto,
442 &e_listen_addr,
443 &s_addr_info,
444 raw_packet,
445 oscore_info);
446 if (observe_key) {
447 if (!coap_op_observe_write(fp_new, observe_key, e_proto, e_listen_addr,
448 s_addr_info, raw_packet, oscore_info))
449 goto fail;
450 coap_delete_bin_const(raw_packet);
451 raw_packet = NULL;
452 coap_delete_bin_const(oscore_info);
453 oscore_info = NULL;
454 }
455 }
456 coap_delete_bin_const(raw_packet);
457 raw_packet = NULL;
458 coap_delete_bin_const(oscore_info);
459 oscore_info = NULL;
460
461 if (fflush(fp_new) == EOF)
462 goto fail;
463 fclose(fp_new);
464 fclose(fp_orig);
465 /* Either old or new is in place */
466 (void)rename(new, (const char *)ctx->observe_save_file->s);
467 coap_free_type(COAP_STRING, new);
468 return;
469
470 fail:
471 coap_delete_bin_const(raw_packet);
472 coap_delete_bin_const(oscore_info);
473 if (fp_new)
474 fclose(fp_new);
475 if (fp_orig)
476 fclose(fp_orig);
477 if (new) {
478 (void)remove(new);
479 }
480 coap_free_type(COAP_STRING, new);
481 return;
482 }
483
484 /*
485 * client has registered a new observe subscription request.
486 */
487 static int
coap_op_observe_added(coap_session_t * session,coap_subscription_t * a_observe_key,coap_proto_t a_e_proto,coap_address_t * a_e_listen_addr,coap_addr_tuple_t * a_s_addr_info,coap_bin_const_t * a_raw_packet,coap_bin_const_t * a_oscore_info,void * user_data)488 coap_op_observe_added(coap_session_t *session,
489 coap_subscription_t *a_observe_key,
490 coap_proto_t a_e_proto, coap_address_t *a_e_listen_addr,
491 coap_addr_tuple_t *a_s_addr_info,
492 coap_bin_const_t *a_raw_packet,
493 coap_bin_const_t *a_oscore_info, void *user_data) {
494 FILE *fp_orig = fopen((const char *)session->context->observe_save_file->s,
495 "r");
496 FILE *fp_new = NULL;
497 coap_subscription_t *observe_key = NULL;
498 coap_proto_t e_proto;
499 coap_address_t e_listen_addr;
500 coap_addr_tuple_t s_addr_info;
501 coap_bin_const_t *raw_packet = NULL;
502 coap_bin_const_t *oscore_info = NULL;
503 char *new = NULL;
504
505 (void)user_data;
506
507 new = coap_malloc_type(COAP_STRING,
508 session->context->observe_save_file->length + 5);
509 if (!new)
510 goto fail;
511
512 strcpy(new, (const char *)session->context->observe_save_file->s);
513 strcat(new, ".tmp");
514 fp_new = fopen(new, "w+");
515 if (fp_new == NULL)
516 goto fail;
517
518 /* Go through and delete observe entry if it exists */
519 while (fp_orig) {
520 if (!coap_op_observe_read(fp_orig, &observe_key, &e_proto, &e_listen_addr,
521 &s_addr_info, &raw_packet, &oscore_info))
522 break;
523 if (observe_key != a_observe_key) {
524 if (!coap_op_observe_write(fp_new, observe_key, e_proto, e_listen_addr,
525 s_addr_info, raw_packet, oscore_info))
526 goto fail;
527 }
528 coap_delete_bin_const(raw_packet);
529 raw_packet = NULL;
530 coap_delete_bin_const(oscore_info);
531 oscore_info = NULL;
532 }
533 coap_delete_bin_const(raw_packet);
534 raw_packet = NULL;
535 coap_delete_bin_const(oscore_info);
536 oscore_info = NULL;
537
538 /* Add in new entry to the end */
539 if (!coap_op_observe_write(fp_new, a_observe_key, a_e_proto, *a_e_listen_addr,
540 *a_s_addr_info, a_raw_packet, a_oscore_info))
541 goto fail;
542
543 if (fflush(fp_new) == EOF)
544 goto fail;
545 fclose(fp_new);
546 if (fp_orig)
547 fclose(fp_orig);
548 /* Either old or new is in place */
549 (void)rename(new, (const char *)session->context->observe_save_file->s);
550 coap_free_type(COAP_STRING, new);
551 return 1;
552
553 fail:
554 coap_delete_bin_const(raw_packet);
555 coap_delete_bin_const(oscore_info);
556 if (fp_new)
557 fclose(fp_new);
558 if (fp_orig)
559 fclose(fp_orig);
560 if (new) {
561 (void)remove(new);
562 }
563 coap_free_type(COAP_STRING, new);
564 return 0;
565 }
566
567 /*
568 * client has de-registered a observe subscription request.
569 */
570 static int
coap_op_observe_deleted(coap_session_t * session,coap_subscription_t * d_observe_key,void * user_data)571 coap_op_observe_deleted(coap_session_t *session,
572 coap_subscription_t *d_observe_key,
573 void *user_data) {
574 FILE *fp_orig = fopen((const char *)session->context->observe_save_file->s,
575 "r");
576 FILE *fp_new = NULL;
577 coap_subscription_t *observe_key = NULL;
578 coap_proto_t e_proto;
579 coap_address_t e_listen_addr;
580 coap_addr_tuple_t s_addr_info;
581 coap_bin_const_t *raw_packet = NULL;
582 coap_bin_const_t *oscore_info = NULL;
583 char *new = NULL;
584
585 (void)user_data;
586
587 if (fp_orig == NULL)
588 goto fail;
589 new = coap_malloc_type(COAP_STRING,
590 session->context->observe_save_file->length + 5);
591 if (!new)
592 goto fail;
593
594 strcpy(new, (const char *)session->context->observe_save_file->s);
595 strcat(new, ".tmp");
596 fp_new = fopen(new, "w+");
597 if (fp_new == NULL)
598 goto fail;
599
600 /* Go through and locate observe entry to delete and not copy it across */
601 while (1) {
602 if (!coap_op_observe_read(fp_orig, &observe_key, &e_proto, &e_listen_addr,
603 &s_addr_info, &raw_packet, &oscore_info))
604 break;
605 if (observe_key != d_observe_key) {
606 if (!coap_op_observe_write(fp_new, observe_key, e_proto, e_listen_addr,
607 s_addr_info, (coap_bin_const_t *)raw_packet,
608 (coap_bin_const_t *)oscore_info))
609 goto fail;
610 }
611 coap_delete_bin_const(raw_packet);
612 raw_packet = NULL;
613 coap_delete_bin_const(oscore_info);
614 oscore_info = NULL;
615 }
616 coap_delete_bin_const(raw_packet);
617 raw_packet = NULL;
618 coap_delete_bin_const(oscore_info);
619 oscore_info = NULL;
620
621 if (fflush(fp_new) == EOF)
622 goto fail;
623 fclose(fp_new);
624 fclose(fp_orig);
625 /* Either old or new is in place */
626 (void)rename(new, (const char *)session->context->observe_save_file->s);
627 coap_free_type(COAP_STRING, new);
628 return 1;
629
630 fail:
631 coap_delete_bin_const(raw_packet);
632 coap_delete_bin_const(oscore_info);
633 if (fp_new)
634 fclose(fp_new);
635 if (fp_orig)
636 fclose(fp_orig);
637 if (new) {
638 (void)remove(new);
639 }
640 coap_free_type(COAP_STRING, new);
641 return 0;
642 }
643
644 /*
645 * This should be called before coap_persist_track_funcs() to prevent
646 * coap_op_obs_cnt_track_observe() getting unnecessarily called.
647 * Should be called after coap_op_dyn_resource_load_disk() to make sure that
648 * all the resources are in the right place.
649 */
650 static void
coap_op_obs_cnt_load_disk(coap_context_t * context)651 coap_op_obs_cnt_load_disk(coap_context_t *context) {
652 FILE *fp = fopen((const char *)context->obs_cnt_save_file->s, "r");
653 char buf[1500];
654
655 if (fp == NULL)
656 return;
657
658 while (fgets(buf, sizeof(buf), fp) != NULL) {
659 char *cp = strchr(buf, ' ');
660 coap_str_const_t resource_key;
661 uint32_t observe_num;
662 coap_resource_t *r;
663
664 if (!cp)
665 break;
666
667 *cp = '\000';
668 cp++;
669 observe_num = atoi(cp);
670 /*
671 * Need to assume 0 .. (context->observe_save_freq-1) have in addition
672 * been sent so need to round up to latest possible send value
673 */
674 observe_num = ((observe_num + context->observe_save_freq) /
675 context->observe_save_freq) *
676 context->observe_save_freq - 1;
677 resource_key.s = (uint8_t *)buf;
678 resource_key.length = strlen(buf);
679 r = coap_get_resource_from_uri_path(context, &resource_key);
680 if (r) {
681 coap_log_debug("persist: Initial observe number being updated\n");
682 coap_persist_set_observe_num(r, observe_num);
683 }
684 }
685 fclose(fp);
686 }
687
688 /*
689 * Called when the observe value of a resource has been changed, but limited
690 * to be called every context->context->observe_save_freq to reduce update
691 * overheads.
692 */
693 static int
coap_op_obs_cnt_track_observe(coap_context_t * context,coap_str_const_t * resource_name,uint32_t n_observe_num,void * user_data)694 coap_op_obs_cnt_track_observe(coap_context_t *context,
695 coap_str_const_t *resource_name,
696 uint32_t n_observe_num,
697 void *user_data) {
698 FILE *fp_orig = fopen((const char *)context->obs_cnt_save_file->s, "r");
699 FILE *fp_new = NULL;
700 char buf[1500];
701 char *new = NULL;
702
703 (void)user_data;
704
705 new = coap_malloc_type(COAP_STRING, context->obs_cnt_save_file->length + 5);
706 if (!new)
707 goto fail;
708
709 strcpy(new, (const char *)context->obs_cnt_save_file->s);
710 strcat(new, ".tmp");
711 fp_new = fopen(new, "w+");
712 if (fp_new == NULL)
713 goto fail;
714
715 /* Go through and locate resource entry to update */
716 while (fp_orig && fgets(buf, sizeof(buf), fp_orig) != NULL) {
717 char *cp = strchr(buf, ' ');
718 uint32_t observe_num;
719 coap_bin_const_t resource_key;
720
721 if (!cp)
722 break;
723
724 *cp = '\000';
725 cp++;
726 observe_num = atoi(cp);
727 resource_key.s = (uint8_t *)buf;
728 resource_key.length = strlen(buf);
729 if (!coap_binary_equal(resource_name, &resource_key)) {
730 if (fprintf(fp_new, "%s %u\n", resource_key.s, observe_num) < 0)
731 goto fail;
732 }
733 }
734 if (fprintf(fp_new, "%s %u\n", resource_name->s, n_observe_num) < 0)
735 goto fail;
736 if (fflush(fp_new) == EOF)
737 goto fail;
738 fclose(fp_new);
739 if (fp_orig)
740 fclose(fp_orig);
741 /* Either old or new is in place */
742 (void)rename(new, (const char *)context->obs_cnt_save_file->s);
743 coap_free_type(COAP_STRING, new);
744 return 1;
745
746 fail:
747 if (fp_new)
748 fclose(fp_new);
749 if (fp_orig)
750 fclose(fp_orig);
751 if (new) {
752 (void)remove(new);
753 }
754 coap_free_type(COAP_STRING, new);
755 return 0;
756 }
757
758 /*
759 * Called when a resource has been deleted.
760 */
761 static int
coap_op_obs_cnt_deleted(coap_context_t * context,coap_str_const_t * resource_name)762 coap_op_obs_cnt_deleted(coap_context_t *context,
763 coap_str_const_t *resource_name) {
764 FILE *fp_orig = fopen((const char *)context->obs_cnt_save_file->s, "r");
765 FILE *fp_new = NULL;
766 char buf[1500];
767 char *new = NULL;
768
769 if (fp_orig == NULL)
770 goto fail;
771 new = coap_malloc_type(COAP_STRING, context->obs_cnt_save_file->length + 5);
772 if (!new)
773 goto fail;
774
775 strcpy(new, (const char *)context->obs_cnt_save_file->s);
776 strcat(new, ".tmp");
777 fp_new = fopen(new, "w+");
778 if (fp_new == NULL)
779 goto fail;
780
781 /* Go through and locate resource entry to delete */
782 while (fgets(buf, sizeof(buf), fp_orig) != NULL) {
783 char *cp = strchr(buf, ' ');
784 uint32_t observe_num;
785 coap_bin_const_t resource_key;
786
787 if (!cp)
788 break;
789
790 *cp = '\000';
791 cp++;
792 observe_num = atoi(cp);
793 resource_key.s = (uint8_t *)buf;
794 resource_key.length = strlen(buf);
795 if (!coap_binary_equal(resource_name, &resource_key)) {
796 if (fprintf(fp_new, "%s %u\n", resource_key.s, observe_num) < 0)
797 goto fail;
798 }
799 }
800 if (fflush(fp_new) == EOF)
801 goto fail;
802 fclose(fp_new);
803 fclose(fp_orig);
804 /* Either old or new is in place */
805 (void)rename(new, (const char *)context->obs_cnt_save_file->s);
806 coap_free_type(COAP_STRING, new);
807 return 1;
808
809 fail:
810 if (fp_new)
811 fclose(fp_new);
812 if (fp_orig)
813 fclose(fp_orig);
814 if (new) {
815 (void)remove(new);
816 }
817 coap_free_type(COAP_STRING, new);
818 return 0;
819 }
820
821 /*
822 * read in dynamic resource entry, allocating name & raw_packet
823 * which need to be freed off by caller.
824 */
825 static int
coap_op_dyn_resource_read(FILE * fp,coap_proto_t * e_proto,coap_string_t ** name,coap_binary_t ** raw_packet)826 coap_op_dyn_resource_read(FILE *fp, coap_proto_t *e_proto,
827 coap_string_t **name,
828 coap_binary_t **raw_packet) {
829 ssize_t size;
830
831 *name = NULL;
832 *raw_packet = NULL;
833
834 if (fread(e_proto, sizeof(*e_proto), 1, fp) == 1) {
835 /* New record 'proto len resource_name len raw_packet' */
836 if (fread(&size, sizeof(size), 1, fp) != 1)
837 goto fail;
838 if (size < 0 || size > 0x10000)
839 goto fail;
840 *name = coap_new_string(size);
841 if (!(*name))
842 goto fail;
843 if (fread((*name)->s, size, 1, fp) != 1)
844 goto fail;
845 if (fread(&size, sizeof(size), 1, fp) != 1)
846 goto fail;
847 if (size < 0 || size > 0x10000)
848 goto fail;
849 *raw_packet = coap_new_binary(size);
850 if (!(*raw_packet))
851 goto fail;
852 if (fread((*raw_packet)->s, size, 1, fp) != 1)
853 goto fail;
854 return 1;
855 }
856 fail:
857 return 0;
858 }
859
860 /*
861 * write out dynamic resource entry.
862 */
863 static int
coap_op_dyn_resource_write(FILE * fp,coap_proto_t e_proto,coap_str_const_t * name,coap_bin_const_t * raw_packet)864 coap_op_dyn_resource_write(FILE *fp, coap_proto_t e_proto,
865 coap_str_const_t *name,
866 coap_bin_const_t *raw_packet) {
867 if (fwrite(&e_proto, sizeof(e_proto), 1, fp) != 1)
868 goto fail;
869 if (fwrite(&name->length, sizeof(name->length), 1, fp) != 1)
870 goto fail;
871 if (fwrite(name->s, name->length, 1, fp) != 1)
872 goto fail;
873 if (fwrite(&raw_packet->length, sizeof(raw_packet->length), 1, fp) != 1)
874 goto fail;
875 if (fwrite(raw_packet->s, raw_packet->length, 1, fp) != 1)
876 goto fail;
877 return 1;
878 fail:
879 return 0;
880 }
881
882 /*
883 * This should be called before coap_persist_track_funcs() to prevent
884 * coap_op_dyn_resource_added() getting unnecessarily called.
885 *
886 * Each record 'proto len resource_name len raw_packet'
887 */
888 static void
coap_op_dyn_resource_load_disk(coap_context_t * ctx)889 coap_op_dyn_resource_load_disk(coap_context_t *ctx) {
890 FILE *fp_orig = NULL;
891 coap_proto_t e_proto;
892 coap_string_t *name = NULL;
893 coap_binary_t *raw_packet = NULL;
894 coap_resource_t *r;
895 coap_session_t *session = NULL;
896 coap_pdu_t *request = NULL;
897 coap_pdu_t *response = NULL;
898 coap_string_t *query = NULL;
899
900 if (!ctx->unknown_resource)
901 return;
902
903 fp_orig = fopen((const char *)ctx->dyn_resource_save_file->s, "r");
904 if (fp_orig == NULL)
905 return;
906 session = (coap_session_t *)coap_malloc_type(COAP_SESSION,
907 sizeof(coap_session_t));
908 if (!session)
909 goto fail;
910 memset(session, 0, sizeof(coap_session_t));
911 session->context = ctx;
912
913 /* Go through and create each dynamic resource if it does not exist*/
914 while (1) {
915 if (!coap_op_dyn_resource_read(fp_orig, &e_proto, &name, &raw_packet))
916 break;
917 r = coap_get_resource_from_uri_path(ctx, (coap_str_const_t *)name);
918 if (!r) {
919 /* Create the new resource using the application logic */
920
921 coap_log_debug("persist: dynamic resource being re-created\n");
922 /*
923 * Need max space incase PDU is updated with updated token,
924 * huge size etc.
925 * */
926 request = coap_pdu_init(0, 0, 0, 0);
927 if (!request)
928 goto fail;
929
930 session->proto = e_proto;
931 if (!coap_pdu_parse(session->proto, raw_packet->s,
932 raw_packet->length, request)) {
933 goto fail;
934 }
935 if (!ctx->unknown_resource->handler[request->code-1])
936 goto fail;
937 response = coap_pdu_init(0, 0, 0, 0);
938 if (!response)
939 goto fail;
940 query = coap_get_query(request);
941 /* Call the application handler to set up this dynamic resource */
942 ctx->unknown_resource->handler[request->code-1](ctx->unknown_resource,
943 session, request,
944 query, response);
945 coap_delete_string(query);
946 query = NULL;
947 coap_delete_pdu(request);
948 request = NULL;
949 coap_delete_pdu(response);
950 response = NULL;
951 }
952 coap_delete_string(name);
953 coap_delete_binary(raw_packet);
954 }
955 fail:
956 coap_delete_string(name);
957 coap_delete_binary(raw_packet);
958 coap_delete_string(query);
959 coap_delete_pdu(request);
960 coap_delete_pdu(response);
961 fclose(fp_orig);
962 coap_free_type(COAP_SESSION, session);
963 }
964
965 /*
966 * Server has set up a new dynamic resource agains a request for an unknown
967 * resource.
968 */
969 static int
coap_op_dyn_resource_added(coap_session_t * session,coap_str_const_t * resource_name,coap_bin_const_t * packet,void * user_data)970 coap_op_dyn_resource_added(coap_session_t *session,
971 coap_str_const_t *resource_name,
972 coap_bin_const_t *packet,
973 void *user_data) {
974 FILE *fp_orig;
975 FILE *fp_new = NULL;
976 char *new = NULL;
977 coap_context_t *context = session->context;
978 coap_string_t *name = NULL;
979 coap_binary_t *raw_packet = NULL;
980 coap_proto_t e_proto;
981
982 (void)user_data;
983
984 fp_orig = fopen((const char *)context->dyn_resource_save_file->s, "a");
985 if (fp_orig == NULL)
986 return 0;
987
988 new = coap_malloc_type(COAP_STRING,
989 context->dyn_resource_save_file->length + 5);
990 if (!new)
991 goto fail;
992
993 strcpy(new, (const char *)context->dyn_resource_save_file->s);
994 strcat(new, ".tmp");
995 fp_new = fopen(new, "w+");
996 if (fp_new == NULL)
997 goto fail;
998
999 /* Go through and locate duplicate resource to delete */
1000 while (1) {
1001 if (!coap_op_dyn_resource_read(fp_orig, &e_proto, &name, &raw_packet))
1002 break;
1003 if (!coap_string_equal(resource_name, name)) {
1004 /* Copy across non-matching entry */
1005 if (!coap_op_dyn_resource_write(fp_new, e_proto, (coap_str_const_t *)name,
1006 (coap_bin_const_t *)raw_packet))
1007 break;
1008 }
1009 coap_delete_string(name);
1010 name = NULL;
1011 coap_delete_binary(raw_packet);
1012 raw_packet = NULL;
1013 }
1014 coap_delete_string(name);
1015 coap_delete_binary(raw_packet);
1016 /* Add new entry to the end */
1017 if (!coap_op_dyn_resource_write(fp_new, session->proto,
1018 resource_name, packet))
1019 goto fail;
1020
1021 if (fflush(fp_new) == EOF)
1022 goto fail;
1023 fclose(fp_new);
1024 fclose(fp_orig);
1025 /* Either old or new is in place */
1026 (void)rename(new, (const char *)context->dyn_resource_save_file->s);
1027 coap_free_type(COAP_STRING, new);
1028 return 1;
1029
1030 fail:
1031 if (fp_new)
1032 fclose(fp_new);
1033 if (fp_orig)
1034 fclose(fp_orig);
1035 if (new) {
1036 (void)remove(new);
1037 }
1038 coap_free_type(COAP_STRING, new);
1039 return 0;
1040 }
1041
1042 /*
1043 * Server has deleted a resource
1044 */
1045 static int
coap_op_resource_deleted(coap_context_t * context,coap_str_const_t * resource_name,void * user_data)1046 coap_op_resource_deleted(coap_context_t *context,
1047 coap_str_const_t *resource_name,
1048 void *user_data) {
1049 FILE *fp_orig = NULL;
1050 FILE *fp_new = NULL;
1051 char *new = NULL;
1052 coap_proto_t e_proto;
1053 coap_string_t *name = NULL;
1054 coap_binary_t *raw_packet = NULL;
1055 (void)user_data;
1056
1057 coap_op_obs_cnt_deleted(context, resource_name);
1058
1059 fp_orig = fopen((const char *)context->dyn_resource_save_file->s, "r");
1060 if (fp_orig == NULL)
1061 return 1;
1062
1063 new = coap_malloc_type(COAP_STRING,
1064 context->dyn_resource_save_file->length + 5);
1065 if (!new)
1066 goto fail;
1067
1068 strcpy(new, (const char *)context->dyn_resource_save_file->s);
1069 strcat(new, ".tmp");
1070 fp_new = fopen(new, "w+");
1071 if (fp_new == NULL)
1072 goto fail;
1073
1074 /* Go through and locate resource to delete and not copy it across */
1075 while (1) {
1076 if (!coap_op_dyn_resource_read(fp_orig, &e_proto, &name, &raw_packet))
1077 break;
1078 if (!coap_string_equal(resource_name, name)) {
1079 /* Copy across non-matching entry */
1080 if (!coap_op_dyn_resource_write(fp_new, e_proto, (coap_str_const_t *)name,
1081 (coap_bin_const_t *)raw_packet))
1082 break;
1083 }
1084 coap_delete_string(name);
1085 name = NULL;
1086 coap_delete_binary(raw_packet);
1087 raw_packet = NULL;
1088 }
1089 coap_delete_string(name);
1090 coap_delete_binary(raw_packet);
1091
1092 if (fflush(fp_new) == EOF)
1093 goto fail;
1094 fclose(fp_new);
1095 fclose(fp_orig);
1096 /* Either old or new is in place */
1097 (void)rename(new, (const char *)context->dyn_resource_save_file->s);
1098 coap_free_type(COAP_STRING, new);
1099 return 1;
1100
1101 fail:
1102 if (fp_new)
1103 fclose(fp_new);
1104 if (fp_orig)
1105 fclose(fp_orig);
1106 if (new) {
1107 (void)remove(new);
1108 }
1109 coap_free_type(COAP_STRING, new);
1110 return 0;
1111 }
1112
1113 int
coap_persist_startup(coap_context_t * context,const char * dyn_resource_save_file,const char * observe_save_file,const char * obs_cnt_save_file,uint32_t save_freq)1114 coap_persist_startup(coap_context_t *context,
1115 const char *dyn_resource_save_file,
1116 const char *observe_save_file,
1117 const char *obs_cnt_save_file,
1118 uint32_t save_freq) {
1119 if (dyn_resource_save_file) {
1120 context->dyn_resource_save_file =
1121 coap_new_bin_const((const uint8_t *)dyn_resource_save_file,
1122 strlen(dyn_resource_save_file));
1123 if (!context->dyn_resource_save_file)
1124 return 0;
1125 coap_op_dyn_resource_load_disk(context);
1126 context->dyn_resource_added = coap_op_dyn_resource_added;
1127 context->resource_deleted = coap_op_resource_deleted;
1128 }
1129 if (obs_cnt_save_file) {
1130 context->obs_cnt_save_file =
1131 coap_new_bin_const((const uint8_t *)obs_cnt_save_file,
1132 strlen(obs_cnt_save_file));
1133 if (!context->obs_cnt_save_file)
1134 return 0;
1135 context->observe_save_freq = save_freq ? save_freq : 1;
1136 coap_op_obs_cnt_load_disk(context);
1137 context->track_observe_value = coap_op_obs_cnt_track_observe;
1138 context->resource_deleted = coap_op_resource_deleted;
1139 }
1140 if (observe_save_file) {
1141 context->observe_save_file =
1142 coap_new_bin_const((const uint8_t *)observe_save_file,
1143 strlen(observe_save_file));
1144 if (!context->observe_save_file)
1145 return 0;
1146 coap_op_observe_load_disk(context);
1147 context->observe_added = coap_op_observe_added;
1148 context->observe_deleted = coap_op_observe_deleted;
1149 }
1150 return 1;
1151 }
1152
1153 void
coap_persist_cleanup(coap_context_t * context)1154 coap_persist_cleanup(coap_context_t *context) {
1155 coap_delete_bin_const(context->dyn_resource_save_file);
1156 coap_delete_bin_const(context->obs_cnt_save_file);
1157 coap_delete_bin_const(context->observe_save_file);
1158 context->dyn_resource_save_file = NULL;
1159 context->obs_cnt_save_file = NULL;
1160 context->observe_save_file = NULL;
1161
1162 /* Close down any tracking */
1163 coap_persist_track_funcs(context, NULL, NULL, NULL, NULL,
1164 NULL, 0, NULL);
1165 }
1166
1167 void
coap_persist_stop(coap_context_t * context)1168 coap_persist_stop(coap_context_t *context) {
1169 if (context == NULL)
1170 return;
1171 context->observe_no_clear = 1;
1172 coap_persist_cleanup(context);
1173 }
1174 #else /* ! COAP_WITH_OBSERVE_PERSIST */
1175 int
coap_persist_startup(coap_context_t * context,const char * dyn_resource_save_file,const char * observe_save_file,const char * obs_cnt_save_file,uint32_t save_freq)1176 coap_persist_startup(coap_context_t *context,
1177 const char *dyn_resource_save_file,
1178 const char *observe_save_file,
1179 const char *obs_cnt_save_file,
1180 uint32_t save_freq) {
1181 (void)context;
1182 (void)dyn_resource_save_file;
1183 (void)observe_save_file;
1184 (void)obs_cnt_save_file;
1185 (void)save_freq;
1186 return 0;
1187 }
1188
1189 void
coap_persist_stop(coap_context_t * context)1190 coap_persist_stop(coap_context_t *context) {
1191 context->observe_no_clear = 1;
1192 /* Close down any tracking */
1193 coap_persist_track_funcs(context, NULL, NULL, NULL, NULL,
1194 NULL, 0, NULL);
1195 }
1196
1197 #endif /* ! COAP_WITH_OBSERVE_PERSIST */
1198
1199 #endif /* COAP_SERVER_SUPPORT */
1200