diff --git a/api/cloud/rd_client.c b/api/cloud/rd_client.c index 6886309da..05ab7dfc9 100644 --- a/api/cloud/rd_client.c +++ b/api/cloud/rd_client.c @@ -43,7 +43,8 @@ _add_resource_payload(CborEncoder *parent, oc_resource_t *resource, oc_rep_start_object(parent, links); oc_rep_set_text_string(links, href, oc_string(resource->uri)); oc_rep_set_string_array(links, rt, resource->types); - oc_core_encode_interfaces_mask(oc_rep_object(links), resource->interfaces); + oc_core_encode_interfaces_mask(oc_rep_object(links), resource->interfaces, + false); if (rel) oc_rep_set_text_string(links, rel, rel); oc_rep_set_int(links, ins, ins); diff --git a/api/oc_collection.c b/api/oc_collection.c index beae2cca4..b3362eef8 100644 --- a/api/oc_collection.c +++ b/api/oc_collection.c @@ -518,7 +518,7 @@ oc_handle_collection_create_request(oc_method_t method, oc_request_t *request) oc_string_len(new_res->resource->uri)); oc_rep_set_string_array(root, rt, new_res->resource->types); oc_core_encode_interfaces_mask(oc_rep_object(root), - new_res->resource->interfaces); + new_res->resource->interfaces, false); oc_rep_set_object(root, p); oc_rep_set_uint(p, bm, (uint8_t)(bm & ~(OC_PERIODIC | OC_SECURE))); oc_rep_close_object(root, p); @@ -676,7 +676,8 @@ collection_encode_links(const oc_collection_t *collection, oc_rep_set_text_string_v1(links, href, oc_string(link->resource->uri), oc_string_len(link->resource->uri)); oc_rep_set_string_array(links, rt, link->resource->types); - oc_core_encode_interfaces_mask(oc_rep_object(links), link->interfaces); + oc_core_encode_interfaces_mask(oc_rep_object(links), link->interfaces, + false); oc_rep_set_string_array(links, rel, link->rel); oc_rep_set_int(links, ins, link->ins); oc_link_params_t *p = (oc_link_params_t *)oc_list_head(link->params); @@ -814,7 +815,8 @@ oc_handle_collection_linked_list_request(oc_request_t *request) oc_rep_set_text_string_v1(links, href, oc_string(link->resource->uri), oc_string_len(link->resource->uri)); oc_rep_set_string_array(links, rt, link->resource->types); - oc_core_encode_interfaces_mask(oc_rep_object(links), link->interfaces); + oc_core_encode_interfaces_mask(oc_rep_object(links), link->interfaces, + false); oc_rep_set_string_array(links, rel, link->rel); oc_rep_set_int(links, ins, link->ins); oc_link_params_t *p = (oc_link_params_t *)oc_list_head(link->params); diff --git a/api/oc_core_res.c b/api/oc_core_res.c index c40af5719..27aa05544 100644 --- a/api/oc_core_res.c +++ b/api/oc_core_res.c @@ -164,7 +164,8 @@ oc_core_shutdown(void) } void -oc_core_encode_interfaces_mask(CborEncoder *parent, unsigned iface_mask) +oc_core_encode_interfaces_mask(CborEncoder *parent, unsigned iface_mask, + bool include_private) { oc_rep_set_key((parent), "if"); oc_rep_start_array((parent), if); @@ -201,6 +202,13 @@ oc_core_encode_interfaces_mask(CborEncoder *parent, unsigned iface_mask) if ((iface_mask & OC_IF_STARTUP_REVERT) != 0) { oc_rep_add_text_string(if, OC_IF_STARTUP_REVERT_STR); } +#ifdef OC_HAS_FEATURE_ETAG_INTERFACE + if (include_private && (iface_mask & PLGD_IF_ETAG) != 0) { + oc_rep_add_text_string(if, PLGD_IF_ETAG_STR); + } +#else /* OC_HAS_FEATURE_ETAG_INTERFACE */ + (void)include_private; +#endif /* OC_HAS_FEATURE_ETAG_INTERFACE */ oc_rep_end_array((parent), if); } diff --git a/api/oc_core_res_internal.h b/api/oc_core_res_internal.h index f76c1a573..dc0059101 100644 --- a/api/oc_core_res_internal.h +++ b/api/oc_core_res_internal.h @@ -71,9 +71,10 @@ oc_device_info_t *oc_core_add_new_device(oc_add_new_device_t cfg); * * @param parent the cbor encoder (cannot be NULL) * @param iface_mask the interfaces (as bit mask) + * @param include_private include private interfaces */ -void oc_core_encode_interfaces_mask(CborEncoder *parent, unsigned iface_mask) - OC_NONNULL(); +void oc_core_encode_interfaces_mask(CborEncoder *parent, unsigned iface_mask, + bool include_private) OC_NONNULL(); /** * @brief store the uri as a string diff --git a/api/oc_discovery.c b/api/oc_discovery.c index c3118d9ef..492cd86f5 100644 --- a/api/oc_discovery.c +++ b/api/oc_discovery.c @@ -140,9 +140,8 @@ discovery_encode_endpoint(CborEncoder *eps, const oc_endpoint_t *ep, } static void -discovery_encode_endpoints(const oc_resource_t *resource, - const oc_request_t *request, size_t device_index, - CborEncoder *link) +discovery_encode_endpoints(CborEncoder *link, const oc_resource_t *resource, + const oc_request_t *request, size_t device_index) { g_err |= oc_rep_encode_text_string(link, "eps", OC_CHAR_ARRAY_LEN("eps")); oc_rep_begin_array(link, eps); @@ -184,8 +183,9 @@ discovery_encode_endpoints(const oc_resource_t *resource, } static bool -process_resource(const oc_resource_t *resource, const oc_request_t *request, - const char *anchor, size_t anchor_len, CborEncoder *links) +encode_resource(CborEncoder *links, const oc_resource_t *resource, + const oc_request_t *request, oc_string_view_t anchor, + bool include_endpoints) { if (resource == NULL || !oc_filter_resource_by_rt(resource, request) || (resource->properties & OC_DISCOVERABLE) == 0) { @@ -202,7 +202,7 @@ process_resource(const oc_resource_t *resource, const oc_request_t *request, } // anchor - oc_rep_set_text_string_v1(link, anchor, anchor, anchor_len); + oc_rep_set_text_string_v1(link, anchor, anchor.data, anchor.length); // uri oc_rep_set_text_string_v1(link, href, oc_string(resource->uri), @@ -221,7 +221,8 @@ process_resource(const oc_resource_t *resource, const oc_request_t *request, oc_rep_close_array(link, rt); // if - oc_core_encode_interfaces_mask(oc_rep_object(link), resource->interfaces); + oc_core_encode_interfaces_mask(oc_rep_object(link), resource->interfaces, + false); // p oc_rep_set_object(link, p); @@ -229,10 +230,11 @@ process_resource(const oc_resource_t *resource, const oc_request_t *request, (uint8_t)(resource->properties & OCF_RES_POLICY_PROPERTIES)); oc_rep_close_object(link, p); - size_t device_index = request->resource->device; - // eps - discovery_encode_endpoints(resource, request, device_index, - oc_rep_object(link)); + if (include_endpoints) { + // eps + discovery_encode_endpoints(oc_rep_object(link), resource, request, + request->resource->device); + } // tag-pos-desc if (resource->tag_pos_desc > 0) { @@ -280,123 +282,73 @@ process_resource(const oc_resource_t *resource, const oc_request_t *request, } static int -process_device_core_resources(const oc_request_t *request, const char *anchor, - size_t anchor_len, CborEncoder *links) +encode_device_core_resources(CborEncoder *links, const oc_request_t *request, + oc_string_view_t anchor, bool include_endpoints) { int matches = 0; - if (process_resource(oc_core_get_resource_by_index(OCF_P, 0), request, anchor, - anchor_len, links)) { - matches++; - } + oc_core_resource_t platformRes[] = { + OCF_P, #ifdef OC_HAS_FEATURE_PLGD_TIME - if (process_resource(oc_core_get_resource_by_index(PLGD_TIME, 0), request, - anchor, anchor_len, links)) { - matches++; - } + PLGD_TIME, #endif /* OC_HAS_FEATURE_PLGD_TIME */ - size_t device_index = request->resource->device; - if (process_resource(oc_core_get_resource_by_index(OCF_RES, device_index), - request, anchor, anchor_len, links)) { - matches++; + }; + for (size_t i = 0; i < OC_ARRAY_SIZE(platformRes); i++) { + if (encode_resource(links, oc_core_get_resource_by_index(platformRes[i], 0), + request, anchor, include_endpoints)) { + matches++; + } } - if (process_resource(oc_core_get_resource_by_index(OCF_D, device_index), - request, anchor, anchor_len, links)) { + + size_t device_index = request->resource->device; + if (oc_get_con_res_announced() && + encode_resource(links, + oc_core_get_resource_by_index(OCF_CON, device_index), + request, anchor, include_endpoints)) { matches++; } + oc_core_resource_t res[] = { + OCF_RES, + OCF_D, #ifdef OC_INTROSPECTION - if (process_resource( - oc_core_get_resource_by_index(OCF_INTROSPECTION_WK, device_index), - request, anchor, anchor_len, links)) { - matches++; - } + OCF_INTROSPECTION_WK, #endif /* OC_INTROSPECTION */ - - if (oc_get_con_res_announced() && - process_resource(oc_core_get_resource_by_index(OCF_CON, device_index), - request, anchor, anchor_len, links)) { - matches++; - } #ifdef OC_MNT - if (process_resource(oc_core_get_resource_by_index(OCF_MNT, device_index), - request, anchor, anchor_len, links)) { - matches++; - } + OCF_MNT, #endif /* OC_MNT */ #ifdef OC_SOFTWARE_UPDATE - if (process_resource( - oc_core_get_resource_by_index(OCF_SW_UPDATE, device_index), request, - anchor, anchor_len, links)) { - matches++; - } + OCF_SW_UPDATE, #endif /* OC_SOFTWARE_UPDATE */ - #ifdef OC_SECURITY - if (process_resource( - oc_core_get_resource_by_index(OCF_SEC_DOXM, device_index), request, - anchor, anchor_len, links)) { - matches++; - } - - if (process_resource( - oc_core_get_resource_by_index(OCF_SEC_PSTAT, device_index), request, - anchor, anchor_len, links)) { - matches++; - } - - if (process_resource(oc_core_get_resource_by_index(OCF_SEC_ACL, device_index), - request, anchor, anchor_len, links)) { - matches++; - } - - if (process_resource(oc_core_get_resource_by_index(OCF_SEC_AEL, device_index), - request, anchor, anchor_len, links)) { - matches++; - } - - if (process_resource( - oc_core_get_resource_by_index(OCF_SEC_CRED, device_index), request, - anchor, anchor_len, links)) { - matches++; - } - - if (process_resource(oc_core_get_resource_by_index(OCF_SEC_SP, device_index), - request, anchor, anchor_len, links)) { - matches++; - } - + OCF_SEC_DOXM, + OCF_SEC_PSTAT, + OCF_SEC_ACL, + OCF_SEC_AEL, + OCF_SEC_CRED, + OCF_SEC_SP, #ifdef OC_PKI - if (process_resource(oc_core_get_resource_by_index(OCF_SEC_CSR, device_index), - request, anchor, anchor_len, links)) { - matches++; - } - - if (process_resource( - oc_core_get_resource_by_index(OCF_SEC_ROLES, device_index), request, - anchor, anchor_len, links)) { - matches++; - } + OCF_SEC_CSR, + OCF_SEC_ROLES, #endif /* OC_PKI */ - - if (process_resource(oc_core_get_resource_by_index(OCF_SEC_SDI, device_index), - request, anchor, anchor_len, links)) { - matches++; - } - + OCF_SEC_SDI, #endif /* OC_SECURITY */ - #if defined(OC_CLIENT) && defined(OC_SERVER) && defined(OC_CLOUD) - if (process_resource( - oc_core_get_resource_by_index(OCF_COAPCLOUDCONF, device_index), request, - anchor, anchor_len, links)) { - matches++; - } + OCF_COAPCLOUDCONF, #endif /* OC_CLIENT && OC_SERVER && OC_CLOUD */ + }; + for (size_t i = 0; i < OC_ARRAY_SIZE(res); i++) { + if (encode_resource(links, + oc_core_get_resource_by_index(res[i], device_index), + request, anchor, include_endpoints)) { + matches++; + } + } return matches; } static int -process_device_resources(CborEncoder *links, const oc_request_t *request) +encode_device_resources(CborEncoder *links, const oc_request_t *request, + bool include_endpoints) { size_t device_index = request->resource->device; char anchor[OC_CHAR_ARRAY_LEN(OC_SCHEME_OCF) + OC_UUID_LEN] = { 0 }; @@ -407,28 +359,32 @@ process_device_resources(CborEncoder *links, const oc_request_t *request) anchor + OC_CHAR_ARRAY_LEN(OC_SCHEME_OCF), OC_UUID_LEN); size_t anchor_len = oc_strnlen(anchor, OC_ARRAY_SIZE(anchor)); - int matches = - process_device_core_resources(request, anchor, anchor_len, links); + int matches = encode_device_core_resources( + links, request, oc_string_view(anchor, anchor_len), include_endpoints); #ifdef OC_SERVER oc_resource_t *resource = oc_ri_get_app_resources(); for (; resource; resource = resource->next) { if (resource->device != device_index || - !(resource->properties & OC_DISCOVERABLE)) + (resource->properties & OC_DISCOVERABLE) == 0) continue; - if (process_resource(resource, request, anchor, anchor_len, links)) { + if (encode_resource(links, resource, request, + oc_string_view(anchor, anchor_len), + include_endpoints)) { matches++; } } -#if defined(OC_COLLECTIONS) +#ifdef OC_COLLECTIONS const oc_resource_t *collection = (oc_resource_t *)oc_collection_get_all(); for (; collection; collection = collection->next) { if (collection->device != device_index || !(collection->properties & OC_DISCOVERABLE)) continue; - if (process_resource(collection, request, anchor, anchor_len, links)) { + if (encode_resource(links, collection, request, + oc_string_view(anchor, anchor_len), + include_endpoints)) { matches++; } } @@ -494,7 +450,8 @@ process_oic_1_1_resource(oc_resource_t *resource, oc_request_t *request, oc_rep_close_array(res, rt); // if - oc_core_encode_interfaces_mask(oc_rep_object(res), resource->interfaces); + oc_core_encode_interfaces_mask(oc_rep_object(res), resource->interfaces, + false); // p oc_rep_set_object(res, p); @@ -580,7 +537,8 @@ process_oic_1_1_device_object(CborEncoder *device, oc_request_t *request, if (baseline) { oc_resource_t *ocf_res = oc_core_get_resource_by_index(OCF_RES, device_num); oc_rep_set_string_array(links, rt, ocf_res->types); - oc_core_encode_interfaces_mask(oc_rep_object(links), ocf_res->interfaces); + oc_core_encode_interfaces_mask(oc_rep_object(links), ocf_res->interfaces, + false); } oc_rep_set_array(links, links); @@ -1080,37 +1038,36 @@ discovery_check_sduuid(oc_request_t *request, const char *query, } #endif /* OC_SECURITY */ +#ifdef OC_SECURITY static void -discovery_resource_get(oc_request_t *request, oc_interface_mask_t iface_mask, - void *data) +discovery_encode_sdi(CborEncoder *object, size_t device) { - (void)data; - -#ifdef OC_SPEC_VER_OIC - if (request->origin && request->origin->version == OIC_VER_1_1_0) { - oc_core_1_1_discovery_handler(request, iface_mask, data); + const oc_sec_sdi_t *s = oc_sec_sdi_get(device); + if (s->priv) { return; } -#endif /* OC_SPEC_VER_OIC */ + char uuid[OC_UUID_LEN] = { 0 }; + oc_uuid_to_str(&s->uuid, uuid, OC_UUID_LEN); + oc_rep_object_set_text_string(object, OCF_RES_PROP_SDUUID, + OC_CHAR_ARRAY_LEN(OCF_RES_PROP_SDUUID), uuid, + oc_strnlen(uuid, OC_ARRAY_SIZE(uuid))); + oc_rep_object_set_text_string(object, OCF_RES_PROP_SDNAME, + OC_CHAR_ARRAY_LEN(OCF_RES_PROP_SDNAME), + oc_string(s->name), oc_string_len(s->name)); +} - // for dev without SVRs, ignore queries for backward compatibility -#ifdef OC_SECURITY - const char *q; - int ql = oc_get_query_value_v1(request, OCF_RES_QUERY_SDUUID, - OC_CHAR_ARRAY_LEN(OCF_RES_QUERY_SDUUID), &q); - if (ql > 0 && !discovery_check_sduuid(request, q, (size_t)ql)) { - return; - } -#endif /* OC_SECURITY */ +#endif - oc_status_t code = OC_STATUS_OK; - int matches = 0; - switch (iface_mask) { +static int +discovery_encode(const oc_request_t *request, oc_interface_mask_t iface) +{ + switch (iface) { case OC_IF_LL: { oc_rep_start_links_array(); - matches = process_device_resources(oc_rep_array(links), request); + int matches = encode_device_resources(oc_rep_array(links), request, true); oc_rep_end_links_array(); - } break; + return matches > 0 ? OC_STATUS_OK : OC_IGNORE; + } #ifdef OC_RES_BATCH_SUPPORT case OC_IF_B: { if (request->origin == NULL @@ -1119,47 +1076,71 @@ discovery_resource_get(oc_request_t *request, oc_interface_mask_t iface_mask, #endif /* OC_SECURITY */ ) { OC_ERR("oc_discovery: insecure batch interface requests are unsupported"); - break; + return -1; } oc_rep_start_links_array(); - code = discovery_process_batch_request(oc_rep_array(links), request); + int code = discovery_process_batch_request(oc_rep_array(links), request); oc_rep_end_links_array(); - if (code != OC_STATUS_NOT_MODIFIED) { - ++matches; - } - } break; + return code; + } #endif /* OC_RES_BATCH_SUPPORT */ - case OC_IF_BASELINE: { + case OC_IF_BASELINE: +#ifdef OC_HAS_FEATURE_ETAG_INTERFACE + case PLGD_IF_ETAG: +#endif /* OC_HAS_FEATURE_ETAG_INTERFACE */ + { + int matches; size_t device = request->resource->device; - oc_rep_start_links_array(); - oc_rep_start_object(oc_rep_array(links), props); - memcpy(&root_map, &props_map, sizeof(CborEncoder)); - oc_process_baseline_interface( - oc_core_get_resource_by_index(OCF_RES, device)); + oc_rep_begin_array(oc_rep_get_encoder(), root); + oc_rep_begin_object(oc_rep_array(root), props); + oc_process_baseline_interface_with_filter( + oc_rep_object(props), oc_core_get_resource_by_index(OCF_RES, device), + NULL, NULL); #ifdef OC_SECURITY - const oc_sec_sdi_t *s = oc_sec_sdi_get(device); - if (!s->priv) { - char uuid[OC_UUID_LEN]; - oc_uuid_to_str(&s->uuid, uuid, OC_UUID_LEN); - oc_rep_set_text_string_v1(root, sduuid, uuid, - oc_strnlen(uuid, OC_ARRAY_SIZE(uuid))); - oc_rep_set_text_string_v1(root, sdname, oc_string(s->name), - oc_string_len(s->name)); - } + discovery_encode_sdi(oc_rep_object(props), device); #endif - oc_rep_set_array(root, links); - matches = process_device_resources(oc_rep_array(links), request); - oc_rep_close_array(root, links); - memcpy(&props_map, &root_map, sizeof(CborEncoder)); - oc_rep_end_object(oc_rep_array(links), props); - oc_rep_end_links_array(); + oc_rep_open_array(props, links); + matches = encode_device_resources(oc_rep_array(links), request, + iface == OC_IF_BASELINE); + oc_rep_close_array(props, links); + oc_rep_end_object(oc_rep_array(root), props); + oc_rep_end_array(oc_rep_get_encoder(), root); + return matches > 0 ? OC_STATUS_OK : OC_STATUS_NOT_MODIFIED; } break; default: break; } + return -1; +} + +static void +discovery_resource_get(oc_request_t *request, oc_interface_mask_t iface, + void *data) +{ + (void)data; + +#ifdef OC_SPEC_VER_OIC + if (request->origin && request->origin->version == OIC_VER_1_1_0) { + oc_core_1_1_discovery_handler(request, iface, data); + return; + } +#endif /* OC_SPEC_VER_OIC */ + + // for dev without SVRs, ignore queries for backward compatibility +#ifdef OC_SECURITY + const char *q; + int ql = oc_get_query_value_v1(request, OCF_RES_QUERY_SDUUID, + OC_CHAR_ARRAY_LEN(OCF_RES_QUERY_SDUUID), &q); + if (ql > 0 && !discovery_check_sduuid(request, q, (size_t)ql)) { + return; + } +#endif /* OC_SECURITY */ + + oc_status_t code = discovery_encode(request, iface); int response_length = oc_rep_get_encoded_payload_size(); - send_response(request, APPLICATION_VND_OCF_CBOR, matches == 0, code, + bool has_data = (code == OC_STATUS_OK); + send_response(request, APPLICATION_VND_OCF_CBOR, !has_data, code, response_length < 0 ? 0 : (size_t)response_length); } @@ -1274,10 +1255,24 @@ oc_create_wkcore_resource(size_t device) void oc_create_discovery_resource(size_t device) { + int interfaces = OC_IF_BASELINE | OC_IF_LL; +#ifdef OC_RES_BATCH_SUPPORT + interfaces |= OC_IF_B; +#endif /* OC_RES_BATCH_SUPPORT */ +#ifdef OC_HAS_FEATURE_ETAG_INTERFACE + interfaces |= PLGD_IF_ETAG; +#endif /* OC_HAS_FEATURE_ETAG_INTERFACE */ + oc_interface_mask_t default_interface = OC_IF_LL; + assert((interfaces & default_interface) == default_interface); + + int properties = OC_DISCOVERABLE; +#ifdef OC_DISCOVERY_RESOURCE_OBSERVABLE + properties |= OC_OBSERVABLE; +#endif /* OC_DISCOVERY_RESOURCE_OBSERVABLE */ oc_core_populate_resource(OCF_RES, device, OCF_RES_URI, - (oc_interface_mask_t)OCF_RES_IF_MASK, - OCF_RES_DEFAULT_IF, OCF_RES_PROPERTIES_MASK, - discovery_resource_get, /*put*/ NULL, /*post*/ NULL, + (oc_interface_mask_t)interfaces, default_interface, + properties, discovery_resource_get, + /*put*/ NULL, /*post*/ NULL, /*delete*/ NULL, 1, OCF_RES_RT); } diff --git a/api/oc_discovery_internal.h b/api/oc_discovery_internal.h index 68a0ef54c..6c817e325 100644 --- a/api/oc_discovery_internal.h +++ b/api/oc_discovery_internal.h @@ -34,20 +34,9 @@ extern "C" { #define OCF_RES_URI "/oic/res" #define OCF_RES_RT "oic.wk.res" -#define OCF_RES_DEFAULT_IF (OC_IF_LL) -enum { - OCF_RES_IF_MASK = OC_IF_BASELINE | OC_IF_LL -#ifdef OC_RES_BATCH_SUPPORT - | OC_IF_B -#endif /* OC_RES_BATCH_SUPPORT */ - , - OCF_RES_PROPERTIES_MASK = OC_DISCOVERABLE -#ifdef OC_DISCOVERY_RESOURCE_OBSERVABLE - | OC_OBSERVABLE -#endif /* OC_DISCOVERY_RESOURCE_OBSERVABLE */ - , -}; +#define OCF_RES_PROP_SDUUID "sduuid" +#define OCF_RES_PROP_SDNAME "sdname" #define OCF_RES_QUERY_SDUUID "sduuid" diff --git a/api/oc_etag.c b/api/oc_etag.c index d46bdff13..d42fddb22 100644 --- a/api/oc_etag.c +++ b/api/oc_etag.c @@ -20,8 +20,14 @@ #ifdef OC_HAS_FEATURE_ETAG +#include "api/oc_discovery_internal.h" #include "api/oc_etag_internal.h" +#include "api/oc_helpers_internal.h" +#include "api/oc_rep_decode_internal.h" +#include "api/oc_rep_encode_internal.h" +#include "api/oc_rep_internal.h" #include "api/oc_resource_internal.h" +#include "api/oc_ri_internal.h" #include "messaging/coap/coap_options.h" #include "oc_base64.h" #include "oc_core_res.h" @@ -31,9 +37,15 @@ #include "port/oc_log_internal.h" #include "port/oc_random.h" #include "util/oc_macros_internal.h" +#include "util/oc_mmem_internal.h" + +#ifdef OC_SECURITY +#include "oc_csr.h" +#endif /* OC_SECURITY */ #ifdef OC_STORAGE #include "api/oc_storage_internal.h" +#include "util/oc_crc_internal.h" #ifdef OC_COLLECTIONS #include "api/oc_collection_internal.h" @@ -43,6 +55,11 @@ #include #include +#include + +#if defined(OC_STORAGE) && !defined(OC_HAS_FEATURE_CRC_ENCODER) +#error "CRC encoder must be enabled to use the ETag feature with storage" +#endif /* OC_STORAGE && !OC_HAS_FEATURE_CRC_ENCODER */ static uint64_t g_etag = 0; @@ -186,37 +203,74 @@ oc_etag_clear_storage(void) return success; } -static bool -etag_iterate_encode_resource(oc_resource_t *resource, void *data) +bool +oc_etag_dump_ignore_resource(const char *uri, size_t uri_len) { - (void)data; - OC_DBG("encoding resource [%zu:]%s", resource->device, - oc_string(resource->uri)); +#ifdef OC_WKCORE + if (uri_len == OC_CHAR_ARRAY_LEN(OC_WELLKNOWNCORE_URI) && + memcmp(uri, OC_WELLKNOWNCORE_URI, uri_len) == 0) { + return true; + } +#endif /* OC_WKCORE */ +#ifdef OC_SECURITY + if (uri_len == OC_CHAR_ARRAY_LEN(OCF_SEC_CSR_URI) && + memcmp(uri, OCF_SEC_CSR_URI, uri_len) == 0) { + return true; + } +#endif /* OC_SECURITY */ + (void)uri; + (void)uri_len; + return false; +} +oc_resource_encode_status_t +oc_etag_encode_resource_etag(CborEncoder *encoder, oc_resource_t *resource) +{ + oc_string_view_t uri = oc_string_view2(&resource->uri); uint64_t etag = oc_resource_get_etag(resource); if (etag == OC_ETAG_UNINITIALIZED) { - OC_DBG("skipping uninitialized etag for resource %s", - oc_string(resource->uri)); - return true; + OC_DBG("skipping uninitialized etag for resource %s", uri.data); + return OC_RESOURCE_ENCODE_SKIPPED; } - CborError err = - oc_rep_encode_text_string(oc_rep_object(root), oc_string(resource->uri), - oc_string_len(resource->uri)); + + uint64_t crc64 = 0; + if (oc_resource_get_crc64(resource, &crc64) != OC_RESOURCE_CRC64_OK) { + OC_DBG("cannot calculate crc64 for device(%zu) resource(%s)", + resource->device, uri.data); + return OC_RESOURCE_ENCODE_SKIPPED; + } + + CborError err = oc_rep_encode_text_string(encoder, uri.data, uri.length); CborEncoder etag_map; memset(&etag_map, 0, sizeof(etag_map)); - err |= oc_rep_encoder_create_map(oc_rep_object(root), &etag_map, - CborIndefiniteLength); + err |= oc_rep_encoder_create_map(encoder, &etag_map, CborIndefiniteLength); err |= oc_rep_encode_text_string(&etag_map, "etag", OC_CHAR_ARRAY_LEN("etag")); err |= oc_rep_encode_uint(&etag_map, etag); - err |= oc_rep_encoder_close_container(oc_rep_object(root), &etag_map); + err |= oc_rep_encode_text_string(&etag_map, "crc", OC_CHAR_ARRAY_LEN("crc")); + err |= oc_rep_encode_uint(&etag_map, crc64); + err |= oc_rep_encoder_close_container(encoder, &etag_map); if (err != CborNoError) { OC_ERR("failed to encode device(%zu) resource %s", resource->device, - oc_string(resource->uri)); + uri.data); g_err |= err; - return false; + return OC_RESOURCE_ENCODE_ERROR; } - return true; + return OC_RESOURCE_ENCODE_OK; +} + +static bool +etag_iterate_encode_resource(oc_resource_t *resource, void *data) +{ + (void)data; + oc_string_view_t uri = oc_string_view2(&resource->uri); + OC_DBG("encoding resource [%zu:%s]", resource->device, uri.data); + if (oc_etag_dump_ignore_resource(uri.data, uri.length)) { + OC_DBG("skipping resource %s", uri.data); + return true; + } + return oc_etag_encode_resource_etag(oc_rep_object(root), resource) != + OC_RESOURCE_ENCODE_ERROR; } static int @@ -255,6 +309,43 @@ oc_etag_dump(void) return success; } +bool +oc_etag_decode_resource_etag(oc_resource_t *resource, const oc_rep_t *rep, + uint64_t *etag) +{ + int64_t etag_store = 0; + if (!oc_rep_get_int(rep, "etag", &etag_store) || + etag_store == OC_ETAG_UNINITIALIZED) { + OC_DBG("etag missing or invalid for resource %zu:%s", resource->device, + oc_string(resource->uri)); + return false; + } + + int64_t crc64_store = 0; + if (!oc_rep_get_int(rep, "crc", &crc64_store)) { + OC_DBG("no checksum for resource %zu:%s", resource->device, + oc_string(resource->uri)); + return false; + } + + uint64_t crc64 = 0; + if (oc_resource_get_crc64(resource, &crc64) != OC_RESOURCE_CRC64_OK) { + OC_DBG("cannot calculate crc64 for resource %zu:%s", resource->device, + oc_string(resource->uri)); + return false; + } + + if ((uint64_t)crc64_store != crc64) { + OC_DBG("ignoring invalid checksum for resource %zu:%s: store (%" PRIu64 + ") vs current(%" PRIu64 ")", + resource->device, oc_string(resource->uri), (uint64_t)crc64_store, + crc64); + return false; + } + *etag = (uint64_t)etag_store; + return true; +} + typedef struct etag_update_from_rep_data_t { const oc_rep_t *rep; @@ -267,20 +358,21 @@ etag_iterate_update_resources_by_rep(oc_resource_t *resource, void *data) etag_update_from_rep_data_t *rep_data = (etag_update_from_rep_data_t *)data; oc_rep_t *res_rep; if (!oc_rep_get_object(rep_data->rep, oc_string(resource->uri), &res_rep)) { - OC_DBG("no representation for resource %s", oc_string(resource->uri)); + OC_DBG("no representation for resource %zu:%s", resource->device, + oc_string(resource->uri)); return true; } - int64_t etag = 0; - if (!oc_rep_get_int(res_rep, "etag", &etag) || etag < 0 || - etag == OC_ETAG_UNINITIALIZED) { - OC_DBG("ignoring invalid etag for resource %s", oc_string(resource->uri)); + uint64_t etag; + if (!oc_etag_decode_resource_etag(resource, res_rep, &etag)) { + OC_DBG("failed to decode etag for resource %zu:%s", resource->device, + oc_string(resource->uri)); return true; } - oc_resource_set_etag(resource, (uint64_t)etag); - if ((uint64_t)etag > *rep_data->etag) { - *rep_data->etag = (uint64_t)etag; + oc_resource_set_etag(resource, etag); + if (etag > *rep_data->etag) { + *rep_data->etag = etag; } return true; } @@ -320,7 +412,7 @@ oc_etag_load_from_storage(bool from_storage_only) { bool success = true; // load g_etag and resource etags from storage - uint64_t etag = oc_clock_time(); + uint64_t etag = oc_etag_get(); for (size_t i = 0; i < oc_core_get_num_devices(); ++i) { if (!from_storage_only) { // clear all etags @@ -358,6 +450,188 @@ oc_etag_load_and_clear(void) return success; } +static bool +resource_get_payload_by_encoder(oc_rep_encoder_type_t type, + oc_resource_t *resource, + oc_interface_mask_t iface, + oc_response_buffer_t *response_buffer, + size_t buffer_max_size) +{ + oc_rep_encoder_set_type(type); + + oc_request_t request; + memset(&request, 0, sizeof(request)); + oc_response_t response; + memset(&response, 0, sizeof(response)); + response.response_buffer = response_buffer; + request.response = &response; + request.resource = resource; + request.method = OC_GET; + +#ifdef OC_DYNAMIC_ALLOCATION + if (buffer_max_size != 0) { + oc_rep_new_realloc_v1(&response_buffer->buffer, + response_buffer->buffer_size, buffer_max_size); + } else { + oc_rep_new_v1(response_buffer->buffer, response_buffer->buffer_size); + } +#else /* OC_DYNAMIC_ALLOCATION */ + (void)buffer_max_size; + oc_rep_new_v1(response_buffer->buffer, response_buffer->buffer_size); +#endif /* !OC_DYNAMIC_ALLOCATION */ + +#if defined(OC_SERVER) && defined(OC_COLLECTIONS) + if (oc_check_if_collection(resource)) { + if (!oc_handle_collection_request(OC_GET, &request, iface, NULL)) { + OC_ERR("cannot calculate crc64 for device(%zu) resource(%s): failed to " + "handle collection request", + resource->device, oc_string(resource->uri)); + return false; + } + return true; + } +#endif /* OC_SERVER && OC_COLLECTIONS */ + resource->get_handler.cb(&request, iface, resource->get_handler.user_data); + return true; +} + +#if OC_DBG_IS_ENABLED + +static void +resource_print_payload(oc_resource_t *resource, oc_interface_mask_t iface) +{ + // GCOVR_EXCL_START +#ifdef OC_DYNAMIC_ALLOCATION + uint8_t *buffer = calloc(1, OC_MIN_APP_DATA_SIZE); + if (buffer == NULL) { + return; + } +#else /* !OC_DYNAMIC_ALLOCATION */ + uint8_t buffer[OC_MIN_APP_DATA_SIZE] = { 0 }; +#endif /* OC_DYNAMIC_ALLOCATION */ + + oc_response_buffer_t response_buffer; + memset(&response_buffer, 0, sizeof(response_buffer)); + response_buffer.buffer = buffer; + response_buffer.buffer_size = OC_MIN_APP_DATA_SIZE; + + if (!resource_get_payload_by_encoder(OC_REP_CBOR_ENCODER, resource, iface, + &response_buffer, + OC_MAX_APP_DATA_SIZE)) { +#ifdef OC_DYNAMIC_ALLOCATION + free(buffer); +#endif /* OC_DYNAMIC_ALLOCATION */ + return; + } + +#ifdef OC_DYNAMIC_ALLOCATION + // might have been reallocated by the handler + buffer = response_buffer.buffer; +#else /* !OC_DYNAMIC_ALLOCATION */ + size_t avail_bytes = oc_mmem_available_size(BYTE_POOL); + if (avail_bytes < response_buffer.response_length) { + OC_DBG("not enough memory to print payload of resource(%s)", + oc_string(resource->uri)); + return; + } +#endif /* OC_DYNAMIC_ALLOCATION */ + + oc_rep_decoder_t decoder = oc_rep_decoder(OC_REP_CBOR_DECODER); + OC_MEMB_LOCAL(rep_objects, oc_rep_t, OC_MAX_NUM_REP_OBJECTS); + struct oc_memb *prev_rep_objects = oc_rep_reset_pool(&rep_objects); + oc_rep_t *rep = NULL; + if (CborNoError != decoder.parse(response_buffer.buffer, + response_buffer.response_length, &rep)) { + oc_rep_set_pool(prev_rep_objects); +#ifdef OC_DYNAMIC_ALLOCATION + free(buffer); +#endif /* OC_DYNAMIC_ALLOCATION */ + return; + } +#ifdef OC_DYNAMIC_ALLOCATION + size_t json_size = oc_rep_to_json(rep, NULL, 0, true); + char *json = (char *)malloc(json_size + 1); + oc_rep_to_json(rep, json, json_size + 1, true); +#else /* !OC_DYNAMIC_ALLOCATION */ + char json[4096] = { 0 }; + oc_rep_to_json(rep, json, OC_ARRAY_SIZE(json), true); +#endif /* OC_DYNAMIC_ALLOCATION */ + OC_DBG("Resource(%s) payload: %s", oc_string(resource->uri), json); + oc_free_rep(rep); + oc_rep_set_pool(prev_rep_objects); +#ifdef OC_DYNAMIC_ALLOCATION + free(json); + free(buffer); +#endif /* OC_DYNAMIC_ALLOCATION */ + // GCOVR_EXCL_STOP +} + +#endif /* OC_DBG_IS_ENABLED */ + +oc_resource_crc64_status_t +oc_resource_get_crc64(oc_resource_t *resource, uint64_t *crc64) +{ + assert(resource != NULL); + assert(crc64 != NULL); + + bool is_collection = false; +#if defined(OC_SERVER) && defined(OC_COLLECTIONS) + if (oc_check_if_collection(resource)) { + is_collection = true; + } +#endif /* OC_SERVER && OC_COLLECTIONS */ + + if (!is_collection && resource->get_handler.cb == NULL) { + OC_ERR("cannot calculate crc64 for device(%zu) resource(%s): get handler " + "not available", + resource->device, oc_string(resource->uri)); + return OC_RESOURCE_CRC64_ERROR; + } + + oc_rep_encoder_reset_t prevEncoder = oc_rep_global_encoder_reset(NULL); + + oc_interface_mask_t iface = OC_IF_BASELINE; +#ifdef OC_HAS_FEATURE_ETAG_INTERFACE + if (oc_resource_supports_interface(resource, PLGD_IF_ETAG)) { + iface = PLGD_IF_ETAG; + } +#endif /* OC_HAS_FEATURE_ETAG_INTERFACE */ + +#if OC_DBG_IS_ENABLED + resource_print_payload(resource, iface); +#endif /* OC_DBG_IS_ENABLED */ + + uint8_t buffer[sizeof(*crc64)] = { 0 }; + oc_response_buffer_t response_buffer; + memset(&response_buffer, 0, sizeof(response_buffer)); + response_buffer.buffer = buffer; + response_buffer.buffer_size = OC_ARRAY_SIZE(buffer); + if (!resource_get_payload_by_encoder(OC_REP_CRC_ENCODER, resource, iface, + &response_buffer, 0)) { + return OC_RESOURCE_CRC64_ERROR; + } + + int payload_size = oc_rep_get_encoded_payload_size(); + if (payload_size == 0) { + OC_DBG("ignoring empty payload for device(%zu) resource(%s)", + resource->device, oc_string(resource->uri)); + oc_rep_global_encoder_reset(&prevEncoder); + return OC_RESOURCE_CRC64_NO_PAYLOAD; + } + + if (payload_size != sizeof(*crc64)) { + OC_ERR("cannot calculate crc64 for device(%zu) resource(%s): failed to " + "encode payload", + resource->device, oc_string(resource->uri)); + oc_rep_global_encoder_reset(&prevEncoder); + return -1; + } + memcpy(crc64, buffer, sizeof(*crc64)); + + oc_rep_global_encoder_reset(&prevEncoder); + return OC_RESOURCE_CRC64_OK; +} + #endif /* OC_STORAGE */ void @@ -365,8 +639,8 @@ oc_resource_set_etag(oc_resource_t *resource, uint64_t etag) { assert(resource != NULL); resource->etag = etag; - OC_DBG("oc_etag: set resource %s etag to %" PRIu64, oc_string(resource->uri), - etag); + OC_DBG("oc_etag: set resource %zu:%s etag to %" PRIu64, resource->device, + oc_string(resource->uri), etag); } uint64_t diff --git a/api/oc_etag_internal.h b/api/oc_etag_internal.h index 69dbcd3b5..168ace6c4 100644 --- a/api/oc_etag_internal.h +++ b/api/oc_etag_internal.h @@ -120,6 +120,48 @@ bool oc_etag_load_from_storage(bool from_storage_only); /** Dump ETags of given device to persisent storage. */ bool oc_etag_dump_for_device(size_t device); +/** Do not dump resource with given URI to storage. */ +bool oc_etag_dump_ignore_resource(const char *uri, size_t uri_len) OC_NONNULL(); + +typedef enum { + OC_RESOURCE_CRC64_OK = 0, ///< resource has a payload and crc64 is calculated + OC_RESOURCE_CRC64_NO_PAYLOAD = 1, ///< resource has no payload + + OC_RESOURCE_CRC64_ERROR = -1, ///< error occured +} oc_resource_crc64_status_t; + +/** Calculate crc64 checksum for given resource */ +oc_resource_crc64_status_t oc_resource_get_crc64(oc_resource_t *resource, + uint64_t *crc64) OC_NONNULL(); + +typedef enum { + OC_RESOURCE_ENCODE_OK = 0, + OC_RESOURCE_ENCODE_SKIPPED = 1, + + OC_RESOURCE_ENCODE_ERROR = -1, +} oc_resource_encode_status_t; + +/** @brief Encode resource ETag + * + * Format: + * "${resource-uri}": { + * "etag": ${resource->etag} + * "crc": ${crc64 checksum of the resource payload} + * } + * + * @param encoder encoder (cannot be NULL) + * @param resource resource to encode (cannot be NULL) + * @return OC_RESOURCE_ENCODE_OK if resource was encoded + * @return OC_RESOURCE_ENCODE_SKIPPED if resource encoding was skipped + * @return OC_RESOURCE_ENCODE_ERROR if error occured + */ +oc_resource_encode_status_t oc_etag_encode_resource_etag( + CborEncoder *encoder, oc_resource_t *resource) OC_NONNULL(); + +/** Decode resource ETag */ +bool oc_etag_decode_resource_etag(oc_resource_t *resource, const oc_rep_t *rep, + uint64_t *etag) OC_NONNULL(); + #endif /* OC_STORAGE */ #ifdef __cplusplus diff --git a/api/oc_introspection.c b/api/oc_introspection.c index 89b69aeef..d9de34a91 100644 --- a/api/oc_introspection.c +++ b/api/oc_introspection.c @@ -16,32 +16,39 @@ * ****************************************************************************/ -#include "oc_config.h" +#include "util/oc_features.h" #ifdef OC_INTROSPECTION #include "api/oc_core_res_internal.h" #include "api/oc_endpoint_internal.h" #include "api/oc_introspection_internal.h" +#include "api/oc_rep_encode_internal.h" #include "api/oc_ri_internal.h" #include "api/oc_server_api_internal.h" #include "api/oc_storage_internal.h" #include "messaging/coap/oc_coap.h" #include "oc_api.h" -#include "oc_config.h" #include "oc_core_res.h" #include "oc_endpoint.h" #include "oc_introspection.h" #include "port/oc_log_internal.h" +#include "port/oc_storage_internal.h" #include "util/oc_macros_internal.h" -#include -#include +#ifdef OC_HAS_FEATURE_CRC_ENCODER +#include "port/oc_storage_internal.h" +#include "util/oc_crc_internal.h" +#endif /* OC_HAS_FEATURE_CRC_ENCODER */ #ifndef OC_IDD_API #include "server_introspection.dat.h" #else /* OC_IDD_API */ +#include +#include +#include + #ifndef OC_STORAGE #error Preprocessor macro OC_IDD_API is defined but OC_STORAGE is not defined \ check oc_config.h and make sure OC_STORAGE is defined if OC_IDD_API is defined. @@ -76,6 +83,9 @@ oc_introspection_get_data(size_t device, uint8_t *buffer, size_t buffer_size) OC_ERR("cannot get introspection data: failed to generate tag"); return -1; } + if (buffer == NULL) { + return oc_storage_size(idd_tag); + } long ret = oc_storage_read(idd_tag, buffer, buffer_size); if (ret < 0) { OC_ERR("cannot get introspection data: failed to read data(error=%ld)", @@ -85,6 +95,9 @@ oc_introspection_get_data(size_t device, uint8_t *buffer, size_t buffer_size) return ret; #else /* !OC_IDD_API */ (void)device; + if (buffer == NULL) { + return introspection_data_size; + } if (introspection_data_size < buffer_size) { memcpy(buffer, introspection_data, introspection_data_size); return introspection_data_size; @@ -95,16 +108,11 @@ oc_introspection_get_data(size_t device, uint8_t *buffer, size_t buffer_size) } static void -oc_core_introspection_data_handler(oc_request_t *request, - oc_interface_mask_t iface_mask, void *data) +introspection_data_handler_cbor(oc_request_t *request) { - (void)iface_mask; - (void)data; - - OC_DBG("in oc_core_introspection_data_handler"); long IDD_size = oc_introspection_get_data( request->resource->device, request->response->response_buffer->buffer, - OC_MAX_APP_DATA_SIZE); + request->response->response_buffer->buffer_size); if (IDD_size < 0) { OC_ERR( "oc_core_introspection_data_handler: failed to get introspection data"); @@ -116,6 +124,81 @@ oc_core_introspection_data_handler(oc_request_t *request, IDD_size, true); } +#ifdef OC_HAS_FEATURE_CRC_ENCODER +static void +introspection_data_handler_crc(oc_request_t *request) +{ + uint64_t crc = 0; +#ifdef OC_IDD_API + char idd_tag[OC_STORAGE_SVR_TAG_MAX]; + if (oc_storage_gen_svr_tag(OC_INTROSPECTION_WK_STORE_NAME, + request->resource->device, idd_tag, + sizeof(idd_tag)) < 0) { + OC_ERR("cannot encode introspection data: failed to generate tag"); + return; + } + long ret = oc_storage_size(idd_tag); + if (ret == -ENOENT || ret == 0) { + OC_DBG("no introspection data"); + return; + } + if (ret < 0) { + OC_ERR( + "cannot encode introspection data: failed to get data size(error=%ld)", + ret); + return; + } + uint8_t idd_data[ret]; + memset(idd_data, 0, (size_t)ret); + ret = oc_storage_read(idd_tag, idd_data, (size_t)ret); + if (ret < 0) { + OC_ERR("cannot encode introspection data: failed to read data(error=%ld)", + ret); + return; + } + crc = oc_crc64(0, idd_data, (size_t)ret); +#else /* !OC_IDD_API */ + crc = oc_crc64(0, introspection_data, introspection_data_size); +#endif /* OC_IDD_API */ + if (oc_rep_encoder_write_uint(oc_rep_global_encoder(), oc_rep_get_encoder(), + crc) != CborNoError) { + OC_ERR("cannot encode introspection data: failed to encode data"); + } + + request->response->response_buffer->response_length = sizeof(crc); + request->response->response_buffer->code = CONTENT_2_05; +} + +#endif /* OC_HAS_FEATURE_CRC_ENCODER */ + +static void +oc_core_introspection_data_handler(oc_request_t *request, + oc_interface_mask_t iface_mask, void *data) +{ + (void)iface_mask; + (void)data; + + OC_DBG("in oc_core_introspection_data_handler"); + oc_rep_encoder_type_t et = oc_rep_encoder_get_type(); + if (et == OC_REP_CBOR_ENCODER) { + introspection_data_handler_cbor(request); + return; + } + +#ifdef OC_HAS_FEATURE_CRC_ENCODER + if (et == OC_REP_CRC_ENCODER) { + introspection_data_handler_crc(request); + return; + } +#endif /* OC_HAS_FEATURE_CRC_ENCODER */ + + OC_DBG("oc_core_introspection_data_handler: cannot encode introspection data " + "by encoder %d", + et); + oc_send_response_internal(request, OC_IGNORE, APPLICATION_VND_OCF_CBOR, 0, + false); +} + bool oc_introspection_wk_get_uri(size_t device, int interface_index, transport_flags flags, oc_string_t *uri) @@ -162,9 +245,14 @@ oc_core_introspection_wk_handler(oc_request_t *request, { (void)data; - int if_index = (request->origin) ? (int)request->origin->interface_index : -1; + int if_index = + (request->origin != NULL) ? (int)request->origin->interface_index : -1; +#ifdef OC_IPV4 transport_flags flags = - (request->origin && (request->origin->flags & IPV6)) ? IPV6 : IPV4; + (request->origin != NULL && (request->origin->flags & IPV6)) ? IPV6 : IPV4; +#else /* !OC_IPV4 */ + transport_flags flags = IPV6; +#endif /* OC_IPV4 */ oc_string_t uri; memset(&uri, 0, sizeof(oc_string_t)); if (!oc_introspection_wk_get_uri(request->resource->device, if_index, flags, diff --git a/api/oc_introspection_internal.h b/api/oc_introspection_internal.h index 34c52ca09..558106c93 100644 --- a/api/oc_introspection_internal.h +++ b/api/oc_introspection_internal.h @@ -47,9 +47,12 @@ extern "C" { * @param buffer_size size of the buffer * @return long size of the introspection data * @return -1 on error + * + * @note if buffer is NULL, the function will return the size of the + * introspection data */ long oc_introspection_get_data(size_t device, uint8_t *buffer, - size_t buffer_size) OC_NONNULL(); + size_t buffer_size); /** * @brief Find endpoint from given device with the given transport flags and diff --git a/api/oc_push.c b/api/oc_push.c index 0b0f59078..3df8cfd0c 100644 --- a/api/oc_push.c +++ b/api/oc_push.c @@ -2407,7 +2407,8 @@ push_update(oc_ns_t *ns_instance) oc_rep_close_array(root, rt); /* if */ - oc_core_encode_interfaces_mask(oc_rep_object(root), src_rsc->interfaces); + oc_core_encode_interfaces_mask(oc_rep_object(root), src_rsc->interfaces, + false); /* build rep object */ src_rsc->payload_builder(); @@ -2518,8 +2519,8 @@ OC_PROCESS_THREAD(oc_push_process, ev, data) oc_rep_close_array(root, rt); /* if */ - oc_core_encode_interfaces_mask(oc_rep_object(root), - src_rsc->interfaces); + oc_core_encode_interfaces_mask(oc_rep_object(root), src_rsc->interfaces, + false); src_rsc->payload_builder(); diff --git a/api/oc_query.c b/api/oc_query.c index 43daaac21..312a515e2 100644 --- a/api/oc_query.c +++ b/api/oc_query.c @@ -59,6 +59,10 @@ oc_query_encode_interface(oc_interface_mask_t iface_mask) return OC_STRING_VIEW("if=" OC_IF_STARTUP_STR); case OC_IF_STARTUP_REVERT: return OC_STRING_VIEW("if=" OC_IF_STARTUP_REVERT_STR); +#ifdef OC_HAS_FEATURE_ETAG_INTERFACE + case PLGD_IF_ETAG: + return OC_STRING_VIEW("if=" PLGD_IF_ETAG_STR); +#endif /* OC_HAS_FEATURE_ETAG_INTERFACE */ default: break; } diff --git a/api/oc_ri.c b/api/oc_ri.c index 0bbe69b93..4ce828d21 100644 --- a/api/oc_ri.c +++ b/api/oc_ri.c @@ -496,7 +496,7 @@ static const char *method_strs[] = { const char * oc_method_to_str(oc_method_t method) { - if (method < 0 || method >= OC_ARRAY_SIZE(method_strs)) { + if (method <= 0 || method >= OC_ARRAY_SIZE(method_strs)) { return method_strs[0]; } return method_strs[method]; @@ -685,87 +685,65 @@ oc_ri_free_resource_properties(oc_resource_t *resource) oc_interface_mask_t oc_ri_get_interface_mask(const char *iface, size_t iface_len) { - if (OC_CHAR_ARRAY_LEN(OC_IF_BASELINE_STR) == iface_len && - strncmp(iface, OC_IF_BASELINE_STR, iface_len) == 0) { - return OC_IF_BASELINE; - } - if (OC_CHAR_ARRAY_LEN(OC_IF_LL_STR) == iface_len && - strncmp(iface, OC_IF_LL_STR, iface_len) == 0) { - return OC_IF_LL; - } - if (OC_CHAR_ARRAY_LEN(OC_IF_B_STR) == iface_len && - strncmp(iface, OC_IF_B_STR, iface_len) == 0) { - return OC_IF_B; - } - if (OC_CHAR_ARRAY_LEN(OC_IF_R_STR) == iface_len && - strncmp(iface, OC_IF_R_STR, iface_len) == 0) { - return OC_IF_R; - } - if (OC_CHAR_ARRAY_LEN(OC_IF_RW_STR) == iface_len && - strncmp(iface, OC_IF_RW_STR, iface_len) == 0) { - return OC_IF_RW; - } - if (OC_CHAR_ARRAY_LEN(OC_IF_A_STR) == iface_len && - strncmp(iface, OC_IF_A_STR, iface_len) == 0) { - return OC_IF_A; - } - if (OC_CHAR_ARRAY_LEN(OC_IF_S_STR) == iface_len && - strncmp(iface, OC_IF_S_STR, iface_len) == 0) { - return OC_IF_S; - } - if (OC_CHAR_ARRAY_LEN(OC_IF_CREATE_STR) == iface_len && - strncmp(iface, OC_IF_CREATE_STR, iface_len) == 0) { - return OC_IF_CREATE; - } - if (OC_CHAR_ARRAY_LEN(OC_IF_W_STR) == iface_len && - strncmp(iface, OC_IF_W_STR, iface_len) == 0) { - return OC_IF_W; - } - if (OC_CHAR_ARRAY_LEN(OC_IF_STARTUP_STR) == iface_len && - strncmp(iface, OC_IF_STARTUP_STR, iface_len) == 0) { - return OC_IF_STARTUP; - } - if (OC_CHAR_ARRAY_LEN(OC_IF_STARTUP_REVERT_STR) == iface_len && - strncmp(iface, OC_IF_STARTUP_REVERT_STR, iface_len) == 0) { - return OC_IF_STARTUP_REVERT; + struct + { + oc_interface_mask_t mask; + oc_string_view_t str; + } iface_to_strs[] = { + { OC_IF_BASELINE, OC_STRING_VIEW(OC_IF_BASELINE_STR) }, + { OC_IF_LL, OC_STRING_VIEW(OC_IF_LL_STR) }, + { OC_IF_B, OC_STRING_VIEW(OC_IF_B_STR) }, + { OC_IF_R, OC_STRING_VIEW(OC_IF_R_STR) }, + { OC_IF_RW, OC_STRING_VIEW(OC_IF_RW_STR) }, + { OC_IF_A, OC_STRING_VIEW(OC_IF_A_STR) }, + { OC_IF_S, OC_STRING_VIEW(OC_IF_S_STR) }, + { OC_IF_CREATE, OC_STRING_VIEW(OC_IF_CREATE_STR) }, + { OC_IF_W, OC_STRING_VIEW(OC_IF_W_STR) }, + { OC_IF_STARTUP, OC_STRING_VIEW(OC_IF_STARTUP_STR) }, + { OC_IF_STARTUP_REVERT, OC_STRING_VIEW(OC_IF_STARTUP_REVERT_STR) }, +#ifdef OC_HAS_FEATURE_ETAG_INTERFACE + { PLGD_IF_ETAG, OC_STRING_VIEW(PLGD_IF_ETAG_STR) }, +#endif /* OC_HAS_FEATURE_ETAG_INTERFACE */ + }; + + for (size_t i = 0; i < OC_ARRAY_SIZE(iface_to_strs); ++i) { + if (iface_len == iface_to_strs[i].str.length && + strncmp(iface, iface_to_strs[i].str.data, iface_len) == 0) { + return iface_to_strs[i].mask; + } } return 0; } -static bool -does_interface_support_method(oc_interface_mask_t iface_mask, - oc_method_t method) +bool +oc_ri_interface_supports_method(oc_interface_mask_t iface, oc_method_t method) { - bool supported = true; - switch (iface_mask) { - /* Per section 7.5.3 of the OCF Core spec, the following three interfaces - * are RETRIEVE-only. - */ + /* Supported operations are defined in section 7.5.3 of the OCF Core spec */ + switch (iface) { + /* The following interfaces are RETRIEVE-only: */ case OC_IF_LL: case OC_IF_S: case OC_IF_R: - if (method != OC_GET) - supported = false; - break; - /* Per section 7.5.3 of the OCF Core spec, the following three interfaces - * support RETRIEVE, UPDATE. - * TODO: Refine logic below after adding logic that identifies - * and handles CREATE requests using PUT/POST. - */ +#ifdef OC_HAS_FEATURE_ETAG_INTERFACE + case PLGD_IF_ETAG: +#endif + return method == OC_GET; + /* The following interafaces are UPDATE(WRITE)-only: */ + case OC_IF_W: + return method == OC_PUT || method == OC_POST || method == OC_DELETE; + /* The CREATE interface is PUT/POST-only: */ + case OC_IF_CREATE: + return method == OC_PUT || method == OC_POST; + /* The following interfaces support RETRIEVE and UPDATE: */ case OC_IF_RW: case OC_IF_B: case OC_IF_BASELINE: - case OC_IF_CREATE: - /* Per section 7.5.3 of the OCF Core spec, the following interface - * supports CREATE, RETRIEVE and UPDATE. - */ case OC_IF_A: case OC_IF_STARTUP: case OC_IF_STARTUP_REVERT: - case OC_IF_W: - break; + return true; } - return supported; + return false; } #ifdef OC_SECURITY @@ -1413,7 +1391,7 @@ oc_ri_invoke_coap_entity_handler(coap_make_response_ctx_t *ctx, * If not, return a 4.00 response. */ if (((iface_mask & ~cur_resource->interfaces) != 0) || - !does_interface_support_method(iface_mask, method)) { + !oc_ri_interface_supports_method(iface_mask, method)) { forbidden = true; bad_request = true; #ifdef OC_SECURITY diff --git a/api/oc_ri_internal.h b/api/oc_ri_internal.h index 9d26371c1..d6bbec5cb 100644 --- a/api/oc_ri_internal.h +++ b/api/oc_ri_internal.h @@ -45,6 +45,9 @@ extern "C" { #define OC_IF_W_STR "oic.if.w" #define OC_IF_STARTUP_STR "oic.if.startup" #define OC_IF_STARTUP_REVERT_STR "oic.if.startup.revert" +#ifdef OC_HAS_FEATURE_ETAG_INTERFACE +#define PLGD_IF_ETAG_STR "x.plgd.if.etag" +#endif /* OC_HAS_FEATURE_ETAG_INTERFACE */ // number of resources with a single instance on the whole platform #define OC_NUM_CORE_PLATFORM_RESOURCES (OCF_CON) @@ -89,9 +92,13 @@ void oc_ri_init(void); */ void oc_ri_shutdown(void); -/** Unchecked conversion from a non-error oc_status_t to coap_status_t */ +/** @brief Unchecked conversion from a non-error oc_status_t to coap_status_t */ coap_status_t oc_status_code_unsafe(oc_status_t key); +/** @brief Check if given method is supported by the interface */ +bool oc_ri_interface_supports_method(oc_interface_mask_t iface, + oc_method_t method); + #ifdef OC_HAS_FEATURE_ETAG /** @brief Get ETag for given resource */ diff --git a/api/oc_server_api.c b/api/oc_server_api.c index 615e9e3e0..805ba8774 100644 --- a/api/oc_server_api.c +++ b/api/oc_server_api.c @@ -399,7 +399,7 @@ oc_process_baseline_interface_with_filter( &resource->types); } if (filter == NULL || filter(OC_BASELINE_PROP_IF, filter_data)) { - oc_core_encode_interfaces_mask(object, resource->interfaces); + oc_core_encode_interfaces_mask(object, resource->interfaces, false); } if (filter == NULL || filter(OC_BASELINE_PROP_TAG_LOCN, filter_data)) { resource_encode_tag_locn(object, resource->tag_locn); diff --git a/api/plgd/plgd_time.c b/api/plgd/plgd_time.c index 8edd7ce78..4692b46f2 100644 --- a/api/plgd/plgd_time.c +++ b/api/plgd/plgd_time.c @@ -412,21 +412,23 @@ dev_time_encode_property_last_synced_time(plgd_time_t pt, int flags) } int -plgd_time_encode(plgd_time_t pt, oc_interface_mask_t iface_mask, int flags) +plgd_time_encode(plgd_time_t pt, oc_interface_mask_t iface, int flags) { - if ((iface_mask & PLGD_TIME_IF_MASK) != iface_mask) { - OC_ERR("cannot encode plgd-time: invalid interface(%d)", (int)iface_mask); + const oc_resource_t *r = oc_core_get_resource_by_index(PLGD_TIME, 0); + if (r == NULL) { + OC_ERR("cannot encode plgd-time: resource does not exist"); + return -1; + } + if (!oc_resource_supports_interface(r, iface)) { + OC_ERR("cannot encode plgd-time: invalid interface(%d)", (int)iface); return -1; } oc_rep_start_root_object(); - if ((iface_mask & OC_IF_BASELINE) != 0) { + if (iface == OC_IF_BASELINE) { // baseline properties - const oc_resource_t *r = oc_core_get_resource_by_index(PLGD_TIME, 0); - if (r != NULL) { - oc_process_baseline_interface_with_filter( - oc_rep_object(root), r, dev_time_property_filter, &flags); - } + oc_process_baseline_interface_with_filter(oc_rep_object(root), r, + dev_time_property_filter, &flags); } bool to_storage = (flags & PLGD_TIME_ENCODE_FLAG_TO_STORAGE) != 0; @@ -566,18 +568,23 @@ plgd_time_resource_post(oc_request_t *request, oc_interface_mask_t iface_mask, } static void -plgd_time_resource_get(oc_request_t *request, oc_interface_mask_t iface_mask, +plgd_time_resource_get(oc_request_t *request, oc_interface_mask_t iface, void *data) { (void)data; int flags = 0; #ifdef OC_SECURITY - if ((request->origin->flags & SECURED) != 0) { + if (request->origin != NULL && (request->origin->flags & SECURED) != 0) { flags |= PLGD_TIME_ENCODE_FLAG_SECURE; } +#ifdef OC_HAS_FEATURE_ETAG_INTERFACE + if (request->origin == NULL && iface == PLGD_IF_ETAG) { + flags |= PLGD_TIME_ENCODE_FLAG_SECURE; + } +#endif /* OC_HAS_FEATURE_ETAG_INTERFACE */ #endif /* OC_SECURITY */ - if (plgd_time_encode(g_oc_plgd_time, iface_mask, flags) != 0) { + if (plgd_time_encode(g_oc_plgd_time, iface, flags) != 0) { OC_ERR("cannot encode plgd-time resource"); oc_send_response_with_callback(request, OC_STATUS_INTERNAL_SERVER_ERROR, true); @@ -591,10 +598,17 @@ void plgd_time_create_resource(void) { OC_DBG("plgd-time: create resource"); + int interfaces = (OC_IF_BASELINE | OC_IF_RW); +#ifdef OC_HAS_FEATURE_ETAG_INTERFACE + interfaces |= PLGD_IF_ETAG; +#endif /* OC_HAS_FEATURE_ETAG_INTERFACE */ + oc_interface_mask_t default_interface = OC_IF_RW; + assert((interfaces & default_interface) == default_interface); + int properties = OC_DISCOVERABLE | OC_OBSERVABLE; + oc_core_populate_resource(PLGD_TIME, /*device*/ 0, PLGD_TIME_URI, - PLGD_TIME_IF_MASK, PLGD_TIME_DEFAULT_IF, - OC_DISCOVERABLE | OC_OBSERVABLE, - plgd_time_resource_get, + (oc_interface_mask_t)interfaces, default_interface, + properties, plgd_time_resource_get, /*put*/ NULL, plgd_time_resource_post, /*delete*/ NULL, 1, PLGD_TIME_RT); } diff --git a/api/plgd/plgd_time_internal.h b/api/plgd/plgd_time_internal.h index 16d048d09..0b62098ce 100644 --- a/api/plgd/plgd_time_internal.h +++ b/api/plgd/plgd_time_internal.h @@ -41,8 +41,6 @@ extern "C" { #define PLGD_TIME_URI "/x.plgd.dev/time" #define PLGD_TIME_RT "x.plgd.dev.time" -#define PLGD_TIME_IF_MASK (OC_IF_RW | OC_IF_BASELINE) -#define PLGD_TIME_DEFAULT_IF (OC_IF_RW) #define PLGD_TIME_STORE_NAME "plgd_time" #define PLGD_TIME_PROP_TIME "time" @@ -105,12 +103,12 @@ typedef enum plgd_time_encode_flag_t { * @brief Encode plgd time properties to the global encoder. * * @param pt plgd time to encode - * @param iface_mask encoding interface + * @param iface encoding interface * @param flags mask of encoding flags * @return 0 on success * @return -1 on error */ -int plgd_time_encode(plgd_time_t pt, oc_interface_mask_t iface_mask, int flags); +int plgd_time_encode(plgd_time_t pt, oc_interface_mask_t iface, int flags); /** * @brief Decode representation to output structure. diff --git a/api/unittest/RITest.cpp b/api/unittest/RITest.cpp index b19faec04..cef4116e3 100644 --- a/api/unittest/RITest.cpp +++ b/api/unittest/RITest.cpp @@ -41,11 +41,55 @@ class TestOcRi : public testing::Test { } }; -TEST_F(TestOcRi, GetInterfaceMask_P) +TEST_F(TestOcRi, StatusCodeToCoapCode) { - EXPECT_EQ(0, oc_ri_get_interface_mask("", 0)); + for (int i = OC_STATUS_OK; i < __NUM_OC_STATUS_CODES__; ++i) { + EXPECT_EQ(oc_status_code_unsafe(static_cast(i)), + oc_status_code(static_cast(i))); + } + + // OC_IGNORE is a special value that translates to CLEAR_TRANSACTION + // others return -1 + for (int i = __NUM_OC_STATUS_CODES__; i < OC_CANCELLED; ++i) { + if (i == OC_IGNORE) { + EXPECT_EQ(CLEAR_TRANSACTION, oc_status_code(OC_IGNORE)); + continue; + } + EXPECT_EQ(-1, oc_status_code(static_cast(i))); + } +} - std::vector all_interfaces{ +TEST_F(TestOcRi, StatusCodeToStr) +{ + for (int i = OC_STATUS_OK; i < __NUM_OC_STATUS_CODES__; ++i) { + std::string str = oc_status_to_str(static_cast(i)); + EXPECT_FALSE(str.empty()); + } + for (int i = __NUM_OC_STATUS_CODES__; i < OC_CANCELLED; ++i) { + std::string str = oc_status_to_str(static_cast(i)); + EXPECT_TRUE(str.empty()); + } +} + +TEST_F(TestOcRi, MethodToStr) +{ + for (int i = OC_GET; i <= OC_FETCH; ++i) { + std::string str = oc_method_to_str(static_cast(i)); + EXPECT_FALSE(str.empty()); + } + + std::string str = oc_method_to_str(static_cast(0)); + EXPECT_TRUE(str.empty()); + str = oc_method_to_str(std::numeric_limits::min()); + EXPECT_TRUE(str.empty()); + str = oc_method_to_str(std::numeric_limits::max()); + EXPECT_TRUE(str.empty()); +} + +static std::vector +getAllInterfaces() +{ + return { OC_IF_BASELINE, OC_IF_LL, OC_IF_B, @@ -57,8 +101,16 @@ TEST_F(TestOcRi, GetInterfaceMask_P) OC_IF_W, OC_IF_STARTUP, OC_IF_STARTUP_REVERT, +#ifdef OC_HAS_FEATURE_ETAG_INTERFACE + PLGD_IF_ETAG, +#endif /* OC_HAS_FEATURE_ETAG_INTERFACE */ }; - std::vector all_interface_strs{ +} + +static std::vector +getAllInterfaceStrings() +{ + return { OC_IF_BASELINE_STR, OC_IF_LL_STR, OC_IF_B_STR, @@ -70,7 +122,18 @@ TEST_F(TestOcRi, GetInterfaceMask_P) OC_IF_W_STR, OC_IF_STARTUP_STR, OC_IF_STARTUP_REVERT_STR, +#ifdef OC_HAS_FEATURE_ETAG_INTERFACE + PLGD_IF_ETAG_STR, +#endif /* OC_HAS_FEATURE_ETAG_INTERFACE */ }; +} + +TEST_F(TestOcRi, GetInterfaceMask_P) +{ + EXPECT_EQ(0, oc_ri_get_interface_mask("", 0)); + + std::vector all_interfaces{ getAllInterfaces() }; + std::vector all_interface_strs{ getAllInterfaceStrings() }; ASSERT_EQ(all_interfaces.size(), all_interface_strs.size()); for (size_t i = 0; i < all_interface_strs.size(); ++i) { @@ -80,6 +143,65 @@ TEST_F(TestOcRi, GetInterfaceMask_P) } } +TEST_F(TestOcRi, InterfaceSupportsMethod) +{ + // read-only -> GET + std::vector readOnlyInterfaces{ + OC_IF_LL, + OC_IF_S, + OC_IF_R, +#ifdef OC_HAS_FEATURE_ETAG_INTERFACE + PLGD_IF_ETAG, +#endif + }; + + for (auto ifm : readOnlyInterfaces) { + EXPECT_TRUE(oc_ri_interface_supports_method(ifm, OC_GET)); + EXPECT_FALSE(oc_ri_interface_supports_method(ifm, OC_POST)); + EXPECT_FALSE(oc_ri_interface_supports_method(ifm, OC_PUT)); + EXPECT_FALSE(oc_ri_interface_supports_method(ifm, OC_DELETE)); + } + + // write-only -> POST/PUT/DELETE + std::vector writeOnlyInterfaces{ OC_IF_W }; + for (auto ifm : writeOnlyInterfaces) { + EXPECT_FALSE(oc_ri_interface_supports_method(ifm, OC_GET)); + EXPECT_TRUE(oc_ri_interface_supports_method(ifm, OC_POST)); + EXPECT_TRUE(oc_ri_interface_supports_method(ifm, OC_PUT)); + EXPECT_TRUE(oc_ri_interface_supports_method(ifm, OC_DELETE)); + } + + // create -> POST/PUT + std::vector createInterfaces{ OC_IF_CREATE }; + for (auto ifm : createInterfaces) { + EXPECT_FALSE(oc_ri_interface_supports_method(ifm, OC_GET)); + EXPECT_TRUE(oc_ri_interface_supports_method(ifm, OC_POST)); + EXPECT_TRUE(oc_ri_interface_supports_method(ifm, OC_PUT)); + EXPECT_FALSE(oc_ri_interface_supports_method(ifm, OC_DELETE)); + } + + // read-write -> GET/POST/PUT/DELETE + std::vector readWriteInterfaces{ + OC_IF_RW, OC_IF_B, OC_IF_BASELINE, + OC_IF_A, OC_IF_STARTUP, OC_IF_STARTUP_REVERT, + }; + for (auto ifm : readWriteInterfaces) { + EXPECT_TRUE(oc_ri_interface_supports_method(ifm, OC_GET)); + EXPECT_TRUE(oc_ri_interface_supports_method(ifm, OC_POST)); + EXPECT_TRUE(oc_ri_interface_supports_method(ifm, OC_PUT)); + EXPECT_TRUE(oc_ri_interface_supports_method(ifm, OC_DELETE)); + } +} + +TEST_F(TestOcRi, InterfaceSupportsMethod_F) +{ + auto invalid = static_cast(0); + EXPECT_FALSE(oc_ri_interface_supports_method(invalid, OC_GET)); + EXPECT_FALSE(oc_ri_interface_supports_method(invalid, OC_POST)); + EXPECT_FALSE(oc_ri_interface_supports_method(invalid, OC_PUT)); + EXPECT_FALSE(oc_ri_interface_supports_method(invalid, OC_DELETE)); +} + static oc_event_callback_retval_t test_timed_callback(void *data) { @@ -116,8 +238,8 @@ TEST_F(TestOcRi, RiTimedCallbacksFilter_P) thing_t b = a; oc_ri_add_timed_event_callback_seconds(&a, test_timed_callback, 0); oc_ri_remove_timed_event_callback(&b, test_timed_callback); - // comparison by pointer address will fail to match the data, so the callback - // won't be removed + // comparison by pointer address will fail to match the data, so the + // callback won't be removed EXPECT_TRUE(oc_ri_has_timed_event_callback(&a, test_timed_callback, false)); auto match_by_value_filter = [](const void *cb_data, diff --git a/api/unittest/coreresourcetest.cpp b/api/unittest/coreresourcetest.cpp index 5b368d8ba..e4888b4bc 100644 --- a/api/unittest/coreresourcetest.cpp +++ b/api/unittest/coreresourcetest.cpp @@ -114,13 +114,15 @@ TEST_F(TestCoreResource, CoreGetResourceV1_P) } static void -encodeInterfaces(unsigned iface_mask, std::vector iface_strs = {}) +encodeInterfaces(unsigned iface_mask, std::vector iface_strs = {}, + bool includePrivate = false) { oc::RepPool pool{}; oc_rep_start_root_object(); EXPECT_EQ(CborNoError, oc_rep_get_cbor_errno()); - oc_core_encode_interfaces_mask(oc_rep_object(root), iface_mask); + oc_core_encode_interfaces_mask(oc_rep_object(root), iface_mask, + includePrivate); EXPECT_EQ(CborNoError, oc_rep_get_cbor_errno()); oc_rep_end_root_object(); EXPECT_EQ(CborNoError, oc_rep_get_cbor_errno()); @@ -158,7 +160,12 @@ encodeInterfaces(unsigned iface_mask, std::vector iface_strs = {}) TEST_F(TestCoreResource, EncodeInterfaces_P) { - encodeInterfaces(0); + bool includePrivateInterfaces = false; +#ifdef OC_HAS_FEATURE_ETAG_INTERFACE + includePrivateInterfaces = true; +#endif /* OC_HAS_FEATURE_ETAG_INTERFACE */ + + encodeInterfaces(0, {}, includePrivateInterfaces); std::vector all_ifs{ OC_IF_BASELINE, @@ -172,6 +179,9 @@ TEST_F(TestCoreResource, EncodeInterfaces_P) OC_IF_W, OC_IF_STARTUP, OC_IF_STARTUP_REVERT, +#ifdef OC_HAS_FEATURE_ETAG_INTERFACE + PLGD_IF_ETAG, +#endif /* OC_HAS_FEATURE_ETAG_INTERFACE */ }; std::vector all_ifstrs{ @@ -186,16 +196,19 @@ TEST_F(TestCoreResource, EncodeInterfaces_P) OC_IF_W_STR, OC_IF_STARTUP_STR, OC_IF_STARTUP_REVERT_STR, +#ifdef OC_HAS_FEATURE_ETAG_INTERFACE + PLGD_IF_ETAG_STR, +#endif /* OC_HAS_FEATURE_ETAG_INTERFACE */ }; ASSERT_EQ(all_ifs.size(), all_ifstrs.size()); unsigned all_ifs_mask = 0; for (size_t i = 0; i < all_ifs.size(); ++i) { - encodeInterfaces(all_ifs[i], { all_ifstrs[i] }); + encodeInterfaces(all_ifs[i], { all_ifstrs[i] }, includePrivateInterfaces); all_ifs_mask |= all_ifs[i]; } - encodeInterfaces(all_ifs_mask, all_ifstrs); + encodeInterfaces(all_ifs_mask, all_ifstrs, includePrivateInterfaces); } class TestCoreResourceWithDevice : public testing::Test { diff --git a/api/unittest/discoverytest.cpp b/api/unittest/discoverytest.cpp index 546862b44..85893345a 100644 --- a/api/unittest/discoverytest.cpp +++ b/api/unittest/discoverytest.cpp @@ -678,12 +678,12 @@ parseBaselinePayload(const oc_rep_t *payload) char *str; size_t str_len; // sduuid: string - if (oc_rep_get_string(rep, "sduuid", &str, &str_len)) { + if (oc_rep_get_string(rep, OCF_RES_PROP_SDUUID, &str, &str_len)) { data.sduuid = std::string(str, str_len); } // sdname: string - if (oc_rep_get_string(rep, "sdname", &str, &str_len)) { + if (oc_rep_get_string(rep, OCF_RES_PROP_SDNAME, &str, &str_len)) { data.sdname = std::string(str, str_len); } diff --git a/api/unittest/etagtest.cpp b/api/unittest/etagtest.cpp index f3ea584a5..dc638d0e0 100644 --- a/api/unittest/etagtest.cpp +++ b/api/unittest/etagtest.cpp @@ -21,7 +21,9 @@ #ifdef OC_HAS_FEATURE_ETAG #include "api/oc_client_api_internal.h" +#include "api/oc_discovery_internal.h" #include "api/oc_etag_internal.h" +#include "api/oc_introspection_internal.h" #include "api/oc_resource_internal.h" #include "api/oc_ri_internal.h" #include "messaging/coap/coap_options.h" @@ -34,25 +36,38 @@ #include "tests/gtest/RepPool.h" #include "tests/gtest/Resource.h" #include "tests/gtest/Storage.h" -#include "util/oc_features.h" +#include "util/oc_mmem_internal.h" + +#ifdef OC_INTROSPECTION +#include "oc_introspection.h" +#endif /* OC_INTROSPECTION */ #ifdef OC_COLLECTIONS #include "api/oc_collection_internal.h" #include "tests/gtest/Collection.h" #endif /* OC_COLLECTIONS */ +#ifdef OC_SECURITY +#include "oc_csr.h" +#endif /* OC_SECURITY */ + #ifdef OC_STORAGE #include "api/oc_storage_internal.h" #endif /* OC_STORAGE */ +#ifdef OC_HAS_FEATURE_PLGD_TIME +#include "api/plgd/plgd_time_internal.h" +#endif /* OC_HAS_FEATURE_PLGD_TIME */ + #include #include #include #include +#include #include #include +#include #include -#include #include static constexpr size_t kDeviceID1{ 0 }; @@ -62,8 +77,16 @@ static constexpr size_t kDeviceID2{ 1 }; static constexpr std::string_view kDynamicResourceURI1{ "/dyn1" }; static constexpr std::string_view kDynamicResourceURI2{ "/dyn2" }; +#ifdef OC_COLLECTIONS +static constexpr std::string_view kCollectionURI = "/col"; +static constexpr std::string_view kColDynamicURI1 = "/col/discoverable"; +#endif /* OC_COLLECTIONS */ #endif // OC_DYNAMIC_ALLOCATION +#if defined(OC_DYNAMIC_ALLOCATION) && !defined(OC_APP_DATA_BUFFER_SIZE) +const long g_max_app_data_size{ oc_get_max_app_data_size() }; +#endif /* OC_DYNAMIC_ALLOCATION && !OC_APP_DATA_BUFFER_SIZE */ + using namespace std::chrono_literals; class TestETag : public ::testing::Test {}; @@ -336,6 +359,9 @@ class TestETagWithServer : public ::testing::Test { #ifdef OC_STORAGE ASSERT_EQ(0, oc::TestStorage.Config()); #endif // OC_STORAGE +#if defined(OC_DYNAMIC_ALLOCATION) && !defined(OC_APP_DATA_BUFFER_SIZE) + oc_set_max_app_data_size(16384); +#endif /* OC_DYNAMIC_ALLOCATION && !OC_APP_DATA_BUFFER_SIZE */ oc::TestDevice::SetServerDevices({ { @@ -358,12 +384,22 @@ class TestETagWithServer : public ::testing::Test { ASSERT_TRUE(oc::TestDevice::StartServer()); #ifdef OC_DYNAMIC_ALLOCATION addDynamicResources(); +#ifdef OC_COLLECTIONS + addCollections(); +#endif /* OC_COLLECTIONS */ #endif // OC_DYNAMIC_ALLOCATION + +#ifdef OC_IDD_API + ASSERT_TRUE(addIntrospectionData(kDeviceID1)); +#endif /* OC_IDD_API */ } static void TearDownTestCase() { oc::TestDevice::StopServer(); +#if defined(OC_DYNAMIC_ALLOCATION) && !defined(OC_APP_DATA_BUFFER_SIZE) + oc_set_max_app_data_size(g_max_app_data_size); +#endif /* OC_DYNAMIC_ALLOCATION && !OC_APP_DATA_BUFFER_SIZE */ #ifdef OC_STORAGE ASSERT_EQ(0, oc::TestStorage.Clear()); #endif // OC_STORAGE @@ -379,6 +415,10 @@ class TestETagWithServer : public ::testing::Test { #ifdef OC_DYNAMIC_ALLOCATION static void onRequest(oc_request_t *request, oc_interface_mask_t, void *) { + if (request->method == OC_GET) { + oc_rep_start_root_object(); + oc_rep_end_root_object(); + } oc_send_response(request, OC_STATUS_OK); } @@ -388,7 +428,14 @@ class TestETagWithServer : public ::testing::Test { const std::vector &ifaces, size_t device); static void addDynamicResources(); + +#ifdef OC_COLLECTIONS + static void addCollections(); +#endif /* OC_COLLECTIONS */ #endif // OC_DYNAMIC_ALLOCATION +#ifdef OC_IDD_API + static bool addIntrospectionData(size_t device); +#endif /* OC_IDD_API */ }; #ifdef OC_DYNAMIC_ALLOCATION @@ -418,8 +465,59 @@ TestETagWithServer::addDynamicResources() { "oic.d.dynamic", "oic.d.test" }, { OC_IF_BASELINE, OC_IF_RW }, kDeviceID2)); } + +#ifdef OC_COLLECTIONS + +void +TestETagWithServer::addCollections() +{ + constexpr std::string_view powerSwitchRT = "oic.d.power"; + + auto col = oc::NewCollection("col", kCollectionURI, kDeviceID1, "oic.wk.col"); + ASSERT_NE(nullptr, col); + oc_resource_set_discoverable(&col->res, true); + oc_collection_add_supported_rt(&col->res, powerSwitchRT.data()); + oc_collection_add_mandatory_rt(&col->res, powerSwitchRT.data()); + ASSERT_TRUE(oc_add_collection_v1(&col->res)); + + oc::DynamicResourceHandler handlers1{}; + handlers1.onGet = onRequest; + + auto dr1 = oc::makeDynamicResourceToAdd( + "Collection Resource 1", std::string(kColDynamicURI1), + { std::string(powerSwitchRT), "oic.d.test" }, { OC_IF_BASELINE, OC_IF_R }, + handlers1); + oc_resource_t *res1 = oc::TestDevice::AddDynamicResource(dr1, kDeviceID1); + ASSERT_NE(nullptr, res1); + oc_link_t *link1 = oc_new_link(res1); + ASSERT_NE(link1, nullptr); + oc_collection_add_link(&col->res, link1); + + col.release(); +} + +#endif /* OC_COLLECTIONS */ + #endif // OC_DYNAMIC_ALLOCATION +#ifdef OC_IDD_API +bool +TestETagWithServer::addIntrospectionData(size_t device) +{ + auto idd_fs = std::ifstream("introspectiontest_IDD.cbor", + std::ios::in | std::ios::binary); + if (!idd_fs.good()) { + return false; + } + std::vector idd{}; + std::for_each(std::istreambuf_iterator(idd_fs), + std::istreambuf_iterator(), + [&idd](char c) { idd.push_back(c); }); + oc_set_introspection_data(device, idd.data(), idd.size()); + return true; +} +#endif /* OC_IDD_API */ + // check that all resources have initialized etags TEST_F(TestETagWithServer, ETagsInitialized) { @@ -515,13 +613,34 @@ TEST_F(TestETagWithServer, DumpAndLoad) // load etags from the storage and clear the storage EXPECT_TRUE(oc_etag_load_and_clear()); - // check if all etags are set to 1337 + // check if etags of resources that are saved to storage are set to 1337 oc::IterateAllResources([&dynResources](const oc_resource_t *resource) { - if (std::find(std::begin(dynResources), std::end(dynResources), resource) != - std::end(dynResources)) { - EXPECT_NE(0, oc_resource_get_etag(resource)); + if (oc_etag_dump_ignore_resource(oc_string(resource->uri), + oc_string_len(resource->uri))) { return; } +#ifdef OC_INTROSPECTION + if (std::string(oc_string(resource->uri)) == OC_INTROSPECTION_DATA_URI && + oc_introspection_get_data(resource->device, nullptr, 0) <= 0) { + return; + } +#endif /* OC_INTROSPECTION */ + + if (!dynResources.empty()) { + if (std::find(std::begin(dynResources), std::end(dynResources), + resource) != std::end(dynResources)) { + EXPECT_NE(0, oc_resource_get_etag(resource)); + return; + } + // adding a dynamic resource will change the payload of /oic/res + // resource + if (resource->device == kDeviceID1 && + std::string(oc_string(resource->uri)) == OCF_RES_URI) { + EXPECT_NE(1337, oc_resource_get_etag(resource)); + return; + } + } + EXPECT_EQ(1337, oc_resource_get_etag(resource)); }); @@ -677,6 +796,10 @@ TEST_F(TestETagWithServer, Dump_FailNoStorage) // oc_etag_get to set etags on all resources TEST_F(TestETagWithServer, ClearStorage) { +#ifdef OC_HAS_FEATURE_PLGD_TIME + plgd_time_set_time(oc_clock_time()); +#endif /* OC_HAS_FEATURE_PLGD_TIME */ + // set all etags to 1337 setAllETags(1337); // store etags to the storage @@ -691,6 +814,11 @@ TEST_F(TestETagWithServer, ClearStorage) EXPECT_NE(0, oc_resource_get_etag(resource)); EXPECT_NE(1337, oc_resource_get_etag(resource)); }); + +#ifdef OC_HAS_FEATURE_PLGD_TIME + plgd_time_set_time(0); + plgd_time_set_status(PLGD_TIME_STATUS_IN_SYNC); +#endif /* OC_HAS_FEATURE_PLGD_TIME */ } // if storage is not properly initialized then oc_etag_clear_storage should @@ -713,6 +841,347 @@ TEST_F(TestETagWithServer, LoadAndClear_Fail) #endif // OC_STORAGE +#ifdef OC_HAS_FEATURE_CRC_ENCODER + +TEST_F(TestETagWithServer, GetCRC64_Fail) +{ + std::string uri = "/res1"; + oc_resource_t res{}; + res.uri = OC_MMEM(&uri[0], uri.length() + 1, nullptr); + + uint64_t crc64 = 0; + EXPECT_EQ(-1, oc_resource_get_crc64(&res, &crc64)); +} + +TEST_F(TestETagWithServer, GetCRC64) +{ + oc_resource_t *platform = oc_core_get_resource_by_index(OCF_P, 0); + ASSERT_NE(nullptr, platform); + + uint64_t crc64_first = 0; + ASSERT_EQ(OC_RESOURCE_CRC64_OK, + oc_resource_get_crc64(platform, &crc64_first)); + + uint64_t crc64_second = 0; + ASSERT_EQ(OC_RESOURCE_CRC64_OK, + oc_resource_get_crc64(platform, &crc64_second)); + + EXPECT_EQ(crc64_first, crc64_second); +} + +TEST_F(TestETagWithServer, GetCRC64Changed) +{ + oc_resource_t *plt = oc_core_get_resource_by_index(OCF_P, 0); + ASSERT_NE(nullptr, plt); + + oc_platform_info_t *plti = oc_core_get_platform_info(); + ASSERT_NE(nullptr, plti); + + oc_platform_info_t plti_copy{}; + memcpy(&plti_copy.pi, plti, sizeof(plti_copy.pi)); + oc_copy_string(&plti_copy.mfg_name, &plti->mfg_name); + + uint64_t crc64_1 = 0; + ASSERT_EQ(OC_RESOURCE_CRC64_OK, oc_resource_get_crc64(plt, &crc64_1)); + + do { + oc_gen_uuid(&plti->pi); + } while (oc_uuid_is_equal(plti_copy.pi, plti->pi)); + + uint64_t crc64_2 = 0; + ASSERT_EQ(OC_RESOURCE_CRC64_OK, oc_resource_get_crc64(plt, &crc64_2)); + EXPECT_NE(crc64_1, crc64_2); + + std::string mfg_name{ "plgd.dev" }; + oc_set_string(&plti->mfg_name, mfg_name.c_str(), mfg_name.length()); + + uint64_t crc64_3 = 0; + ASSERT_EQ(OC_RESOURCE_CRC64_OK, oc_resource_get_crc64(plt, &crc64_3)); + EXPECT_NE(crc64_2, crc64_3); + EXPECT_NE(crc64_1, crc64_3); + + // restore original values, which should result in the original checksum + memcpy(&plti->pi, &plti_copy.pi, sizeof(plti_copy.pi)); + oc_copy_string(&plti->mfg_name, &plti_copy.mfg_name); + + uint64_t crc64_4 = 0; + ASSERT_EQ(OC_RESOURCE_CRC64_OK, oc_resource_get_crc64(plt, &crc64_4)); + EXPECT_EQ(crc64_1, crc64_4); + + oc_free_string(&plti_copy.mfg_name); +} + +#if defined(OC_SERVER) && defined(OC_COLLECTIONS) + +TEST_F(TestETagWithServer, GetCRC64Collection) +{ + auto *col = oc_get_collection_by_uri(kCollectionURI.data(), + kCollectionURI.length(), kDeviceID1); + ASSERT_NE(nullptr, col); + + uint64_t crc64_1{}; + ASSERT_EQ(OC_RESOURCE_CRC64_OK, oc_resource_get_crc64(&col->res, &crc64_1)); + + uint64_t crc64_2{}; + auto *link = + oc_get_link_by_uri(col, kColDynamicURI1.data(), kColDynamicURI1.length()); + ASSERT_NE(nullptr, link); + + ASSERT_TRUE( + oc_collection_remove_link_and_notify(&col->res, link, false, false)); + ASSERT_EQ(OC_RESOURCE_CRC64_OK, oc_resource_get_crc64(&col->res, &crc64_2)); + EXPECT_NE(crc64_1, crc64_2); + + oc_collection_add_link(&col->res, link); + uint64_t crc64_3{}; + ASSERT_EQ(OC_RESOURCE_CRC64_OK, oc_resource_get_crc64(&col->res, &crc64_3)); + EXPECT_EQ(crc64_1, crc64_3); +} + +#endif // OC_SERVER && OC_COLLECTIONS + +using CheckSumMap = std::map>; + +static bool +storeChecksums(oc_resource_t *resource, void *data) +{ + auto uri = std::string(oc_string(resource->uri)); + if (oc_etag_dump_ignore_resource(oc_string(resource->uri), + oc_string_len(resource->uri))) { + return true; + } + auto &checksums = *static_cast(data); + uint64_t crc64 = 0; + if (oc_resource_get_crc64(resource, &crc64) < 0) { + throw std::string("failed to get crc64 for resource(") + uri + ")"; + } + checksums[uri] = crc64; + return true; +} + +TEST_F(TestETagWithServer, GetCRC64AllResources) +{ + // iterate all resources -> store to map[uri]checksum + std::map> checksums_1{}; + try { + oc_resources_iterate(kDeviceID1, true, true, true, true, storeChecksums, + &checksums_1); + } catch (const std::string &err) { + FAIL() << err; + } + ASSERT_FALSE(checksums_1.empty()); + + // iterate all resources for the second time + std::map> checksums_2{}; + try { + oc_resources_iterate(kDeviceID1, true, true, true, true, storeChecksums, + &checksums_2); + } catch (const std::string &err) { + FAIL() << err; + } + ASSERT_FALSE(checksums_2.empty()); + + // compare maps + ASSERT_EQ(checksums_1.size(), checksums_2.size()); + for (const auto &[uri, checksum] : checksums_1) { + auto it2 = checksums_2.find(uri); + ASSERT_NE(checksums_2.end(), it2); + EXPECT_EQ(checksum, it2->second) + << "checksum for resource(" << uri << ") is different"; + } +} + +TEST_F(TestETagWithServer, EncodeResource) +{ + oc_resource_t *platform = oc_core_get_resource_by_index(OCF_P, 0); + ASSERT_NE(nullptr, platform); + + oc::RepPool pool{}; + oc_rep_start_root_object(); + EXPECT_EQ(OC_RESOURCE_ENCODE_OK, + oc_etag_encode_resource_etag(oc_rep_object(root), platform)); + oc_rep_end_root_object(); + ASSERT_EQ(CborNoError, oc_rep_get_cbor_errno()); + + oc::oc_rep_unique_ptr rep = pool.ParsePayload(); + ASSERT_NE(nullptr, rep.get()); + OC_DBG("payload: %s", oc::RepPool::GetJson(rep.get(), true).data()); + + oc_rep_t *obj = nullptr; + ASSERT_TRUE(oc_rep_get_object(rep.get(), oc_string(platform->uri), &obj)); + ASSERT_NE(nullptr, obj); + + int64_t etag = 0; + ASSERT_TRUE(oc_rep_get_int(obj, "etag", &etag)); + EXPECT_EQ(oc_resource_get_etag(platform), static_cast(etag)); + + uint64_t expectedCrc = 0; + ASSERT_EQ(OC_RESOURCE_CRC64_OK, + oc_resource_get_crc64(platform, &expectedCrc)); + int64_t crc = 0; + ASSERT_TRUE(oc_rep_get_int(obj, "crc", &crc)); + EXPECT_EQ(expectedCrc, static_cast(crc)); +} + +TEST_F(TestETagWithServer, EncodeResource_FailBufferTooSmall) +{ + oc_resource_t *platform = oc_core_get_resource_by_index(OCF_P, 0); + ASSERT_NE(nullptr, platform); + + oc::RepPool pool{ 8 }; + oc_rep_start_root_object(); + ASSERT_EQ(CborNoError, oc_rep_get_cbor_errno()); + EXPECT_EQ(OC_RESOURCE_ENCODE_ERROR, + oc_etag_encode_resource_etag(oc_rep_object(root), platform)); + EXPECT_NE(CborNoError, oc_rep_get_cbor_errno()); +} + +TEST_F(TestETagWithServer, DecodeResource) +{ + oc_resource_t *platform = oc_core_get_resource_by_index(OCF_P, 0); + ASSERT_NE(nullptr, platform); + + oc::RepPool pool{}; + oc_rep_start_root_object(); + EXPECT_EQ(OC_RESOURCE_ENCODE_OK, + oc_etag_encode_resource_etag(oc_rep_object(root), platform)); + oc_rep_end_root_object(); + ASSERT_EQ(CborNoError, oc_rep_get_cbor_errno()); + + oc::oc_rep_unique_ptr rep = pool.ParsePayload(); + ASSERT_NE(nullptr, rep.get()); + OC_DBG("payload: %s", oc::RepPool::GetJson(rep.get(), true).data()); + oc_rep_t *platformRep = nullptr; + ASSERT_TRUE( + oc_rep_get_object(rep.get(), oc_string(platform->uri), &platformRep)); + + uint64_t etag = 0; + EXPECT_TRUE(oc_etag_decode_resource_etag(platform, platformRep, &etag)); + EXPECT_EQ(oc_resource_get_etag(platform), etag); +} + +constexpr std::string_view kETagKey{ "etag" }; +constexpr std::string_view kCRCKey{ "crc" }; + +TEST_F(TestETagWithServer, DecodeResource_FailMissingETag) +{ + oc_resource_t *platform = oc_core_get_resource_by_index(OCF_P, 0); + ASSERT_NE(nullptr, platform); + + oc::RepPool pool{}; + CborEncoder etag_map; + memset(&etag_map, 0, sizeof(etag_map)); + int err = oc_rep_encoder_create_map(oc_rep_get_encoder(), &etag_map, + CborIndefiniteLength); + err |= oc_rep_encode_text_string(&etag_map, kCRCKey.data(), kCRCKey.length()); + err |= oc_rep_encode_uint(&etag_map, 0); + err |= oc_rep_encoder_close_container(oc_rep_get_encoder(), &etag_map); + ASSERT_EQ(CborNoError, err); + oc::oc_rep_unique_ptr rep = pool.ParsePayload(); + ASSERT_NE(nullptr, rep.get()); + + uint64_t etag = 0; + EXPECT_FALSE(oc_etag_decode_resource_etag(platform, rep.get(), &etag)); +} + +TEST_F(TestETagWithServer, DecodeResource_FailInvalidETag) +{ + oc_resource_t *platform = oc_core_get_resource_by_index(OCF_P, 0); + ASSERT_NE(nullptr, platform); + + oc::RepPool pool{}; + CborEncoder etag_map; + memset(&etag_map, 0, sizeof(etag_map)); + int err = oc_rep_encoder_create_map(oc_rep_get_encoder(), &etag_map, + CborIndefiniteLength); + err |= + oc_rep_encode_text_string(&etag_map, kETagKey.data(), kETagKey.length()); + err |= oc_rep_encode_uint(&etag_map, OC_ETAG_UNINITIALIZED); + err |= oc_rep_encode_text_string(&etag_map, kCRCKey.data(), kCRCKey.length()); + err |= oc_rep_encode_uint(&etag_map, 0); + err |= oc_rep_encoder_close_container(oc_rep_get_encoder(), &etag_map); + ASSERT_EQ(CborNoError, err); + oc::oc_rep_unique_ptr rep = pool.ParsePayload(); + ASSERT_NE(nullptr, rep.get()); + + uint64_t etag = 0; + EXPECT_FALSE(oc_etag_decode_resource_etag(platform, rep.get(), &etag)); +} + +TEST_F(TestETagWithServer, DecodeResource_FailMissingCRC) +{ + oc_resource_t *platform = oc_core_get_resource_by_index(OCF_P, 0); + ASSERT_NE(nullptr, platform); + + oc::RepPool pool{}; + CborEncoder etag_map; + memset(&etag_map, 0, sizeof(etag_map)); + int err = oc_rep_encoder_create_map(oc_rep_get_encoder(), &etag_map, + CborIndefiniteLength); + err |= + oc_rep_encode_text_string(&etag_map, kETagKey.data(), kETagKey.length()); + err |= oc_rep_encode_uint(&etag_map, oc_resource_get_etag(platform)); + err |= oc_rep_encoder_close_container(oc_rep_get_encoder(), &etag_map); + ASSERT_EQ(CborNoError, err); + oc::oc_rep_unique_ptr rep = pool.ParsePayload(); + ASSERT_NE(nullptr, rep.get()); + + uint64_t etag = 0; + EXPECT_FALSE(oc_etag_decode_resource_etag(platform, rep.get(), &etag)); +} + +TEST_F(TestETagWithServer, DecodeResource_FailNoResourcePayload) +{ + std::string uri = "/test"; + oc_resource_t res{}; + res.uri = OC_MMEM(&uri[0], uri.length() + 1, nullptr); + res.etag = 1337; + + oc::RepPool pool{}; + CborEncoder etag_map; + memset(&etag_map, 0, sizeof(etag_map)); + int err = oc_rep_encoder_create_map(oc_rep_get_encoder(), &etag_map, + CborIndefiniteLength); + err |= + oc_rep_encode_text_string(&etag_map, kETagKey.data(), kETagKey.length()); + err |= oc_rep_encode_uint(&etag_map, res.etag); + err |= oc_rep_encode_text_string(&etag_map, kCRCKey.data(), kCRCKey.length()); + err |= oc_rep_encode_uint(&etag_map, 42); + err |= oc_rep_encoder_close_container(oc_rep_get_encoder(), &etag_map); + ASSERT_EQ(CborNoError, err); + oc::oc_rep_unique_ptr rep = pool.ParsePayload(); + ASSERT_NE(nullptr, rep.get()); + + uint64_t etag = 0; + EXPECT_FALSE(oc_etag_decode_resource_etag(&res, rep.get(), &etag)); +} + +TEST_F(TestETagWithServer, DecodeResource_InvalidChecksum) +{ + oc_resource_t *platform = oc_core_get_resource_by_index(OCF_P, 0); + ASSERT_NE(nullptr, platform); + + oc::RepPool pool{}; + CborEncoder etag_map; + memset(&etag_map, 0, sizeof(etag_map)); + int err = oc_rep_encoder_create_map(oc_rep_get_encoder(), &etag_map, + CborIndefiniteLength); + err |= + oc_rep_encode_text_string(&etag_map, kETagKey.data(), kETagKey.length()); + err |= oc_rep_encode_uint(&etag_map, oc_resource_get_etag(platform)); + err |= oc_rep_encode_text_string(&etag_map, kCRCKey.data(), kCRCKey.length()); + err |= oc_rep_encode_uint(&etag_map, 42); + err |= oc_rep_encoder_close_container(oc_rep_get_encoder(), &etag_map); + ASSERT_EQ(CborNoError, err); + oc::oc_rep_unique_ptr rep = pool.ParsePayload(); + ASSERT_NE(nullptr, rep.get()); + + uint64_t etag = 0; + EXPECT_FALSE(oc_etag_decode_resource_etag(platform, rep.get(), &etag)); +} + +#endif // OC_HAS_FEATURE_CRC_ENCODER + template static void getHandlerCheckCode(oc_client_response_t *data) diff --git a/api/unittest/querytest.cpp b/api/unittest/querytest.cpp index a388a44c1..e3a98a021 100644 --- a/api/unittest/querytest.cpp +++ b/api/unittest/querytest.cpp @@ -633,6 +633,9 @@ TEST_F(TestQuery, EncodeInterface_P) OC_IF_W, OC_IF_STARTUP, OC_IF_STARTUP_REVERT, +#ifdef OC_HAS_FEATURE_ETAG_INTERFACE + PLGD_IF_ETAG, +#endif /* OC_HAS_FEATURE_ETAG_INTERFACE */ }; std::vector iface_strs = { @@ -647,6 +650,9 @@ TEST_F(TestQuery, EncodeInterface_P) OC_IF_W_STR, OC_IF_STARTUP_STR, OC_IF_STARTUP_REVERT_STR, +#ifdef OC_HAS_FEATURE_ETAG_INTERFACE + PLGD_IF_ETAG_STR, +#endif /* OC_HAS_FEATURE_ETAG_INTERFACE */ }; for (size_t i = 0; i < ifaces.size(); ++i) { diff --git a/include/oc_ri.h b/include/oc_ri.h index 983726239..535f72ca3 100644 --- a/include/oc_ri.h +++ b/include/oc_ri.h @@ -167,17 +167,20 @@ typedef enum { * */ typedef enum { - OC_IF_BASELINE = 1 << 1, ///< oic.if.baseline - OC_IF_LL = 1 << 2, ///< oic.if.ll - OC_IF_B = 1 << 3, ///< oic.if.b - OC_IF_R = 1 << 4, ///< oic.if.r - OC_IF_RW = 1 << 5, ///< oic.if.rw - OC_IF_A = 1 << 6, ///< oic.if.a - OC_IF_S = 1 << 7, ///< oic.if.s - OC_IF_CREATE = 1 << 8, ///< oic.if.create - OC_IF_W = 1 << 9, ///< oic.if.w - OC_IF_STARTUP = 1 << 10, ///< oic.if.startup - OC_IF_STARTUP_REVERT = 1 << 11 ///< oic.if.startup.revert + OC_IF_BASELINE = 1 << 1, ///< oic.if.baseline + OC_IF_LL = 1 << 2, ///< oic.if.ll + OC_IF_B = 1 << 3, ///< oic.if.b + OC_IF_R = 1 << 4, ///< oic.if.r + OC_IF_RW = 1 << 5, ///< oic.if.rw + OC_IF_A = 1 << 6, ///< oic.if.a + OC_IF_S = 1 << 7, ///< oic.if.s + OC_IF_CREATE = 1 << 8, ///< oic.if.create + OC_IF_W = 1 << 9, ///< oic.if.w + OC_IF_STARTUP = 1 << 10, ///< oic.if.startup + OC_IF_STARTUP_REVERT = 1 << 11, ///< oic.if.startup.revert +#ifdef OC_HAS_FEATURE_ETAG_INTERFACE + PLGD_IF_ETAG = 1 << 12, ///< x.plgd.if.etag +#endif /* OC_HAS_FEATURE_ETAG_INTERFACE */ } oc_interface_mask_t; typedef enum { @@ -496,7 +499,7 @@ int oc_coap_status_to_status(coap_status_t status); * * @param[in] key key the application level key of the code * @return CoAP status code in const char * - * @return Empty string for an invalid log level value + * @return Empty string for an invalid status value */ OC_API const char *oc_status_to_str(oc_status_t key); @@ -505,7 +508,7 @@ const char *oc_status_to_str(oc_status_t key); * @brief Convert method to string. It is thread safe. * * @return Method in const char *. - * @return Empty string for an invalid log level value + * @return Empty string for an invalid method value */ OC_API const char *oc_method_to_str(oc_method_t method); diff --git a/util/oc_crc_internal.h b/util/oc_crc_internal.h index 1d427021d..1f00bc673 100644 --- a/util/oc_crc_internal.h +++ b/util/oc_crc_internal.h @@ -23,7 +23,6 @@ #ifdef OC_HAS_FEATURE_CRC_ENCODER -#include "util/oc_compiler.h" #include #include @@ -35,13 +34,12 @@ extern "C" { * @brief Calculate CRC64 for a buffer of data. * * @param crc The initial CRC64 value (use 0 to start a new CRC64 calculation). - * @param buffer The buffer of data to calculate CRC64 for (cannot be NULL). + * @param buffer The buffer of data to calculate CRC64 for. * @param size The size of the buffer of data to calculate CRC64 for. * * @return The CRC64 value. */ -uint64_t oc_crc64(uint64_t crc, const uint8_t *buffer, size_t size) - OC_NONNULL(); +uint64_t oc_crc64(uint64_t crc, const uint8_t *buffer, size_t size); #ifdef __cplusplus } diff --git a/util/oc_features.h b/util/oc_features.h index 62100c55a..24139f253 100644 --- a/util/oc_features.h +++ b/util/oc_features.h @@ -62,8 +62,9 @@ #endif /* OC_ETAG && OC_SERVER */ -#ifdef OC_HAS_FEATURE_ETAG +#if defined(OC_HAS_FEATURE_ETAG) && defined(OC_STORAGE) #define OC_HAS_FEATURE_CRC_ENCODER -#endif /* OC_HAS_FEATURE_ETAG */ +#define OC_HAS_FEATURE_ETAG_INTERFACE +#endif /* OC_HAS_FEATURE_ETAG && OC_STORAGE */ #endif /* OC_FEATURES_H */