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 24void 25coap_subscription_init(coap_subscription_t *s) { 26 assert(s); 27 memset(s, 0, sizeof(coap_subscription_t)); 28} 29 30void 31coap_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 48coap_subscription_t * 49coap_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 } 278oscore_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 288malformed: 289 coap_log_warn("coap_persist_observe_add: discard malformed PDU\n"); 290fail: 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 */ 305static int 306coap_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 } 355fail: 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 */ 370static int 371coap_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; 400fail: 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 */ 410static void 411coap_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 470fail: 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 */ 487static int 488coap_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 553fail: 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 */ 570static int 571coap_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 630fail: 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 */ 650static void 651coap_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 */ 693static int 694coap_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 746fail: 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 */ 761static int 762coap_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 809fail: 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 */ 825static int 826coap_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 } 856fail: 857 return 0; 858} 859 860/* 861 * write out dynamic resource entry. 862 */ 863static int 864coap_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; 878fail: 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 */ 888static void 889coap_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 } 955fail: 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 */ 969static int 970coap_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 1030fail: 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 */ 1045static int 1046coap_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 1101fail: 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 1113int 1114coap_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 1153void 1154coap_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 1167void 1168coap_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 */ 1175int 1176coap_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 1189void 1190coap_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