• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(&not_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