1c87c5fbaSopenharmony_ci/* coap_cache.c -- Caching of CoAP requests 2c87c5fbaSopenharmony_ci* 3c87c5fbaSopenharmony_ci* Copyright (C) 2020-2023 Olaf Bergmann <bergmann@tzi.org> 4c87c5fbaSopenharmony_ci* 5c87c5fbaSopenharmony_ci * SPDX-License-Identifier: BSD-2-Clause 6c87c5fbaSopenharmony_ci * 7c87c5fbaSopenharmony_ci* This file is part of the CoAP library libcoap. Please see 8c87c5fbaSopenharmony_ci* README for terms of use. 9c87c5fbaSopenharmony_ci*/ 10c87c5fbaSopenharmony_ci 11c87c5fbaSopenharmony_ci/** 12c87c5fbaSopenharmony_ci * @file coap_cache.c 13c87c5fbaSopenharmony_ci * @brief CoAP Cache handling 14c87c5fbaSopenharmony_ci */ 15c87c5fbaSopenharmony_ci 16c87c5fbaSopenharmony_ci#include "coap3/coap_internal.h" 17c87c5fbaSopenharmony_ci 18c87c5fbaSopenharmony_ci#if COAP_SERVER_SUPPORT 19c87c5fbaSopenharmony_ci/* Determines if the given option_type denotes an option type that can 20c87c5fbaSopenharmony_ci * be used as CacheKey. Options that can be cache keys are not Unsafe 21c87c5fbaSopenharmony_ci * and not marked explicitly as NoCacheKey. */ 22c87c5fbaSopenharmony_cistatic int 23c87c5fbaSopenharmony_ciis_cache_key(uint16_t option_type, size_t cache_ignore_count, 24c87c5fbaSopenharmony_ci const uint16_t *cache_ignore_options) { 25c87c5fbaSopenharmony_ci size_t i; 26c87c5fbaSopenharmony_ci 27c87c5fbaSopenharmony_ci /* https://rfc-editor.org/rfc/rfc7252#section-5.4.6 Nocachekey definition */ 28c87c5fbaSopenharmony_ci if ((option_type & 0x1e) == 0x1c) 29c87c5fbaSopenharmony_ci return 0; 30c87c5fbaSopenharmony_ci /* 31c87c5fbaSopenharmony_ci * https://rfc-editor.org/rfc/rfc7641#section-2 Observe is not a 32c87c5fbaSopenharmony_ci * part of the cache-key. 33c87c5fbaSopenharmony_ci */ 34c87c5fbaSopenharmony_ci if (option_type == COAP_OPTION_OBSERVE) 35c87c5fbaSopenharmony_ci return 0; 36c87c5fbaSopenharmony_ci 37c87c5fbaSopenharmony_ci /* Check for option user has defined as not part of cache-key */ 38c87c5fbaSopenharmony_ci for (i = 0; i < cache_ignore_count; i++) { 39c87c5fbaSopenharmony_ci if (cache_ignore_options[i] == option_type) { 40c87c5fbaSopenharmony_ci return 0; 41c87c5fbaSopenharmony_ci } 42c87c5fbaSopenharmony_ci } 43c87c5fbaSopenharmony_ci 44c87c5fbaSopenharmony_ci return 1; 45c87c5fbaSopenharmony_ci} 46c87c5fbaSopenharmony_ci 47c87c5fbaSopenharmony_ciint 48c87c5fbaSopenharmony_cicoap_cache_ignore_options(coap_context_t *ctx, 49c87c5fbaSopenharmony_ci const uint16_t *options, 50c87c5fbaSopenharmony_ci size_t count) { 51c87c5fbaSopenharmony_ci if (ctx->cache_ignore_options) { 52c87c5fbaSopenharmony_ci coap_free_type(COAP_STRING, ctx->cache_ignore_options); 53c87c5fbaSopenharmony_ci } 54c87c5fbaSopenharmony_ci if (count) { 55c87c5fbaSopenharmony_ci assert(options); 56c87c5fbaSopenharmony_ci ctx->cache_ignore_options = coap_malloc_type(COAP_STRING, count * sizeof(options[0])); 57c87c5fbaSopenharmony_ci if (ctx->cache_ignore_options) { 58c87c5fbaSopenharmony_ci memcpy(ctx->cache_ignore_options, options, count * sizeof(options[0])); 59c87c5fbaSopenharmony_ci ctx->cache_ignore_count = count; 60c87c5fbaSopenharmony_ci } else { 61c87c5fbaSopenharmony_ci coap_log_warn("Unable to create cache_ignore_options\n"); 62c87c5fbaSopenharmony_ci return 0; 63c87c5fbaSopenharmony_ci } 64c87c5fbaSopenharmony_ci } else { 65c87c5fbaSopenharmony_ci ctx->cache_ignore_options = NULL; 66c87c5fbaSopenharmony_ci ctx->cache_ignore_count = count; 67c87c5fbaSopenharmony_ci } 68c87c5fbaSopenharmony_ci return 1; 69c87c5fbaSopenharmony_ci} 70c87c5fbaSopenharmony_ci 71c87c5fbaSopenharmony_cicoap_cache_key_t * 72c87c5fbaSopenharmony_cicoap_cache_derive_key_w_ignore(const coap_session_t *session, 73c87c5fbaSopenharmony_ci const coap_pdu_t *pdu, 74c87c5fbaSopenharmony_ci coap_cache_session_based_t session_based, 75c87c5fbaSopenharmony_ci const uint16_t *cache_ignore_options, 76c87c5fbaSopenharmony_ci size_t cache_ignore_count) { 77c87c5fbaSopenharmony_ci coap_opt_t *option; 78c87c5fbaSopenharmony_ci coap_opt_iterator_t opt_iter; 79c87c5fbaSopenharmony_ci coap_digest_ctx_t *dctx; 80c87c5fbaSopenharmony_ci coap_digest_t digest; 81c87c5fbaSopenharmony_ci coap_cache_key_t *cache_key; 82c87c5fbaSopenharmony_ci 83c87c5fbaSopenharmony_ci if (!coap_option_iterator_init(pdu, &opt_iter, COAP_OPT_ALL)) { 84c87c5fbaSopenharmony_ci return NULL; 85c87c5fbaSopenharmony_ci } 86c87c5fbaSopenharmony_ci 87c87c5fbaSopenharmony_ci dctx = coap_digest_setup(); 88c87c5fbaSopenharmony_ci if (!dctx) 89c87c5fbaSopenharmony_ci return NULL; 90c87c5fbaSopenharmony_ci 91c87c5fbaSopenharmony_ci if (session_based == COAP_CACHE_IS_SESSION_BASED) { 92c87c5fbaSopenharmony_ci /* Include the session ptr */ 93c87c5fbaSopenharmony_ci if (!coap_digest_update(dctx, (const uint8_t *)&session, sizeof(session))) { 94c87c5fbaSopenharmony_ci goto update_fail; 95c87c5fbaSopenharmony_ci } 96c87c5fbaSopenharmony_ci } 97c87c5fbaSopenharmony_ci while ((option = coap_option_next(&opt_iter))) { 98c87c5fbaSopenharmony_ci if (is_cache_key(opt_iter.number, cache_ignore_count, 99c87c5fbaSopenharmony_ci cache_ignore_options)) { 100c87c5fbaSopenharmony_ci if (!coap_digest_update(dctx, (const uint8_t *)&opt_iter.number, 101c87c5fbaSopenharmony_ci sizeof(opt_iter.number))) { 102c87c5fbaSopenharmony_ci goto update_fail; 103c87c5fbaSopenharmony_ci } 104c87c5fbaSopenharmony_ci if (!coap_digest_update(dctx, coap_opt_value(option), 105c87c5fbaSopenharmony_ci coap_opt_length(option))) { 106c87c5fbaSopenharmony_ci goto update_fail; 107c87c5fbaSopenharmony_ci } 108c87c5fbaSopenharmony_ci } 109c87c5fbaSopenharmony_ci } 110c87c5fbaSopenharmony_ci 111c87c5fbaSopenharmony_ci /* The body of a FETCH payload is part of the cache key, 112c87c5fbaSopenharmony_ci * see https://rfc-editor.org/rfc/rfc8132#section-2 */ 113c87c5fbaSopenharmony_ci if (pdu->code == COAP_REQUEST_CODE_FETCH) { 114c87c5fbaSopenharmony_ci size_t len; 115c87c5fbaSopenharmony_ci const uint8_t *data; 116c87c5fbaSopenharmony_ci if (coap_get_data(pdu, &len, &data)) { 117c87c5fbaSopenharmony_ci if (!coap_digest_update(dctx, data, len)) { 118c87c5fbaSopenharmony_ci goto update_fail; 119c87c5fbaSopenharmony_ci } 120c87c5fbaSopenharmony_ci } 121c87c5fbaSopenharmony_ci } 122c87c5fbaSopenharmony_ci 123c87c5fbaSopenharmony_ci if (!coap_digest_final(dctx, &digest)) { 124c87c5fbaSopenharmony_ci /* coap_digest_final() is guaranteed to free off dctx no matter what */ 125c87c5fbaSopenharmony_ci return NULL; 126c87c5fbaSopenharmony_ci } 127c87c5fbaSopenharmony_ci cache_key = coap_malloc_type(COAP_CACHE_KEY, sizeof(coap_cache_key_t)); 128c87c5fbaSopenharmony_ci if (cache_key) { 129c87c5fbaSopenharmony_ci memcpy(cache_key->key, digest.key, sizeof(cache_key->key)); 130c87c5fbaSopenharmony_ci } 131c87c5fbaSopenharmony_ci return cache_key; 132c87c5fbaSopenharmony_ciupdate_fail: 133c87c5fbaSopenharmony_ci coap_digest_free(dctx); 134c87c5fbaSopenharmony_ci return NULL; 135c87c5fbaSopenharmony_ci} 136c87c5fbaSopenharmony_ci 137c87c5fbaSopenharmony_cicoap_cache_key_t * 138c87c5fbaSopenharmony_cicoap_cache_derive_key(const coap_session_t *session, 139c87c5fbaSopenharmony_ci const coap_pdu_t *pdu, 140c87c5fbaSopenharmony_ci coap_cache_session_based_t session_based) { 141c87c5fbaSopenharmony_ci return coap_cache_derive_key_w_ignore(session, pdu, session_based, 142c87c5fbaSopenharmony_ci session->context->cache_ignore_options, 143c87c5fbaSopenharmony_ci session->context->cache_ignore_count); 144c87c5fbaSopenharmony_ci} 145c87c5fbaSopenharmony_ci 146c87c5fbaSopenharmony_civoid 147c87c5fbaSopenharmony_cicoap_delete_cache_key(coap_cache_key_t *cache_key) { 148c87c5fbaSopenharmony_ci coap_free_type(COAP_CACHE_KEY, cache_key); 149c87c5fbaSopenharmony_ci} 150c87c5fbaSopenharmony_ci 151c87c5fbaSopenharmony_cicoap_cache_entry_t * 152c87c5fbaSopenharmony_cicoap_new_cache_entry(coap_session_t *session, const coap_pdu_t *pdu, 153c87c5fbaSopenharmony_ci coap_cache_record_pdu_t record_pdu, 154c87c5fbaSopenharmony_ci coap_cache_session_based_t session_based, 155c87c5fbaSopenharmony_ci unsigned int idle_timeout) { 156c87c5fbaSopenharmony_ci coap_cache_entry_t *entry = coap_malloc_type(COAP_CACHE_ENTRY, 157c87c5fbaSopenharmony_ci sizeof(coap_cache_entry_t)); 158c87c5fbaSopenharmony_ci if (!entry) { 159c87c5fbaSopenharmony_ci return NULL; 160c87c5fbaSopenharmony_ci } 161c87c5fbaSopenharmony_ci 162c87c5fbaSopenharmony_ci memset(entry, 0, sizeof(coap_cache_entry_t)); 163c87c5fbaSopenharmony_ci entry->session = session; 164c87c5fbaSopenharmony_ci if (record_pdu == COAP_CACHE_RECORD_PDU) { 165c87c5fbaSopenharmony_ci entry->pdu = coap_pdu_init(pdu->type, pdu->code, pdu->mid, pdu->alloc_size); 166c87c5fbaSopenharmony_ci if (entry->pdu) { 167c87c5fbaSopenharmony_ci if (!coap_pdu_resize(entry->pdu, pdu->alloc_size)) { 168c87c5fbaSopenharmony_ci coap_delete_pdu(entry->pdu); 169c87c5fbaSopenharmony_ci coap_free_type(COAP_CACHE_ENTRY, entry); 170c87c5fbaSopenharmony_ci return NULL; 171c87c5fbaSopenharmony_ci } 172c87c5fbaSopenharmony_ci /* Need to get the appropriate data across */ 173c87c5fbaSopenharmony_ci memcpy(entry->pdu, pdu, offsetof(coap_pdu_t, token)); 174c87c5fbaSopenharmony_ci memcpy(entry->pdu->token, pdu->token, pdu->used_size); 175c87c5fbaSopenharmony_ci /* And adjust all the pointers etc. */ 176c87c5fbaSopenharmony_ci entry->pdu->data = entry->pdu->token + (pdu->data - pdu->token); 177c87c5fbaSopenharmony_ci } 178c87c5fbaSopenharmony_ci } 179c87c5fbaSopenharmony_ci entry->cache_key = coap_cache_derive_key(session, pdu, session_based); 180c87c5fbaSopenharmony_ci if (!entry->cache_key) { 181c87c5fbaSopenharmony_ci coap_free_type(COAP_CACHE_ENTRY, entry); 182c87c5fbaSopenharmony_ci return NULL; 183c87c5fbaSopenharmony_ci } 184c87c5fbaSopenharmony_ci entry->idle_timeout = idle_timeout; 185c87c5fbaSopenharmony_ci if (idle_timeout > 0) { 186c87c5fbaSopenharmony_ci coap_ticks(&entry->expire_ticks); 187c87c5fbaSopenharmony_ci entry->expire_ticks += idle_timeout * COAP_TICKS_PER_SECOND; 188c87c5fbaSopenharmony_ci } 189c87c5fbaSopenharmony_ci 190c87c5fbaSopenharmony_ci HASH_ADD(hh, session->context->cache, cache_key[0], sizeof(coap_cache_key_t), entry); 191c87c5fbaSopenharmony_ci return entry; 192c87c5fbaSopenharmony_ci} 193c87c5fbaSopenharmony_ci 194c87c5fbaSopenharmony_cicoap_cache_entry_t * 195c87c5fbaSopenharmony_cicoap_cache_get_by_key(coap_context_t *ctx, const coap_cache_key_t *cache_key) { 196c87c5fbaSopenharmony_ci coap_cache_entry_t *cache_entry = NULL; 197c87c5fbaSopenharmony_ci 198c87c5fbaSopenharmony_ci assert(cache_key); 199c87c5fbaSopenharmony_ci if (cache_key) { 200c87c5fbaSopenharmony_ci HASH_FIND(hh, ctx->cache, cache_key, sizeof(coap_cache_key_t), cache_entry); 201c87c5fbaSopenharmony_ci } 202c87c5fbaSopenharmony_ci if (cache_entry && cache_entry->idle_timeout > 0) { 203c87c5fbaSopenharmony_ci coap_ticks(&cache_entry->expire_ticks); 204c87c5fbaSopenharmony_ci cache_entry->expire_ticks += cache_entry->idle_timeout * COAP_TICKS_PER_SECOND; 205c87c5fbaSopenharmony_ci } 206c87c5fbaSopenharmony_ci return cache_entry; 207c87c5fbaSopenharmony_ci} 208c87c5fbaSopenharmony_ci 209c87c5fbaSopenharmony_cicoap_cache_entry_t * 210c87c5fbaSopenharmony_cicoap_cache_get_by_pdu(coap_session_t *session, 211c87c5fbaSopenharmony_ci const coap_pdu_t *request, 212c87c5fbaSopenharmony_ci coap_cache_session_based_t session_based) { 213c87c5fbaSopenharmony_ci coap_cache_key_t *cache_key = coap_cache_derive_key(session, request, session_based); 214c87c5fbaSopenharmony_ci coap_cache_entry_t *cache_entry; 215c87c5fbaSopenharmony_ci 216c87c5fbaSopenharmony_ci if (!cache_key) 217c87c5fbaSopenharmony_ci return NULL; 218c87c5fbaSopenharmony_ci 219c87c5fbaSopenharmony_ci cache_entry = coap_cache_get_by_key(session->context, cache_key); 220c87c5fbaSopenharmony_ci coap_delete_cache_key(cache_key); 221c87c5fbaSopenharmony_ci if (cache_entry && cache_entry->idle_timeout > 0) { 222c87c5fbaSopenharmony_ci coap_ticks(&cache_entry->expire_ticks); 223c87c5fbaSopenharmony_ci cache_entry->expire_ticks += cache_entry->idle_timeout * COAP_TICKS_PER_SECOND; 224c87c5fbaSopenharmony_ci } 225c87c5fbaSopenharmony_ci return cache_entry; 226c87c5fbaSopenharmony_ci} 227c87c5fbaSopenharmony_ci 228c87c5fbaSopenharmony_civoid 229c87c5fbaSopenharmony_cicoap_delete_cache_entry(coap_context_t *ctx, coap_cache_entry_t *cache_entry) { 230c87c5fbaSopenharmony_ci 231c87c5fbaSopenharmony_ci assert(cache_entry); 232c87c5fbaSopenharmony_ci 233c87c5fbaSopenharmony_ci if (cache_entry) { 234c87c5fbaSopenharmony_ci HASH_DELETE(hh, ctx->cache, cache_entry); 235c87c5fbaSopenharmony_ci } 236c87c5fbaSopenharmony_ci if (cache_entry->pdu) { 237c87c5fbaSopenharmony_ci coap_delete_pdu(cache_entry->pdu); 238c87c5fbaSopenharmony_ci } 239c87c5fbaSopenharmony_ci coap_delete_cache_key(cache_entry->cache_key); 240c87c5fbaSopenharmony_ci if (cache_entry->callback && cache_entry->app_data) { 241c87c5fbaSopenharmony_ci cache_entry->callback(cache_entry->app_data); 242c87c5fbaSopenharmony_ci } 243c87c5fbaSopenharmony_ci coap_free_type(COAP_CACHE_ENTRY, cache_entry); 244c87c5fbaSopenharmony_ci} 245c87c5fbaSopenharmony_ci 246c87c5fbaSopenharmony_ciconst coap_pdu_t * 247c87c5fbaSopenharmony_cicoap_cache_get_pdu(const coap_cache_entry_t *cache_entry) { 248c87c5fbaSopenharmony_ci return cache_entry->pdu; 249c87c5fbaSopenharmony_ci} 250c87c5fbaSopenharmony_ci 251c87c5fbaSopenharmony_civoid 252c87c5fbaSopenharmony_cicoap_cache_set_app_data(coap_cache_entry_t *cache_entry, 253c87c5fbaSopenharmony_ci void *data, 254c87c5fbaSopenharmony_ci coap_cache_app_data_free_callback_t callback) { 255c87c5fbaSopenharmony_ci cache_entry->app_data = data; 256c87c5fbaSopenharmony_ci cache_entry->callback = callback; 257c87c5fbaSopenharmony_ci} 258c87c5fbaSopenharmony_ci 259c87c5fbaSopenharmony_civoid * 260c87c5fbaSopenharmony_cicoap_cache_get_app_data(const coap_cache_entry_t *cache_entry) { 261c87c5fbaSopenharmony_ci return cache_entry->app_data; 262c87c5fbaSopenharmony_ci} 263c87c5fbaSopenharmony_ci 264c87c5fbaSopenharmony_civoid 265c87c5fbaSopenharmony_cicoap_expire_cache_entries(coap_context_t *ctx) { 266c87c5fbaSopenharmony_ci coap_tick_t now; 267c87c5fbaSopenharmony_ci coap_cache_entry_t *cp, *ctmp; 268c87c5fbaSopenharmony_ci 269c87c5fbaSopenharmony_ci coap_ticks(&now); 270c87c5fbaSopenharmony_ci HASH_ITER(hh, ctx->cache, cp, ctmp) { 271c87c5fbaSopenharmony_ci if (cp->idle_timeout > 0) { 272c87c5fbaSopenharmony_ci if (cp->expire_ticks <= now) { 273c87c5fbaSopenharmony_ci coap_delete_cache_entry(ctx, cp); 274c87c5fbaSopenharmony_ci } 275c87c5fbaSopenharmony_ci } 276c87c5fbaSopenharmony_ci } 277c87c5fbaSopenharmony_ci} 278c87c5fbaSopenharmony_ci 279c87c5fbaSopenharmony_ci#endif /* ! COAP_SERVER_SUPPORT */ 280