diff options
Diffstat (limited to 'libccnx-common/ccnx')
230 files changed, 56640 insertions, 0 deletions
diff --git a/libccnx-common/ccnx/common/.gitignore b/libccnx-common/ccnx/common/.gitignore new file mode 100644 index 00000000..e81ef847 --- /dev/null +++ b/libccnx-common/ccnx/common/.gitignore @@ -0,0 +1,2 @@ +ccnx_LibraryVersion.c +paths.c diff --git a/libccnx-common/ccnx/common/CMakeLists.txt b/libccnx-common/ccnx/common/CMakeLists.txt new file mode 100644 index 00000000..34e71e79 --- /dev/null +++ b/libccnx-common/ccnx/common/CMakeLists.txt @@ -0,0 +1,272 @@ +# Define a few configuration variables that we want accessible in the software + +set(VALIDATION_HDRS + validation/ccnxValidation_CRC32C.h + validation/ccnxValidation_EcSecp256K1.h + validation/ccnxValidation_HmacSha256.h + validation/ccnxValidation_RsaSha256.h + ) + +source_group(validation FILES ${VALIDATION_HDRS}) + +set(INTERNAL_HDRS + internal/ccnx_ChunkingFacadeV1.h + internal/ccnx_ContentObjectFacadeV1.h + internal/ccnx_ContentObjectInterface.h + internal/ccnx_InterestFacadeV1.h + internal/ccnx_InterestDefault.h + internal/ccnx_InterestInterface.h + internal/ccnx_InterestReturnFacadeV1.h + internal/ccnx_InterestReturnInterface.h + internal/ccnx_InterestPayloadIdMethod.h + internal/ccnx_ManifestFacadeV1.h + internal/ccnx_ManifestInterface.h + internal/ccnx_MessageInterface.h + internal/ccnx_TlvDictionary.h + internal/ccnx_ValidationFacadeV1.h + internal/ccnx_WireFormatFacadeV1.h + internal/ccnx_WireFormatMessageInterface.h + ) + +source_group(internal FILES ${INTERNAL_HDRS}) + +set(CODEC_HDRS + codec/ccnxCodec_EncodingBuffer.h + codec/ccnxCodec_Error.h + codec/ccnxCodec_ErrorCodes.h + codec/ccnxCodec_NetworkBuffer.h + codec/ccnxCodec_TlvEncoder.h + codec/ccnxCodec_TlvDecoder.h + codec/ccnxCodec_TlvUtilities.h + codec/ccnxCodec_TlvPacket.h + ) + +source_group(codec FILES ${CODEC_HDRS}) + +set(TESTDATA_HDRS + codec/testdata/tlv_Schema.h + codec/testdata/testdata_common.h + ) + +set(SCHEMAV1_TESTDATA_HDRS + codec/schema_v1/testdata/v1_content_nameA_crc32c.h + codec/schema_v1/testdata/v1_content_nameA_keyid1_rsasha256.h + codec/schema_v1/testdata/v1_content_no_payload.h + codec/schema_v1/testdata/v1_ContentObjectSchema.h + codec/schema_v1/testdata/v1_content_zero_payload.h + codec/schema_v1/testdata/v1_cpi_add_route_crc32c.h + codec/schema_v1/testdata/v1_cpi_add_route.h + codec/schema_v1/testdata/v1_CPISchema.h + codec/schema_v1/testdata/v1_interest_all_fields.h + codec/schema_v1/testdata/v1_interest_bad_message_length.h + codec/schema_v1/testdata/v1_interest_bad_validation_alg.h + codec/schema_v1/testdata/v1_interest_nameA_badcrc32c.h + codec/schema_v1/testdata/v1_interest_nameA_crc32c.h + codec/schema_v1/testdata/v1_interest_nameA.h + codec/schema_v1/testdata/v1_InterestSchema.h + codec/schema_v1/testdata/v1_interest_validation_alg_overrun.h + codec/schema_v1/testdata/v1_testrig_truthSet.h + codec/schema_v1/testdata/v1_testrig_truthTable.h + ) + +source_group(schema_v1_testData FILES ${SCHEMAV1_TESTDATA_HDRS}) + +set(SCHEMAV1_HDRS + codec/schema_v1/ccnxCodecSchemaV1_TlvDictionary.h + codec/schema_v1/ccnxCodecSchemaV1_CryptoSuite.h + codec/schema_v1/ccnxCodecSchemaV1_FixedHeaderDecoder.h + codec/schema_v1/ccnxCodecSchemaV1_FixedHeaderEncoder.h + codec/schema_v1/ccnxCodecSchemaV1_FixedHeader.h + codec/schema_v1/ccnxCodecSchemaV1_HashCodec.h + codec/schema_v1/ccnxCodecSchemaV1_LinkCodec.h + codec/schema_v1/ccnxCodecSchemaV1_ManifestDecoder.h + codec/schema_v1/ccnxCodecSchemaV1_ManifestEncoder.h + codec/schema_v1/ccnxCodecSchemaV1_MessageDecoder.h + codec/schema_v1/ccnxCodecSchemaV1_MessageEncoder.h + codec/schema_v1/ccnxCodecSchemaV1_NameCodec.h + codec/schema_v1/ccnxCodecSchemaV1_NameSegmentCodec.h + codec/schema_v1/ccnxCodecSchemaV1_OptionalHeadersDecoder.h + codec/schema_v1/ccnxCodecSchemaV1_OptionalHeadersEncoder.h + codec/schema_v1/ccnxCodecSchemaV1_PacketDecoder.h + codec/schema_v1/ccnxCodecSchemaV1_PacketEncoder.h + codec/schema_v1/ccnxCodecSchemaV1_Types.h + codec/schema_v1/ccnxCodecSchemaV1_ValidationDecoder.h + codec/schema_v1/ccnxCodecSchemaV1_ValidationEncoder.h + ) + +source_group(codec FILES ${SCHEMAV1_HDRS}) + +set(CORE_HDRS + libccnxCommon_About.h + ccnx_ContentObject.h + ccnx_Interest.h + ccnx_InterestReturn.h + ccnx_InterestPayloadId.h + ccnx_KeyLocator.h + ccnx_KeystoreUtilities.h + ccnx_Link.h + ccnx_Manifest.h + ccnx_ManifestHashGroup.h + ccnx_Name.h + ccnx_NameSegment.h + ccnx_NameSegmentNumber.h + ccnx_NameLabel.h + ccnx_PayloadType.h + ccnx_TimeStamp.h + ccnx_WireFormatMessage.h + ) + +source_group(core FILES ${CORE_HDRS}) + +set(ALL_HDRS + ${VALIDATION_HDRS} + ${INTERNAL_HDRS} + ${CODEC_HDRS} + ${TESTDATA_HDRS} + ${SCHEMAV1_TESTDATA_HDRS} + ${SCHEMAV1_HDRS} + ${CORE_HDRS}) + +set(VALIDATION_SRCS + validation/ccnxValidation_CRC32C.c + validation/ccnxValidation_EcSecp256K1.c + validation/ccnxValidation_HmacSha256.c + validation/ccnxValidation_RsaSha256.c + ) + +source_group(validation FILES ${VALIDATION_SRCS}) + +set(INTERNAL_SRCS + internal/ccnx_ChunkingFacadeV1.c + internal/ccnx_ContentObjectFacadeV1.c + internal/ccnx_ContentObjectInterface.c + internal/ccnx_InterestFacadeV1.c + internal/ccnx_InterestDefault.c + internal/ccnx_InterestInterface.c + internal/ccnx_InterestReturnFacadeV1.c + internal/ccnx_InterestReturnInterface.c + internal/ccnx_ManifestFacadeV1.c + internal/ccnx_ManifestInterface.c + internal/ccnx_TlvDictionary.c + internal/ccnx_ValidationFacadeV1.c + internal/ccnx_WireFormatFacadeV1.c + internal/ccnx_WireFormatMessageInterface.c + ) + +source_group(internal FILES ${INTERNAL_SRCS}) + +set(CODEC_V1_SRCS + codec/schema_v1/ccnxCodecSchemaV1_CryptoSuite.c + codec/schema_v1/ccnxCodecSchemaV1_FixedHeaderDecoder.c + codec/schema_v1/ccnxCodecSchemaV1_FixedHeaderEncoder.c + codec/schema_v1/ccnxCodecSchemaV1_HashCodec.c + codec/schema_v1/ccnxCodecSchemaV1_LinkCodec.c + codec/schema_v1/ccnxCodecSchemaV1_ManifestDecoder.c + codec/schema_v1/ccnxCodecSchemaV1_ManifestEncoder.c + codec/schema_v1/ccnxCodecSchemaV1_MessageDecoder.c + codec/schema_v1/ccnxCodecSchemaV1_MessageEncoder.c + codec/schema_v1/ccnxCodecSchemaV1_NameCodec.c + codec/schema_v1/ccnxCodecSchemaV1_NameSegmentCodec.c + codec/schema_v1/ccnxCodecSchemaV1_OptionalHeadersDecoder.c + codec/schema_v1/ccnxCodecSchemaV1_OptionalHeadersEncoder.c + codec/schema_v1/ccnxCodecSchemaV1_PacketDecoder.c + codec/schema_v1/ccnxCodecSchemaV1_PacketEncoder.c + codec/schema_v1/ccnxCodecSchemaV1_TlvDictionary.c + codec/schema_v1/ccnxCodecSchemaV1_ValidationDecoder.c + codec/schema_v1/ccnxCodecSchemaV1_ValidationEncoder.c + ) + +source_group(codec FILES ${CODEC_V1_SRCS}) + +set(CODEC_SRCS + codec/ccnxCodec_EncodingBuffer.c + codec/ccnxCodec_Error.c + codec/ccnxCodec_NetworkBuffer.c + codec/ccnxCodec_TlvEncoder.c + codec/ccnxCodec_TlvDecoder.c + codec/ccnxCodec_TlvUtilities.c + codec/ccnxCodec_TlvPacket.c + ${CODEC_V1_SRCS} + ) + +source_group(codec FILES ${CODEC_SRCS}) + +set(CORE_SRCS + libccnxCommon_About.c + ccnx_ContentObject.c + ccnx_Interest.c + ccnx_InterestReturn.c + ccnx_InterestPayloadId.c + ccnx_KeyLocator.c + ccnx_KeystoreUtilities.c + ccnx_Link.c + ccnx_Manifest.c + ccnx_ManifestHashGroup.c + ccnx_Name.c + ccnx_NameSegment.c + ccnx_NameSegmentNumber.c + ccnx_NameLabel.c + ccnx_TimeStamp.c + ccnx_WireFormatMessage.c + ) + +source_group(core FILES ${CORE_SRCS}) + +set(ALL_SRCS + ${INTERNAL_SRCS} + ${VALIDATION_SRCS} + ${CORE_SRCS} + ${CODEC_SRCS} + ${VALIDATION_HDRS} + ${INTERNAL_HDRS} + ${CODEC_HDRS} + ${TESTDATA_HDRS} + ${SCHEMAV1_TESTDATA_HDRS} + ${SCHEMAV1_HDRS} + ${COMMON_HDRS} + ) + +set(PUBLIC_HDRS + ${CODEC_PRIVATE_HDRS} +) + +configure_file("config.h.in" "config.h" @ONLY) + +if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang") + set(CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS "${CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS} -undefined dynamic_lookup") + message( "-- Set \"-undefined dynamic_lookup\" for shared libraries") +endif() + +add_library(ccnx_common STATIC ${ALL_SRCS} ${ALL_HDRS}) +add_library(ccnx_common.shared SHARED ${ALL_SRCS}) + +target_link_libraries(ccnx_common.shared ${LIBPARC_LIBRARIES}) +set_target_properties(ccnx_common.shared PROPERTIES + C_STANDARD 99 + SOVERSION 1 + VERSION 1.0 + OUTPUT_NAME ccnx_common ) + +set(libccnx_commonLibraries + ccnx_common + ccnx_common.shared + ) + +foreach(lib ${libccnx_commonLibraries}) + install(TARGETS ${lib} LIBRARY DESTINATION lib ARCHIVE DESTINATION lib) + set_property(TARGET ${lib} PROPERTY C_STANDARD 99) +endforeach() + +install(FILES ${CORE_HDRS} DESTINATION include/ccnx/common ) +install(FILES ${INTERNAL_HDRS} DESTINATION include/ccnx/common/internal ) +install(FILES ${VALIDATION_HDRS} DESTINATION include/ccnx/common/validation ) +install(FILES ${CODEC_HDRS} DESTINATION include/ccnx/common/codec ) +install(FILES ${TESTDATA_HDRS} DESTINATION include/ccnx/common/codec/testdata ) +install(FILES ${SCHEMAV1_HDRS} DESTINATION include/ccnx/common/codec/schema_v1 ) +install(FILES ${SCHEMAV1_TESTDATA_HDRS} DESTINATION include/ccnx/common/codec/schema_v1/testdata ) + +#add_subdirectory(test) +#add_subdirectory(validation/test) +#add_subdirectory(codec/test) +#add_subdirectory(internal/test) +#add_subdirectory(codec/schema_v1/test) diff --git a/libccnx-common/ccnx/common/Groups.dox b/libccnx-common/ccnx/common/Groups.dox new file mode 100644 index 00000000..8f3dec80 --- /dev/null +++ b/libccnx-common/ccnx/common/Groups.dox @@ -0,0 +1,16 @@ +/** +@defgroup Naming CCN Naming +@brief CCN Naming + +@defgroup Interest CCN Interest +@brief Functions to manipulate CCN Interest instances + +@defgroup ContentObject CCN Content Object +@brief Functions to manipulate CCN Content Object instances. + +@defgroup Signature Signatures and Keys +@brief Functions to manipulate Signatures and Keys. + +@defgroup Utility General Purpose functionality. +@brief General purpose utility objects and functionality. +*/ diff --git a/libccnx-common/ccnx/common/ccnx_ContentObject.c b/libccnx-common/ccnx/common/ccnx_ContentObject.c new file mode 100644 index 00000000..b7367da9 --- /dev/null +++ b/libccnx-common/ccnx/common/ccnx_ContentObject.c @@ -0,0 +1,406 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ +#include <config.h> + +#include <LongBow/runtime.h> + +#include <ccnx/common/internal/ccnx_ContentObjectInterface.h> + +#include <ccnx/common/ccnx_ContentObject.h> + +#include <ccnx/common/validation/ccnxValidation_HmacSha256.h> +#include <ccnx/common/validation/ccnxValidation_RsaSha256.h> + +#include <ccnx/common/internal/ccnx_ValidationFacadeV1.h> + +static const CCNxContentObjectInterface *_defaultImplementation = &CCNxContentObjectFacadeV1_Implementation; + +/** + * A CCNxContentObject is a wrapper class providing a consistent interface for applications. + * Internally, the data of a ContentObject is stored in the underlying ccnxTlvDictionary. + * Because we may have different schemas for a ContentObject, an CCNxContentObjectInterface pointer + * is also stored in the underlying ccnxTlvDictionary. This provides access to the functions + * used to create and access a particular implementation of a ContentObject. + */ + +CCNxContentObject * +ccnxContentObject_CreateWithNameAndPayload(const CCNxName *contentName, const PARCBuffer *payload) +{ + return ccnxContentObject_CreateWithImplAndPayload(_defaultImplementation, + contentName, + CCNxPayloadType_DATA, + payload); +} + +CCNxContentObject * +ccnxContentObject_CreateWithPayload(const PARCBuffer *payload) +{ + return ccnxContentObject_CreateWithImplAndPayload(_defaultImplementation, + NULL, + CCNxPayloadType_DATA, + payload); +} + +CCNxContentObject * +ccnxContentObject_CreateWithImplAndPayload(const CCNxContentObjectInterface *impl, + const CCNxName *contentName, + const CCNxPayloadType payloadType, + const PARCBuffer *payload) +{ + CCNxContentObject *result = NULL; + + if (impl->createWithNameAndPayload != NULL) { + if (contentName == NULL) { + result = impl->createWithPayload(payloadType, payload); + } else { + result = impl->createWithNameAndPayload(contentName, payloadType, payload); + } + + // And set the dictionary's interface pointer to the one we just used to create this. + ccnxTlvDictionary_SetMessageInterface(result, impl); + } else { + trapNotImplemented("ContentObject implementations must implement createWithNameAndPayload()"); + } + + ccnxContentObject_SetPathLabel(result, 0); + + return result; +} + +bool +ccnxContentObject_SetSignature(CCNxContentObject *contentObject, const PARCBuffer *keyId, + const PARCSignature *signature, const CCNxKeyLocator *keyLocator) +{ + ccnxContentObject_OptionalAssertValid(contentObject); + CCNxContentObjectInterface *impl = ccnxContentObjectInterface_GetInterface(contentObject); + + bool result = false; + + if (impl->setSignature != NULL) { + result = impl->setSignature(contentObject, keyId, signature, keyLocator); + } + + return result; +} + +PARCBuffer * +ccnxContentObject_GetKeyId(const CCNxContentObject *contentObject) +{ + ccnxContentObject_OptionalAssertValid(contentObject); + + PARCBuffer *result = NULL; + + CCNxContentObjectInterface *impl = ccnxContentObjectInterface_GetInterface(contentObject); + if (impl->getKeyId != NULL) { + result = impl->getKeyId(contentObject); + } + + return result; +} + +CCNxName * +ccnxContentObject_GetName(const CCNxContentObject *contentObject) +{ + ccnxContentObject_OptionalAssertValid(contentObject); + CCNxContentObjectInterface *impl = ccnxContentObjectInterface_GetInterface(contentObject); + + CCNxName *result = NULL; + + if (impl->getName != NULL) { + result = impl->getName(contentObject); + } else { + trapNotImplemented("ccnxContentObject_GetName"); + } + + return result; +} + +PARCBuffer * +ccnxContentObject_GetPayload(const CCNxContentObject *contentObject) +{ + ccnxContentObject_OptionalAssertValid(contentObject); + CCNxContentObjectInterface *impl = ccnxContentObjectInterface_GetInterface(contentObject); + + PARCBuffer *result = NULL; + + if (impl->getPayload != NULL) { + result = impl->getPayload(contentObject); + } else { + trapNotImplemented("ccnxContentObject_GetPayload"); + } + + return result; +} + +CCNxPayloadType +ccnxContentObject_GetPayloadType(const CCNxContentObject *contentObject) +{ + ccnxContentObject_OptionalAssertValid(contentObject); + CCNxContentObjectInterface *impl = ccnxContentObjectInterface_GetInterface(contentObject); + + CCNxPayloadType result = CCNxPayloadType_DATA; + + if (impl->getPayloadType != NULL) { + result = impl->getPayloadType(contentObject); + } else { + trapNotImplemented("ccnxContentObject_GetPayloadType"); + } + + return result; +} + +bool +ccnxContentObject_SetPayload(CCNxContentObject *contentObject, CCNxPayloadType payloadType, const PARCBuffer *payload) +{ + ccnxContentObject_OptionalAssertValid(contentObject); + CCNxContentObjectInterface *impl = ccnxContentObjectInterface_GetInterface(contentObject); + + bool result = false;; + + if (impl->setPayload != NULL) { + result = impl->setPayload(contentObject, payloadType, payload);; + } else { + trapNotImplemented("ccnxContentObject_SetPayload"); + } + + return result; +} + +bool +ccnxContentObject_SetExpiryTime(CCNxContentObject *contentObject, const uint64_t expiryTIme) +{ + bool result = false; + + ccnxContentObject_OptionalAssertValid(contentObject); + CCNxContentObjectInterface *impl = ccnxContentObjectInterface_GetInterface(contentObject); + + if (impl->setExpiryTime != NULL) { + result = impl->setExpiryTime(contentObject, expiryTIme); + } else { + trapNotImplemented("ccnxContentObject_SetExpiryTime"); + } + return result; +} + +bool +ccnxContentObject_HasExpiryTime(const CCNxContentObject *contentObject) +{ + ccnxContentObject_OptionalAssertValid(contentObject); + CCNxContentObjectInterface *impl = ccnxContentObjectInterface_GetInterface(contentObject); + + bool result = false; + + if (impl->hasExpiryTime != NULL) { + result = impl->hasExpiryTime(contentObject); + } else { + return false; + } + + return result; +} + +uint64_t +ccnxContentObject_GetExpiryTime(const CCNxContentObject *contentObject) +{ + ccnxContentObject_OptionalAssertValid(contentObject); + CCNxContentObjectInterface *impl = ccnxContentObjectInterface_GetInterface(contentObject); + + if (impl->hasExpiryTime != NULL && !impl->hasExpiryTime((CCNxTlvDictionary *) contentObject)) { + trapUnexpectedState("ContentObject has no ExpiryTime. Call HasExpiryTime() first."); + } + + if (impl->getExpiryTime != NULL) { + return impl->getExpiryTime(contentObject); + } else { + trapNotImplemented("ccnxContentObject_HasExpiryTime"); + } +} + + +uint64_t +ccnxContentObject_GetPathLabel(const CCNxContentObject *contentObject) +{ + ccnxContentObject_OptionalAssertValid(contentObject); + CCNxContentObjectInterface *impl = ccnxContentObjectInterface_GetInterface(contentObject); + + if (impl->hasPathLabel != NULL && !impl->hasPathLabel((CCNxTlvDictionary *) contentObject)) { + trapUnexpectedState("ContentObject has no PathLabel. Call HasPathLabel() first."); + } + + if (impl->getPathLabel != NULL) { + return impl->getPathLabel(contentObject); + } else { + trapNotImplemented("ccnxContentObject_GetPathLabel"); + } +} + +bool +ccnxContentObject_SetPathLabel(CCNxContentObject *contentObject, const uint64_t pathLabel) +{ + bool result = false; + + ccnxContentObject_OptionalAssertValid(contentObject); + CCNxContentObjectInterface *impl = ccnxContentObjectInterface_GetInterface(contentObject); + + if (impl->setPathLabel != NULL) { + result = impl->setPathLabel(contentObject, pathLabel); + } else { + trapNotImplemented("ccnxContentObject_SetPathLabel"); + } + return result; +} + +bool +ccnxContentObject_HasPathLabel(const CCNxContentObject *contentObject) +{ + ccnxContentObject_OptionalAssertValid(contentObject); + CCNxContentObjectInterface *impl = ccnxContentObjectInterface_GetInterface(contentObject); + + bool result = false; + + if (impl->hasPathLabel != NULL) { + result = impl->hasPathLabel(contentObject); + } else { + return false; + } + + return result; +} + +bool +ccnxContentObject_SetFinalChunkNumber(CCNxContentObject *contentObject, const uint64_t finalChunkNumber) +{ + ccnxContentObject_OptionalAssertValid(contentObject); + CCNxContentObjectInterface *impl = ccnxContentObjectInterface_GetInterface(contentObject); + + bool result = false; + + if (impl->setFinalChunkNumber != NULL) { + result = impl->setFinalChunkNumber(contentObject, finalChunkNumber); + } else { + trapNotImplemented("ccnxContentObject_SetFinalChunkNumber"); + } + return result; +} + +bool +ccnxContentObject_HasFinalChunkNumber(const CCNxContentObject *contentObject) +{ + ccnxContentObject_OptionalAssertValid(contentObject); + CCNxContentObjectInterface *impl = ccnxContentObjectInterface_GetInterface(contentObject); + + bool result = false; + + if (impl->hasFinalChunkNumber != NULL) { + result = impl->hasFinalChunkNumber(contentObject); + } else { + trapNotImplemented("ccnxContentObject_HasFinalChunkNumber"); + } + + return result; +} + +uint64_t +ccnxContentObject_GetFinalChunkNumber(const CCNxContentObject *contentObject) +{ + ccnxContentObject_OptionalAssertValid(contentObject); + CCNxContentObjectInterface *impl = ccnxContentObjectInterface_GetInterface(contentObject); + + if (impl->hasFinalChunkNumber != NULL && !impl->hasFinalChunkNumber((CCNxTlvDictionary *) contentObject)) { + trapUnexpectedState("ContentObject has no final chunk number. Call ccnxContentObject_HasFinalChunkNumber() first."); + } + + if (impl->getFinalChunkNumber != NULL) { + return impl->getFinalChunkNumber(contentObject); + } else { + trapNotImplemented("ccnxContentObject_GetFinalChunkNumber"); + } +} + +void +ccnxContentObject_Display(const CCNxContentObject *contentObject, int indentation) +{ + ccnxContentObject_OptionalAssertValid(contentObject); + CCNxContentObjectInterface *impl = ccnxContentObjectInterface_GetInterface(contentObject); + + if (impl->display != NULL) { + impl->display(contentObject, indentation); + } else { + ccnxTlvDictionary_Display(contentObject, indentation); + } +} + +char * +ccnxContentObject_ToString(const CCNxContentObject *contentObject) +{ + ccnxContentObject_OptionalAssertValid(contentObject); + CCNxContentObjectInterface *impl = ccnxContentObjectInterface_GetInterface(contentObject); + + char *result = NULL; + + if (impl->toString != NULL) { + result = impl->toString(contentObject); + } else { + trapNotImplemented("ccnxContentObject_ToString"); + } + return result; +} + +bool +ccnxContentObject_Equals(const CCNxContentObject *objectA, const CCNxContentObject *objectB) +{ + CCNxContentObjectInterface *implA = ccnxContentObjectInterface_GetInterface(objectA); + CCNxContentObjectInterface *implB = ccnxContentObjectInterface_GetInterface(objectB); + + assertNotNull(implA, "ContentObject must have an valid implementation pointer."); + assertNotNull(implB, "ContentObject must have an valid implementation pointer."); + + if (implA != implB) { + return false; + } + + if (implA->equals != NULL) { + return implA->equals(objectA, objectB); + } else { + trapNotImplemented("ccnxContentObject_Equals"); + } +} + +CCNxContentObject * +ccnxContentObject_Acquire(const CCNxContentObject *contentObject) +{ + return ccnxTlvDictionary_Acquire(contentObject); +} + +void +ccnxContentObject_Release(CCNxContentObject **contentObjectP) +{ + ccnxTlvDictionary_Release(contentObjectP); +} + +void +ccnxContentObject_AssertValid(const CCNxContentObject *contentObject) +{ + assertNotNull(contentObject, "Parameter must be a non-null CCNxContentObject pointer"); + CCNxContentObjectInterface *impl = ccnxContentObjectInterface_GetInterface(contentObject); + + assertNotNull(impl, "ContentObject must have a non-NUll implementation"); + if (impl->assertValid != NULL) { + impl->assertValid(contentObject); + } +} diff --git a/libccnx-common/ccnx/common/ccnx_ContentObject.h b/libccnx-common/ccnx/common/ccnx_ContentObject.h new file mode 100755 index 00000000..bff92433 --- /dev/null +++ b/libccnx-common/ccnx/common/ccnx_ContentObject.h @@ -0,0 +1,677 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file ccnx_ContentObject.h + * @brief A CCNx ContentObject contains content to be sent in response to an Interest. + * + * The canonical CCN content object. A content object contains a payload, a {@link CCNxName}, + * and security binding information. It's sent in response to a CCN Interest. + * + * @see {@link CCNxInterest} + * + */ + +#ifndef libccnx_ccnx_ContentObject_h +#define libccnx_ccnx_ContentObject_h +#include <stdbool.h> + +#include <ccnx/common/ccnx_Name.h> +#include <ccnx/common/ccnx_PayloadType.h> +#include <ccnx/common/ccnx_KeyLocator.h> + +#include <ccnx/common/internal/ccnx_ContentObjectInterface.h> +#include <ccnx/common/internal/ccnx_TlvDictionary.h> + +#include <parc/security/parc_Signature.h> + +#include <parc/algol/parc_Buffer.h> + +/** + * @typedef CCNxContentObject + * @brief The CCNx Content Object + */ +typedef CCNxTlvDictionary CCNxContentObject; + +/** + * Create a new instance of a `CCNxContentObject`, using dynamically allocated memory, with + * the specified name and payload. + * + * The created instance must be released by calling {@link ccnxContentObject_Release()}. + * + * @param [in] contentName The CCNxName associated with this `CCNxContentObject`. + * @param [in] payload The data to be encapsulated by this `CCNxContentObject`. May be NULL. + * + * @return A new instance of a `CCNxContentObject`. + * + * Example: + * @code + * { + * CCNxName *name = ccnxName_CreateFromCString("lci:/foo/bar"); + * PARCBuffer *payload = parcBuffer_Allocate(<...>); + * + * CCNxContentObject *contentObject = ccnxContentObject_CreateWithNameAndPayload(name, payload); + * + * ccnxContentObject_Release(&contentObject); + * ccnxName_Release(&name); + * parcBuffer_Release(&payload); + * } + * @endcode + * + * @see {@link ccnxContentObject_Release} + */ +CCNxContentObject *ccnxContentObject_CreateWithNameAndPayload(const CCNxName *contentName, + const PARCBuffer *payload); + +/** + * Create a new instance of a `CCNxContentObject`, using dynamically allocated memory, with + * the specified payload. This will be a "nameless" Content Object. + * + * The created instance must be released by calling {@link ccnxContentObject_Release()}. + * + * @param [in] payload The data to be encapsulated by this `CCNxContentObject`. May be NULL. + * + * @return A new instance of a `CCNxContentObject`. + * + * Example: + * @code + * { + * CCNxName *name = ccnxName_CreateFromCString("lci:/foo/bar"); + * PARCBuffer *payload = parcBuffer_Allocate(<...>); + * + * CCNxContentObject *contentObject = ccnxContentObject_CreateWithNameAndPayload(name, payload); + * + * ccnxContentObject_Release(&contentObject); + * ccnxName_Release(&name); + * parcBuffer_Release(&payload); + * } + * @endcode + * + * @see {@link ccnxContentObject_Release} + */ +CCNxContentObject *ccnxContentObject_CreateWithPayload(const PARCBuffer *payload); + +/** + * Create a new instance of a `CCNxContentObject`, using dynamically allocated memory, with + * the specified payload, using the specified {@link CCNxContentObjectInterface}. + * + * The created instance must be released by calling {@link ccnxContentObject_Release}(). + * + * @param [in] implementation The interface's underlying implementation to use to build this ContentObject. + * @param [in] contentName The CCNxName associated with this `CCNxContentObject`. + * @param [in] type the type of the payload. Must be a valid {@link CCNxPayloadType}. + * @param [in] payload The data to be encapsulated by this `CCNxContentObject`. May be NULL. + * + * @return A new instance of a `CCNxContentObject`. + * + * Example: + * @code + * { + * CCNxName *name = ccnxName_CreateFromCString("lci:/foo/bar"); + * PARCBuffer *payload = parcBuffer_Allocate(<...>); + * + * CCNxContentObject *contentObject = + * ccnxContentObject_CreateWithImplAndPayload(&contentObjectImpl_Facade_V0, + * name, + * CCNxPayloadType_DATA, + * payload); + * + * ccnxContentObject_Release(&contentObject); + * ccnxName_Release(&name); + * parcBuffer_Release(&payload); + * } + * @endcode + * + * @see ccnxContentObject_Release + */ +CCNxContentObject *ccnxContentObject_CreateWithImplAndPayload(const CCNxContentObjectInterface *implementation, + const CCNxName *contentName, + const CCNxPayloadType type, + const PARCBuffer *payload); + + +/** + * Return a pointer to the {@link CCNxName} associated with this `CCNxContentObject`. + * + * This is memory managed by the `CCNxContentObject` and does not have to be released separately + * unless {@link ccnxName_Acquire()} is called to acquire it. + * + * @param [in] contentObject An pointer to an instance of a `CCNxContentObject`. + * + * @return A pointer to the `CCNxName` associated with the specified `CCNxContentObject`. + * + * Example: + * @code + * { + * CCNxName *name = ccnxName_CreateFromCString("lci:/foo/bar"); + * PARCBuffer *payload = parcBuffer_Allocate(<...>); + * + * CCNxContentObject *contentObject = ccnxContentObject_CreateWithNameAndPayload(name, payload); + * + * CCNxName *pointerToName = ccnxContentObject_GetName(contentObject); + * + * ... + * + * ccnxContentObject_Release(&contentObject); + * ccnxName_Release(&name); + * parcBuffer_Release(&payload); + * } + * @endcode + * + * @see `CCNxName` + */ +CCNxName *ccnxContentObject_GetName(const CCNxContentObject *contentObject); + +/** + * Return a pointer to the payload associated with this `CCNxContentObject`. + * + * The returned pointer points to the {@link PARCBuffer} containing the `CCNxContentObject`'s payload. + * This is memory managed by the `CCNxContentObject` and does not have to be released separately + * unless {@link parcBuffer_Acquire()} is called to acquire it. + * + * @param [in] contentObject A pointer to an instance of a `CCNxContentObject`. + * + * @return A pointer to the `PARCBuffer` containing the specified `CCNxContentObject`'s payload. + * + * Example: + * @code + * { + * CCNxName *name = ccnxName_CreateFromCString("lci:/foo/bar"); + * PARCBuffer *payload = parcBuffer_Allocate(<...>); + * + * CCNxContentObject *contentObject = ccnxContentObject_CreateWithNameAndPayload(name, payload); + * + * CCNxName *pointerToPayload = ccnxContentObject_GetPayload(contentObject); + * + * ... + * + * ccnxContentObject_Release(&contentObject); + * ccnxName_Release(&name); + * parcBuffer_Release(&payload); + * } + * @endcode + * + * @see `PARCBuffer` + */ +PARCBuffer *ccnxContentObject_GetPayload(const CCNxContentObject *contentObject); + +/** + * Return the type of payload for this `CCNxContentObject`. + * + * The enumeration must be one of the defined values in {@link CCNxPayloadType}. + * + * @param [in] contentObject A pointer to an instance of a `CCNxContentObject`. + * + * @return The `CCNxContentObject` instances payload type. + * + * Example: + * @code + * { + * CCNxName *name = ccnxName_CreateFromCString("lci:/foo/bar"); + * PARCBuffer *payload = parcBuffer_Allocate(<...>); + * + * CCNxContentObject *contentObject = ccnxContentObject_CreateWithNameAndPayload(name, payload); + * + * ... + * + * CCNxPayloadType payloadType = ccnxContentObject_GetPayloadType(contentObject); + * if (payloadType == CCNx_PAYLOAD_DATA) { + * printf("Payload type is CCNx_PAYLOAD_DATA (raw data)"); + * } + * + * ... + * + * ccnxContentObject_Release(&contentObject); + * ccnxName_Release(&name); + * parcBuffer_Release(&payload); + * } + * @endcode + */ +CCNxPayloadType ccnxContentObject_GetPayloadType(const CCNxContentObject *contentObject); + +/** + * Set the number of the final chunk necessary to represent the content + * in this ContentObject. + * + * The final chunk number is the 0-based count of the last chunk necessary + * to encapsulate the content in this ContentObject. For example, if it would + * take 10 chunks to encapsulate some content, the final chunk number would be 9. + * + * @param [in,out] contentObject A pointer to the `CCNxContentObject` to which to assign the final chunk number. + * @param [in] finalChunkNumber The number of the final chunk + * + * @return `true` If the final chunk number was succesfully set. + * @return `false` If the final chunk number could not be set. This might happen if the + * underlying transport format doesn't support final chunk numbers. + * Example: + * @code + * { + * uint64_t finalChunkNumber = 2803; + * ccnxContentObject_SetFinalChunkNumber(contentObject, finalChunkNumber); + * } + * @endcode + * + * @see ccnxContentObject_GetFinalChunkNumber + */ +bool ccnxContentObject_SetFinalChunkNumber(CCNxContentObject *contentObject, const uint64_t finalChunkNumber); + +/** + * Returns `true` if this `CCNxContentObject` has a final chunk number. + * + * @param [in] contentObject A pointer to the `CCNxContentObject` + * + * @return `true` If the object has a final chunk number set. + * @return `false` If the object has no final chunk number set. + * + * Example: + * @code + * { + * if (ccnxContentObject_HasFinalChunkNumber(contentObject)) { + * uint64_t finalChunkNumber = ccnxContentObject_GetFinalChunkNumber(contentObject); + * ... + * } + * } + * @endcode + * + * @see {@link ccnxContentObject_GetFinalChunkNumber} + */ +bool ccnxContentObject_HasFinalChunkNumber(const CCNxContentObject *contentObject); + +/** + * Return the final chunk number specified by this `CCNxContentObject`. + * + * @param [in] contentObject A pointer to the `CCNxContentObject` to get the final chunk number from. + * + * @return The final chunk number of the specified `CCNxContentObject`. + * + * Example: + * @code + * { + * uint64_t finalChunkNumber = ccnxContentObject_SetFinalChunkNumber(contentObject); + * } + * @endcode + * + * @see {@link ccnxContentObject_SetFinalChunkNumber} + */ +uint64_t ccnxContentObject_GetFinalChunkNumber(const CCNxContentObject *contentObject); + +/** + * Associate the supplied keyId, signature, and keyLocator with the specified `CCNxContentObject`. + * + * @param [in,out] contentObject A pointer to the `CCNxContentObject` to update. + * @param [in] keyId A pointer to the {@link PARCBuffer} containing the keyId to assign to the contentObject. + * @param [in] signature A pointer to a {@link PARCSignature} to assign to the contentObject. + * @param [in] keyLocator A pointer to a {@link CCNxKeyLocator} to assign to the contentObject. May be NULL. + * + * @return true if the signature payload was successfully set, false otherwise. + * + * Example: + * @code + * { + * CCNxName *name = ccnxName_CreateFromCString("lci:/hello/dolly"); + * PARCBuffer *payload = parcBuffer_WrapCString("hello"); + * + * CCNxContentObject *contentObject = ccnxContentObject_CreateWithNameAndPayload(name, payload); + * + * PARCBuffer *keyId = parcBuffer_WrapCString("keyhash"); + * PARCBuffer *sigbits = parcBuffer_CreateFromArray((void *) "siggybits", strlen("siggybits")); + * PARCSignature *signature = parcSignature_Create(PARCSigningAlgorithm_RSA, PARCCryptoHashType_SHA256, parcBuffer_Flip(sigbits)); + * + * ccnxContentObject_SetSignature(contentObject, keyId, signature, NULL); + * + * parcBuffer_Release(&payload); + * parcBuffer_Release(&sigbits); + * parcBuffer_Release(&keyId); + * parcSignature_Release(&signature); + * ccnxName_Release(&name); + * ccnxContentObject_Release(&contentObject); + * } + * @endcode + * + * @see `PARCSignature` + * @see `CCNxKeyLocator` + * @see `PARCBuffer` + */ +bool ccnxContentObject_SetSignature(CCNxContentObject *contentObject, const PARCBuffer *keyId, + const PARCSignature *signature, const CCNxKeyLocator *keyLocator); + + +/** + * Get the associated keyId from the specified `CCNxContentObject`. + * + * @param [in] contentObject A pointer to the `CCNxContentObject`. + * + * @return PARCBuffer A PARCBuffer containing the keyId or NULL if there is no keyId associated with the object. + * + * Example: + * @code + * { + * PARCBuffer *keyId = ccnxContentObject_GetKeyId(contentObject); + * if (keyId != NULL) { + * ... + * } + * } + * @endcode + * + * @see `ccnxContentObject_SetSignature` + */ +PARCBuffer *ccnxContentObject_GetKeyId(const CCNxContentObject *contentObject); + +/** + * Increase the number of references to a `CCNxContentObject`. + * + * Note that a new `CCNxContentObject` is not created, + * only that the given `CCNxContentObject` reference count is incremented. + * Discard the reference by invoking {@link ccnxContentObject_Release}. + * + * @param [in] contentObject A pointer to the original instance. + * @return The value of the input parameter @p instance. + * + * Example: + * @code + * { + * + * CCNxContentObject *reference = ccnxContentObject_Acquire(contentObject); + * + * ... + * + * ccnxContentObject_Release(&reference); + * + * } + * @endcode + * + * @see {@link ccnxContentObject_Release} + */ +CCNxContentObject *ccnxContentObject_Acquire(const CCNxContentObject *contentObject); + + +/** + * Release a previously acquired reference to the specified instance, + * decrementing the reference count for the instance. + * + * The pointer to the instance is set to NULL as a side-effect of this function. + * + * If the invocation causes the last reference to the instance to be released, + * the instance is deallocated and the instance's implementation will perform + * additional cleanup and release other privately held references. + * + * @param [in,out] contentObjectP A pointer to a pointer to the instance to release. + * + * Example: + * @code + * { + * CCNxContentObject *reference = ccnxContentObject_Acquire(contentObject); + * + * ... + * + * ccnxContentObject_Release(&reference); + * } + * @endcode + * + * @see {@link ccnxContentObject_Acquire} + */ +void ccnxContentObject_Release(CCNxContentObject **contentObjectP); + +#ifdef Libccnx_DISABLE_VALIDATION +# define ccnxContentObject_OptionalAssertValid(_instance_) +#else +# define ccnxContentObject_OptionalAssertValid(_instance_) ccnxContentObject_AssertValid(_instance_) +#endif + +/** + * Assert that an instance of `CCNxContentObject` is valid. + * + * If the instance is not valid, terminate via {@link trapIllegalValue} + * + * Valid means the internal state of the type is consistent with its + * required current or future behaviour. + * This may include the validation of internal instances of types. + * + * @param [in] contentObject A pointer to the instance to check. + * + * Example: + * @code + * { + * CCNxName *name = ccnxName_CreateFromCString("lci:/foo/bar"); + * PARCBuffer *payload = parcBuffer_Allocate(<...>); + * + * CCNxContentObject *contentObject = ccnxContentObject_CreateWithNameAndPayload(name, payload); + * + * ccnxContentObject_AssertValid(contentObject); + * + * ccnxContentObject_Release(&contentObject); + * ccnxName_Release(&name); + * parcBuffer_Release(&payload); + * } + * @endcode + */ +void ccnxContentObject_AssertValid(const CCNxContentObject *contentObject); + +/** + * Produce a null-terminated C-string representation of the specified instance. + * + * The non-null result must be freed by the caller via {@link parcMemory_Deallocate}. + * + * @param [in] instance A pointer to the instance. + * + * @return NULL Memory could not be allocated. + * @return non-NULL A null-terminated string that must be deallocated via `parcMemory_Deallocate`. + * + * Example: + * @code + * { + * ... + * char *stringRep = ccnxContentObject_ToString(contentObject); + * parcMemory_Deallocate(&stringRep); + * } + * @endcode + * @see `parcMemory_Deallocate` + */ +char *ccnxContentObject_ToString(const CCNxContentObject *contentObject); + +/** + * Determine if two `CCNxContentObject` instances are equal. + * + * The following equivalence relations on non-null `CCNxContentObject` instances are maintained: + * + * * It is reflexive: for any non-null reference value x, `ccnxContentObject_Equals(x, x)` + * must return true. + * + * * It is symmetric: for any non-null reference values x and y, + * `ccnxContentObject_Equals(x, y)` must return true if and only if + * `ccnxContentObject_Equals(y, x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `ccnxContentObject_Equals(x, y)` returns true and + * `ccnxContentObject_Equals(y, z)` returns true, + * then `ccnxContentObject_Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple + * invocations of `ccnxContentObject_Equals(x, y)` consistently return true or + * consistently return false. + * + * * For any non-null reference value x, `ccnxContentObject_Equals(x, NULL)` must + * return false. + * + * @param objectA A pointer to a `CCNxContentObject` instance. + * @param objectB A pointer to a `CCNxContentObject` instance. + * @return true if the two `CCNxContentObject` instances are equal. + * + * Example: + * @code + * @endcode + */ +bool ccnxContentObject_Equals(const CCNxContentObject *objectA, const CCNxContentObject *objectB); + +/** + * Get the ExpiryTime of the specified `ContentObject` instance. + * + * The ExpiryTime is the time at which the Payload expires, as expressed by a timestamp containing the number of + * milliseconds since the epoch in UTC. A cache or end system should not respond with a Content Object past its + * ExpiryTime. Routers forwarding a Content Object do not need to check the ExpiryTime. If the ExpiryTime field + * is missing, the Content Object has no expressed expiration and a cache or end system may use the Content + * Object for as long as it desires. + * + * Note: Calling this method on a `CCNxContentObject` that has no expiry time will cause a trap. Before calling, + * check that an ExpiryTime exists by calling {@link ccnxContentObject_HasExpiryTime} first. + * + * @param [in] instance A pointer to a `CCNxContentObject` instance. + * + * @return the ExpiryTime of the specified ContentObject. + * + * Example: + * @code + * { + * CCNxContentObject *contentObject = ccnxContentObject_Create<...>; + * + * if (ccnxContentObject_HasExpiryTime(contentObject) { + * uint64_t expiryTime = ccnxContentObject_GetExpiryTime(contentObject); + * } else { + * ccnxContentObject_SetExpiryTime(contentObject, 1478188800000ULL); + * } + * } + * @endcode + * @see `ccnxContentObject_HasExpiryTime` + * @see `ccnxContentObject_SetExpiryTime` + */ +uint64_t ccnxContentObject_GetExpiryTime(const CCNxContentObject *contentObject); + +/** + * Set the ExpiryTime of the specified `ContentObject` instance. + * + * See {@link ccnxContentObject_GetExpiryTime} for a description of ExpiryTime. + * + * @param [in] instance A pointer to the `CCNxContentObject` instance to be updated. + * @param [in] expiryTime The ExpiryTime to set. + * + * @return true, if the ExpiryTime was succesfully set. + * + * Example: + * @code + * { + * CCNxContentObject *contentObject = ccnxContentObject_Create<...>; + * + * if (ccnxContentObject_HasExpiryTime(contentObject) { + * uint64_t expiryTime = ccnxContentObject_GetExpiryTime(contentObject); + * } else { + * ccnxContentObject_SetExpiryTime(contentObject, 1478188800000ULL); + * } + * } + * @endcode + * @see `ccnxContentObject_GetExpiryTime` + * @see `ccnxContentObject_HasExpiryTime` + */ +bool ccnxContentObject_SetExpiryTime(CCNxContentObject *contentObject, const uint64_t expiryTime); + +/** + * Test whether the specified `ContentObject` instance has an ExpiryTime set. + * + * See {@link ccnxContentObject_GetExpiryTime} for a description of ExpiryTime. + * + * @param [in] instance A pointer to the `CCNxContentObject` instance to be updated. + * + * @return true, if the specified `CCNxContentObject` has an ExpiryTime. + * + * Example: + * @code + * { + * CCNxContentObject *contentObject = ccnxContentObject_Create<...>; + * + * if (ccnxContentObject_HasExpiryTime(contentObject) { + * uint64_t expiryTime = ccnxContentObject_GetExpiryTime(contentObject); + * } else { + * ccnxContentObject_SetExpiryTime(contentObject, 1478188800000ULL); + * } + * } + * @endcode + * @see `ccnxContentObject_GetExpiryTime` + * @see `ccnxContentObject_SetExpiryTime` + */ +bool ccnxContentObject_HasExpiryTime(const CCNxContentObject *contentObject); + +uint64_t ccnxContentObject_GetPathLabel(const CCNxContentObject *contentObject); +bool ccnxContentObject_SetPathLabel(CCNxContentObject *contentObject, const uint64_t pathLabel); +bool ccnxContentObject_HasPathLabel(const CCNxContentObject *contentObject); + +/** + * Set a payload on the specified `CCnxContentObject` instance. + * + * Currently, a payload may only be set once on a `CCnxContentObject` once, and may not be replaced. + * + * @param [in] contentObject A pointer to the `CCNxContentObject` instance to be updated. + * @param [in] payloadType The type of payload. See {@link CCNxPayloadType} for available types. + * @param [in] payload A pointer to the {@link PARCBuffer} to assign as the payload.. + * + * @return true, if the payload was successfully added to the specified `CCNxContentObject` + * + * Example: + * @code + * { + * CCNxName *name = ccnxName_CreateFromCString("lci:/foo/bar"); + * PARCBuffer *payload = parcBuffer_Allocate(<...>); + * + * CCNxContentObject *contentObject = ccnxContentObject_CreateWithNameAndPayload(name, NULL); + * + * CCNxName *pointerToPayload = ccnxContentObject_GetPayload(contentObject); + * if (pointerToPayload == NULL) { + * ccnxContentObject_SetPayload(contentObject, CCNxPayloadType_DATA, payload); + * } + * + * ... + * + * ccnxContentObject_Release(&contentObject); + * ccnxName_Release(&name); + * parcBuffer_Release(&payload); + * } + * @endcode + * @see `CCNxPayloadType` + * @see `PARCBuffer` + * @see `ccnxContentObject_GetPayload` + */ +bool ccnxContentObject_SetPayload(CCNxContentObject *contentObject, CCNxPayloadType payloadType, const PARCBuffer *payload); + +/** + * Get the {@link CCNxPayloadType} of the specified `CCnxContentObject` instance. + * + * @param [in] instance A pointer to the `CCNxContentObject` instance to be updated. + * + * @return true, if the payload was successfully added to the specified `CCNxContentObject` + * + * Example: + * @code + * { + * CCNxName *name = ccnxName_CreateFromCString("lci:/foo/bar"); + * PARCBuffer *payload = parcBuffer_Allocate(<...>); + * + * CCNxContentObject *contentObject = ccnxContentObject_CreateWithNameAndPayload(name, NULL); + * + * CCNxName *pointerToPayload = ccnxContentObject_GetPayload(contentObject); + * if (pointerToPayload == NULL) { + * ccnxContentObject_SetPayload(contentObject, CCNxPayloadType_DATA, payload); + * } + * + * ... + * + * ccnxContentObject_Release(&contentObject); + * ccnxName_Release(&name); + * parcBuffer_Release(&payload); + * } + * @endcode + * @see `CCNxPayloadType` + * @see `PARCBuffer` + * @see `ccnxContentObject_GetPayload` + */ +CCNxPayloadType ccnxContentObject_GetPayloadType(const CCNxContentObject *contentObject); +#endif // libccnx_ccnx_ContentObject_h diff --git a/libccnx-common/ccnx/common/ccnx_Interest.c b/libccnx-common/ccnx/common/ccnx_Interest.c new file mode 100755 index 00000000..481be0bc --- /dev/null +++ b/libccnx-common/ccnx/common/ccnx_Interest.c @@ -0,0 +1,399 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ +#include <config.h> + +#include <LongBow/runtime.h> + +#include <ccnx/common/ccnx_Interest.h> + +#include <ccnx/common/internal/ccnx_InterestDefault.h> +#include <ccnx/common/ccnx_InterestPayloadId.h> +#include <ccnx/common/ccnx_WireFormatMessage.h> + +#include <parc/algol/parc_Memory.h> + +#include <parc/algol/parc_DisplayIndented.h> + +#include <stdio.h> + +static const CCNxInterestInterface *_defaultImplementation = &CCNxInterestFacadeV1_Implementation; + +CCNxInterest * +ccnxInterest_Create(const CCNxName *name, + uint32_t lifetime, + const PARCBuffer *keyId, + const PARCBuffer *contentObjectHash) +{ + return ccnxInterest_CreateWithImpl(_defaultImplementation, + name, + lifetime, + keyId, + contentObjectHash, + CCNxInterestDefault_HopLimit); +} + +CCNxInterest * +ccnxInterest_CreateWithImpl(const CCNxInterestInterface *impl, + const CCNxName *name, + const uint32_t interestLifetime, + const PARCBuffer *keyId, + const PARCBuffer *contentObjectHash, + const uint32_t hopLimit) +{ + CCNxInterest *result = NULL; + + if (impl->create != NULL) { + result = impl->create(name, interestLifetime, keyId, contentObjectHash, hopLimit); + + // And set the dictionary's interface pointer to the one we just used to create this. + ccnxTlvDictionary_SetMessageInterface(result, impl); + } else { + trapNotImplemented("Interest implementations must implement create()"); + } + return result; +} + + +CCNxInterest * +ccnxInterest_CreateSimple(const CCNxName *name) +{ + return ccnxInterest_Create(name, + CCNxInterestDefault_LifetimeMilliseconds, + NULL, + NULL); +} + +void +ccnxInterest_AssertValid(const CCNxInterest *interest) +{ + assertNotNull(interest, "Must be a non-null pointer to a CCNxInterest."); + + // Check for required fields in the underlying dictionary. Case 1036 + CCNxInterestInterface *impl = ccnxInterestInterface_GetInterface(interest); + assertNotNull(impl, "Interest must have an valid implementation pointer."); + + if (impl->assertValid != NULL) { + impl->assertValid(interest); + } +} + +CCNxInterest * +ccnxInterest_Acquire(const CCNxInterest *instance) +{ + return ccnxTlvDictionary_Acquire(instance); +} + +void +ccnxInterest_Release(CCNxInterest **instanceP) +{ + ccnxTlvDictionary_Release(instanceP); +} + +bool +ccnxInterest_Equals(const CCNxInterest *a, const CCNxInterest *b) +{ + if (a == b) { + return true; + } + if (a == NULL || b == NULL) { + return false; + } + + CCNxInterestInterface *implA = ccnxInterestInterface_GetInterface(a); + CCNxInterestInterface *implB = ccnxInterestInterface_GetInterface(b); + + if (implA != implB) { + return false; + } + + CCNxName *nameA = implA->getName(a); + CCNxName *nameB = implB->getName(b); + + PARCBuffer *keyA = implA->getKeyIdRestriction(a); + PARCBuffer *keyB = implB->getKeyIdRestriction(b); + + uint64_t lifetimeA = implA->getLifetime(a); + uint64_t lifetimeB = implB->getLifetime(b); + + if (ccnxName_Equals(nameA, nameB)) { + if (parcBuffer_Equals(keyA, keyB)) { + // Must compare the excludes. + if (lifetimeA == lifetimeB) { + return true; + } + } + } + + return false; +} + +CCNxName * +ccnxInterest_GetName(const CCNxInterest *interest) +{ + ccnxInterest_OptionalAssertValid(interest); + CCNxInterestInterface *impl = ccnxInterestInterface_GetInterface(interest); + + CCNxName *result = NULL; + + if (impl->getName != NULL) { + result = impl->getName(interest); + } else { + trapNotImplemented("ccnxInterest_GetName"); + } + return result; +} + +bool +ccnxInterest_SetContentObjectHashRestriction(CCNxInterest *interest, const PARCBuffer *contentObjectHash) +{ + ccnxInterest_OptionalAssertValid(interest); + CCNxInterestInterface *impl = ccnxInterestInterface_GetInterface(interest); + + bool result = false; + + if (impl->setContentObjectHashRestriction != NULL) { + result = impl->setContentObjectHashRestriction(interest, contentObjectHash); + } else { + trapNotImplemented("ccnxInterest_SetContentObjectHashRestriction"); + } + return result; +} + +PARCBuffer * +ccnxInterest_GetContentObjectHashRestriction(const CCNxInterest *interest) +{ + ccnxInterest_OptionalAssertValid(interest); + CCNxInterestInterface *impl = ccnxInterestInterface_GetInterface(interest); + + PARCBuffer *result = NULL; + + if (impl->getContentObjectHashRestriction != NULL) { + result = impl->getContentObjectHashRestriction(interest); + } else { + trapNotImplemented("ccnxInterest_GetContentObjectHash"); + } + return result; +} + +bool +ccnxInterest_SetKeyIdRestriction(CCNxInterest *interest, const PARCBuffer *keyId) +{ + ccnxInterest_OptionalAssertValid(interest); + CCNxInterestInterface *impl = ccnxInterestInterface_GetInterface(interest); + + bool result = false; + + if (impl->setKeyIdRestriction != NULL) { + result = impl->setKeyIdRestriction(interest, keyId); + } else { + trapNotImplemented("ccnxInterest_SetKeyIdRestriction"); + } + return result; +} + +PARCBuffer * +ccnxInterest_GetKeyIdRestriction(const CCNxInterest *interest) +{ + ccnxInterest_OptionalAssertValid(interest); + CCNxInterestInterface *impl = ccnxInterestInterface_GetInterface(interest); + + PARCBuffer *result = NULL; + + if (impl->getKeyIdRestriction != NULL) { + result = impl->getKeyIdRestriction(interest); + } else { + trapNotImplemented("ccnxInterest_GetKeyIdRestriction"); + } + return result; +} + +bool +ccnxInterest_SetLifetime(CCNxInterest *interest, uint32_t lifetime) +{ + ccnxInterest_OptionalAssertValid(interest); + CCNxInterestInterface *impl = ccnxInterestInterface_GetInterface(interest); + + bool result = false; + + if (impl->setLifetime != NULL) { + result = impl->setLifetime(interest, lifetime); + } else { + trapNotImplemented("ccnxInterest_SetLifetime"); + } + return result; +} + +uint32_t +ccnxInterest_GetLifetime(const CCNxInterest *interest) +{ + ccnxInterest_OptionalAssertValid(interest); + CCNxInterestInterface *impl = ccnxInterestInterface_GetInterface(interest); + + uint32_t result = 0; + + if (impl->getLifetime != NULL) { + result = impl->getLifetime(interest); + } else { + trapNotImplemented("ccnxInterest_GetLifetime"); + } + return result; +} + +bool +ccnxInterest_SetPayload(CCNxInterest *interest, const PARCBuffer *payload) +{ + ccnxInterest_OptionalAssertValid(interest); + CCNxInterestInterface *impl = ccnxInterestInterface_GetInterface(interest); + + bool result = false; + + if (impl->setPayload != NULL) { + result = impl->setPayload(interest, payload); + } else { + trapNotImplemented("ccnxInterest_SetPayload"); + } + return result; +} + +bool +ccnxInterest_SetPayloadAndId(CCNxInterest *interest, const PARCBuffer *payload) +{ + ccnxInterest_OptionalAssertValid(interest); + CCNxInterestInterface *impl = ccnxInterestInterface_GetInterface(interest); + + bool result = false; + + if (impl->setPayloadAndId != NULL) { + result = impl->setPayloadAndId(interest, payload); + } else { + trapNotImplemented("ccnxInterest_SetPayloadAndId"); + } + return result; +} + +bool +ccnxInterest_SetPayloadWithId(CCNxInterest *interest, const PARCBuffer *payload, const CCNxInterestPayloadId *payloadId) +{ + ccnxInterest_OptionalAssertValid(interest); + CCNxInterestInterface *impl = ccnxInterestInterface_GetInterface(interest); + + bool result = false; + + if (impl->setPayloadWithId != NULL) { + result = impl->setPayloadWithId(interest, payload, payloadId); + } else { + trapNotImplemented("ccnxInterest_SetPayloadWithId"); + } + return result; +} + +PARCBuffer * +ccnxInterest_GetPayload(const CCNxInterest *interest) +{ + ccnxInterest_OptionalAssertValid(interest); + CCNxInterestInterface *impl = ccnxInterestInterface_GetInterface(interest); + + PARCBuffer *result = NULL; + + if (impl->getPayload != NULL) { + result = impl->getPayload(interest); + } else { + trapNotImplemented("ccnxInterest_GetPayload"); + } + return result; +} + +bool +ccnxInterest_SetHopLimit(CCNxTlvDictionary *interest, uint32_t hopLimit) +{ + ccnxInterest_OptionalAssertValid(interest); + CCNxInterestInterface *impl = ccnxInterestInterface_GetInterface(interest); + + bool result = false; + + if (impl->setHopLimit != NULL) { + result = impl->setHopLimit(interest, hopLimit); + } else { + trapNotImplemented("ccnxInterest_GetSetHopLimit"); + } + // Make sure any attached wire format buffers are in sync with the dictionary + ccnxWireFormatMessage_SetHopLimit(interest, hopLimit); + return result; +} + +uint32_t +ccnxInterest_GetHopLimit(const CCNxTlvDictionary *interest) +{ + ccnxInterest_OptionalAssertValid(interest); + CCNxInterestInterface *impl = ccnxInterestInterface_GetInterface(interest); + + uint32_t result = 0; + + if (impl->getHopLimit != NULL) { + result = impl->getHopLimit(interest); + } else { + trapNotImplemented("ccnxInterest_GetHopLimit"); + } + return result; +} + +void +ccnxInterest_Display(const CCNxInterest *interest, int indentation) +{ + ccnxInterest_OptionalAssertValid(interest); + + parcDisplayIndented_PrintLine(indentation, "CCNxInterest@%p {\n", interest); + ccnxName_Display(ccnxInterest_GetName(interest), indentation + 1); + + CCNxInterestInterface *impl = ccnxInterestInterface_GetInterface(interest); + + if (impl->display != NULL) { + impl->display(interest, 1); + } + + parcDisplayIndented_PrintLine(indentation, "}\n"); +} + +char * +ccnxInterest_ToString(const CCNxInterest *interest) +{ + char *result = NULL; + + ccnxInterest_OptionalAssertValid(interest); + + CCNxInterestInterface *impl = ccnxInterestInterface_GetInterface(interest); + + if (impl->toString != NULL) { + result = impl->toString(interest); + } else { + char *name = ccnxName_ToString(ccnxInterest_GetName(interest)); + uint32_t lifetime = ccnxInterest_GetLifetime(interest); + + char *string; + int failure = asprintf(&string, "CCNxInterest{.name=\"%s\" .lifetime=%dms}", name, lifetime); + assertTrue(failure > 0, "Error from asprintf"); + + parcMemory_Deallocate((void **) &name); + + result = parcMemory_StringDuplicate(string, strlen(string)); + + free(string); + } + + return result; +} diff --git a/libccnx-common/ccnx/common/ccnx_Interest.h b/libccnx-common/ccnx/common/ccnx_Interest.h new file mode 100755 index 00000000..20dd5c53 --- /dev/null +++ b/libccnx-common/ccnx/common/ccnx_Interest.h @@ -0,0 +1,807 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file ccnx_Interest.h + * @ingroup Interest + * @brief A CCNx Interest expresses an interest in a piece of named data. + * + * The canonical CCN Interest. An Interest contains a {@link CCNxName}, + * the desired payload, and two optional restrictions (ContentObjectHash and + * KeyId) to limit responses + * to a specific publisher or a specific Content Object. + * + * @see {@link CCNxContentObject} + * @see {@link CCNxName} + * + */ + +#ifndef libccnx_ccnx_Interest_h +#define libccnx_ccnx_Interest_h + +#include <ccnx/common/internal/ccnx_TlvDictionary.h> +#include <ccnx/common/internal/ccnx_InterestInterface.h> + +/** + * @typedef CCNxInterest + * @brief The CCNx Interest Message + */ + +typedef CCNxTlvDictionary CCNxInterest; + +/** + * Create a new instance of `CCNxInterest` for the specified name, with the specified lifetime and + * publisher's key digest, using the specified {@link CCNxInterestInterface}, using dynamically allocated memory. + * + * The name and key digest are used for matching against ContentObjects. If the specified key digest is NULL, + * this Interest will match against any ContentObject with the same name (regardless of whether the + * ContentObject has matching key digest). If the specified key digest is NOT NULL, then this Interest + * will match only against any ContentObject with both the same name and the same key digest. The key digest + * comparison test is a simple buffer comparison. + * + * The lifetime, specified in milliseconds, is a hint to the system how long the application is + * willing to wait for a response before it will re-send the same Interest. It is used by forwarders as a + * guideline on how long they should attempt to keep the Interest alive in their tables. It is not a guarantee + * that they will do so, however. + * + * The created instance of `CCNxInterest` must be released by calling {@link ccnxInterest_Release}(). + * + * @param [in] implementation A pointer to the {@link CCNxInterestInterface} to be used to build this Interest. + * @param [in] name A pointer to a {@link CCNxName} expressing the name of the content you are interested in. + * @param [in] lifetime A `uint32_t` specifying the number of milliseconds the application + * will wait before re-sending the Interest. + * @param [in] keyId A pointer to a {@link PARCBuffer} containing a key digest that should be matched + * against ContentObjects whose names match our specified name. This value may be NULL and, if so, will + * not be used in the matching process. + * @param [in] contentObjectHash A pointer to a `PARCBuffer` containing the hash of the ContentObject that is expected + * to be returned in response to this Interest. + * + * @return A new instance of a `CCNxInterest`. + * + * Example: + * @code + * { + * CCNxName *name = ccnxName_CreateFromCString("lci:/parc/csl/sensors/temp/2"); + * + * PARCBuffer *keyId = parcBuffer_Allocate(8); + * parcBuffer_PutUint64(keyId, 1234L); + * + * uint32_t lifetime = CCNxInterestDefaultLifetimeMilliseconds; + * + * CCNxInterest *interest = ccnxInterest_CreateWithImpl(&CCNxInterestFacadeV1_Implementation, + * name, lifetime, keyId, NULL); + * + * ... + * + * parcBuffer_Release(&keyId); + * ccnxName_Release(&name); + * ccnxInterest_Release(&interest); + * } + * @endcode + * + * @see {@link ccnxInterest_Release} + */ +CCNxInterest *ccnxInterest_CreateWithImpl(const CCNxInterestInterface *implementation, + const CCNxName *name, + const uint32_t interestLifetime, + const PARCBuffer *keyId, + const PARCBuffer *contentObjectHash, + const uint32_t hopLimit); + +/** + * Create a new instance of `CCNxInterest` for the specified name, with the specified lifetime and + * publisher's key digest, using dynamically allocated memory. + * + * The name and key digest are used for matching against ContentObjects. If the specified key digest is NULL, + * this Interest will match against any ContentObject with the same name (regardless of whether the + * ContentObject has matching key digest). If the specified key digest is NOT NULL, then this Interest + * will match only against any ContentObject with both the same name and the same key digest. The key digest + * comparison test is a simple buffer comparison. + * + * The lifetime, specified in milliseconds, is a hint to the system how long the application is + * willing to wait for a response before it will re-send the same Interest. It is used by forwarders as a + * guideline on how long they should attempt to keep the Interest alive in their tables. It is not a guarantee + * that they will do so, however. + * + * The created instance of `CCNxInterest` must be released by calling {@link ccnxInterest_Release}(). + * + * @param [in] name A pointer to a {@link CCNxName} expressing the name of the content you are interested in. + * @param [in] lifetime A `uint32_t` specifying the number of milliseconds the application + * will wait before re-sending the Interest. + * @param [in] keyId A pointer to a {@link PARCBuffer} containing a key digest that should be matched + * against ContentObjects whose names match our specified name. This value may be NULL and, if so, will + * not be used in the matching process. + * @param [in] contentObjectHash A pointer to a `PARCBuffer` containing the hash of the ContentObject that is expected + * to be returned in response to this Interest. + * + * @return A new instance of a `CCNxInterest`. + * + * Example: + * @code + * { + * CCNxName *name = ccnxName_CreateFromCString("lci:/parc/csl/sensors/temp/2"); + * + * PARCBuffer *keyId = parcBuffer_Allocate(8); + * parcBuffer_PutUint64(keyId, 1234L); + * + * uint32_t lifetime = CCNxInterestDefaultLifetimeMilliseconds; + * + * CCNxInterest *interest = ccnxInterest_Create(name, lifetime, keyId, NULL); + * + * ... + * + * parcBuffer_Release(&keyId); + * ccnxName_Release(&name); + * ccnxInterest_Release(&interest); + * } + * @endcode + * + * @see {@link ccnxInterest_Release} + */ +CCNxInterest *ccnxInterest_Create(const CCNxName *name, + uint32_t lifetime, + const PARCBuffer *keyId, + const PARCBuffer *contentObjectHash); + + +/** + * Create a new instance of `CCNxInterest` for the specified name, using dynamically allocated memory. + * + * The created instance will specify a default lifetime, and will not specify a publisher's key + * digest for matching. Only the specified name will be used for matching. To specify values + * for the lifetime or publisher's key digest, see {@link ccnxInterest_Create()}. + * + * The created instance of `CCNxInterest` must be released by calling {@link ccnxInterest_Release}. + * + * @param [in] name A pointer to a `CCNxName` expressing the name of the content you are interested in. + * + * @return A new instance of a `CCNxInterest'. + * + * Example: + * @code + * { + * CCNxName *name = ccnxName_CreateFromCString("lci:/parc/csl/sensors/humidity/4"); + * + * CCNxInterest *interest = ccnxInterest_CreateSimple(name); + * + * ... + * + * ccnxName_Release(&name); + * ccnxInterest_Release(&interest); + * } + * @endcode + * + * @see {@link ccnxInterest_Create} + * @see {@link ccnxInterest_Release} + */ +CCNxInterest *ccnxInterest_CreateSimple(const CCNxName *name); + +/** + * Determine if two `CCNxInterest` instances are equal. + * + * The following equivalence relations on non-null `CCNxInterest` instances are maintained: + * + * * It is reflexive: for any non-null reference value x, `ccnxInterest_Equals(x, x)` + * must return true. + * + * * It is symmetric: for any non-null reference values x and y, + * `ccnxInterest_Equals(x, y)` must return true if and only if + * `ccnxInterest_Equals(y, x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `ccnxInterest_Equals(x, y)` returns true and + * `ccnxInterest_Equals(y, z)` returns true, + * then `ccnxInterest_Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple + * invocations of `ccnxInterest_Equals(x, y)` consistently return true or + * consistently return false. + * + * * For any non-null reference value x, `ccnxInterest_Equals(x, NULL)` must + * return false. + * + * @param interestA A pointer to a `CCNxInterest` instance. + * @param interestB A pointer to a `CCNxInterest` instance. + * @return true if the two `CCNxInterest` instances are equal. + * + * Example: + * @code + * { + * CCNxName *name = ccnxName_CreateFromCString("lci:/parc/csl/sensors/radiation/11"); + * uint32_t lifetime = 15 * 1000; // 15 seconds, in milliseconds + * + * PARCBuffer *keyDigest1 = parcBuffer_Allocate(8); + * parcBuffer_PutUint64(keyDigest1, 1234L); + * + * PARCBuffer *keyDigest2 = parcBuffer_Allocate(8); + * parcBuffer_PutUint64(keyDigest2, 6789L); // different buffer contents than keyDigest1 + * + * CCNxInterest *interestA = ccnxInterest_Create(name, lifetime, keyDigest1, NULL); + * CCNxInterest *interestB = ccnxInterest_Create(name, lifetime, keyDigest1, NULL); // same as A + * + * CCNxInterest *interestC = ccnxInterest_Create(name, lifetime, keyDigest2); // different key digest + * + * if (ccnxInterest_Equals(interestA, interestB)) { + * // this is expected... + * } + * + * if (ccnxInterest_Equals(interestA, interestC)) { + * // this is NOT expected + * } + * + * ... + * + * ccnxName_Release(&name); + * parcBuffer_Release(&keyDigest1); + * parcBuffer_Release(&keyDigest2); + * ccnxInterest_Release(&interestA); + * ccnxInterest_Release(&interestB); + * ccnxInterest_Release(&interestC); + * } + * @endcode + */ +bool ccnxInterest_Equals(const CCNxInterest *interestA, const CCNxInterest *interestB); + +/** + * Return a pointer to the {@link CCNxName} associated with the given `CCNxInterest`. + * + * The pointer points to memory managed by the `CCNxInterest` and does not have to + * be released unless {@link ccnxName_Acquire()} is called to acquire it. + * + * @param [in] interest A pointer to a `CCNxInterest` instance. + * + * @return A pointer to the CCNxName associated with the specified `CCNxInterest`. + * + * Example: + * @code + * { + * CCNxName *name = ccnxInterest_GetName(interest); + * } + * @endcode + * + * @see {@link CCNxName} + */ +CCNxName *ccnxInterest_GetName(const CCNxInterest *interest); + +/** + * Assign a lifetime value to the specified `CCNxInterest`. + * + * The lifetime, specified in milliseconds, is a hint to the system how long the application is + * willing to wait for a response before it will re-send the same Interest. It is used by forwarders as a + * guideline on how long they should attempt to keep the Interest alive in their tables. It is not a guarantee + * that they will do so, however. + * + * @param [in] interest A pointer to a `CCNxInterest` instance. + * @param [in] lifetime A `uint32_t` containing the desired lifetime of this Interest, in milliseconds. + * + * Example: + * @code + * { + * CCNxName *name = ccnxName_CreateFromCString("lci:/parc/csl/media/Ab00se"); + * CCNxInterest *interest = ccnxInterest_CreateSimple(name); + * + * uint32_t lifetime = 15 * 1000; // 15 seconds, in milliseconds + * ccnxInterest_SetLifetime(interest, lifetime); + * + * ... + * + * ccnxName_Release(&name); + * ccnxInterest_Release(&interest); + * } + * @endcode + * + * @see {@link ccnxInterest_GetLifetime} + */ +bool ccnxInterest_SetLifetime(CCNxInterest *interest, uint32_t lifetime); + +/** + * Retrieve the specified `CCNxInterest`'s lifetime value as a uint32_t. + * @param [in] interest A pointer to a `CCNxInterest` instance. + * + * @return The lifetime, in milliseconds, specified for this `CCNxInterest`. + * + * Example: + * @code + * { + * CCNxName *name = ccnxName_CreateFromCString("lci:/parc/csl/media/b00se"); + * CCNxInterest *interest = ccnxInterest_CreateSimple(name); + * + * uint32_t lifetime = ccnxInterest_GetLifetime(interest); + * ... + * + * ccnxName_Release(&name); + * ccnxInterest_Release(&interest); + * } + * @endcode + * + * @see {@link ccnxInterest_SetLifetime} + */ +uint32_t ccnxInterest_GetLifetime(const CCNxInterest *interest); + +/** + * Assign a key ID to the specified `CCNxInterest`. + * + * The key ID is used when matching this `CCNxInterest` to CCNx content. If a non-NULL + * key ID is specified in the `CCNxInterest`, then only content with a matching key + * digest will be matched. If the `CCNxInterest`'s key ID is NULL, then it is not used + * when matching against content. + * + * @param [in,out] interest A pointer to a `CCNxInterest` instance. + * @param [in] keyId A pointer to a {@link PARCBuffer} containing the desired key ID, or NULL. + * + * Example: + * @code + * { + * CCNxName *name = ccnxName_CreateFromCString("lci:/parc/csl/media/r00"); + * + * PARCBuffer *keyId = parcBuffer_Allocate(8); + * parcBuffer_PutUint64(keyId, 1234L); + * + * CCNxInterest *interest = ccnxInterest_CreateSimple(name); + * + * ccnxInterest_SetKeyIdRestriction(interest, keyId); + * + * ... + * + * ccnxName_Release(&name); + * ccnxInterest_Release(&interest); + * parcBuffer_Release(&keyId); + * } + * @endcode + * + * @see ccnxInterest_GetKeyId + */ +bool ccnxInterest_SetKeyIdRestriction(CCNxInterest *interest, const PARCBuffer *keyId); + +/** + * Return a pointer to the {@link PARCBuffer} containing the publisher key digest associated with the given `CCNxInterest`. + * + * The pointer points to memory managed by the `CCNxInterest` and does not have to + * be released unless {@link parcBuffer_Acquire()} is called to acquire it. + * + * @param [in] interest A pointer to a `CCNxInterest` instance. + * + * @return A pointer to a PARCBuffer containing the `CCNxInterest`'s key ID. + * + * Example: + * @code + * { + * CCNxName *name = ccnxName_CreateFromCString("lci:/parc/csl/media/p1e"); + * + * PARCBuffer *keyId = parcBuffer_Allocate(8); + * parcBuffer_PutUint64(keyId, 1234L); + * uint32_t lifetime = 5000; + * + * CCNxInterest *interest = ccnxInterest_Create(name, lifetime, keyId, NULL); + * + * PARCBuffer *keyIdP = ccnxInterest_GetKeyIdRestriction(interest); + * + * ... + * + * ccnxName_Release(&name); + * parcBuffer_Release(&keyId); + * ccnxInterest_Release(&interest); + * } + * } + * @endcode + * + * @see {@link ccnxInterest_SetKeyId} + */ +PARCBuffer *ccnxInterest_GetKeyIdRestriction(const CCNxInterest *interest); + +/** + * Assign a `ContentObjectHash` to the specified `CCNxInterest`. + * + * The `ContentObjectHash` is used when performing an exact match against this `CCNxInterest` and + * the response CCNx content. If a non-NULL `ContentObjectHash` is specified in the `CCNxInterest`, + * then only content whose hash digest matches the specified value will match and be returned. If + * the `ContentObjectHash` field is NULL, this binary equality check is not used when checking a response + * Content Object. + * + * @param [in,out] interest A pointer to a `CCNxInterest` instance. + * @param [in] contentObjectHash A pointer to a {@link PARCBuffer} containing the desired ContentObjectHash, or NULL. + * + * Example: + * @code + * { + * CCNxName *name = ccnxName_CreateFromCString("lci:/parc/csl/media/r00"); + * + * PARCBuffer *contentObjectHash = parcBuffer_Allocate(8); + * parcBuffer_PutUint64(contentObjectHashcontentObjectHash, 1234L); + * + * CCNxInterest *interest = ccnxInterest_CreateSimple(name); + * + * ccnxInterest_SetContentObjectHashRestriction(interest, contentObjectHash); + * + * ... + * + * ccnxName_Release(&name); + * ccnxInterest_Release(&interest); + * parcBuffer_Release(&contentObjectHash); + * } + * @endcode + * + * @see {@link ccnxInterest_GetContentObjectHash} + */ +bool ccnxInterest_SetContentObjectHashRestriction(CCNxInterest *interest, const PARCBuffer *contentObjectHash); + +/** + * Return a pointer to the {@link PARCBuffer} containing the `ContentObjectHash` associated with the given `CCNxInterest`. + * + * The pointer points to memory managed by the `CCNxInterest` and does not have to + * be released unless {@link parcBuffer_Acquire()} is called to acquire it. + * + * @param [in] interest A pointer to a `CCNxInterest` instance. + * + * @return A pointer to a `PARCBuffer` containing the `CCNxInterest`'s `ContentObjectHash`. + * + * Example: + * @code + * { + * CCNxName *name = ccnxName_CreateFromCString("lci:/parc/csl/media/p1e"); + * + * uint32_t lifetime = 3 * 1000; // milliseconds + * + * PARCBuffer *contentObjectHash = parcBuffer_Allocate(8); + * parcBuffer_PutUint64(contentObjectHash, 1234L); + * + * + * CCNxInterest *interest = ccnxInterest_Create(name, lifetime, NULL, contentObjectHash); + * + * PARCBuffer *contentObjectHashP = ccnxInterest_GetContentObjectHashRestriction(interest); + * + * ... + * + * ccnxName_Release(&name); + * parcBuffer_Release(&contentObjectHash); + * ccnxInterest_Release(&interest); + * } + * } + * @endcode + * + * @see {@link ccnxInterest_SetContentObjectHash} + */ +PARCBuffer *ccnxInterest_GetContentObjectHashRestriction(const CCNxInterest *interest); + +/** + * Produce a null-terminated string representation of the specified instance. + * + * The result must be freed by the caller via {@link parcMemory_Deallocate()}. + * + * @param [in] interest A pointer to the instance. + * + * @return NULL Cannot allocate memory. + * @return non-NULL A pointer to an allocated, nul-terminated C string that must be deallocated via `parcMemory_Deallocate()`. + * + * Example: + * @code + * { + * CCNxName *name = ccnxName_CreateFromCString("lci:/parc/csl/sensors/radiation/11"); + * CCNxInterest *interest = ccnxInterest_CreateSimple(name); + * + * char *string = ccnxInterest_ToString(interest); + * + * if (string != NULL) { + * printf("Interest looks like: %s\n", string); + * parcMemory_Deallocate(string); + * } else { + * printf("Cannot allocate memory\n"); + * } + * + * ccnxName_Release(&name); + * ccnxInterest_Release(&instance); + * } + * @endcode + * + * @see ccnxInterest_Display + * @see parcMemory_Deallocate + */ +char *ccnxInterest_ToString(const CCNxInterest *interest); + +/** + * Print a human readable representation of the given `CCNxInterest`. + * + * @param [in] interest A pointer to the instance to display. + * @param [in] indentation The level of indentation to use to pretty-print the output. + * + * Example: + * @code + * { + * CCNxName *name = ccnxName_CreateFromCString("lci:/parc/csl/sensors/radiation/11"); + * CCNxInterest *interest = ccnxInterest_CreateSimple(name); + * + * ccnxInterest_Display(interest, 0); + * + * ccnxName_Release(&name); + * ccnxInterest_Release(&instance); + * } + * @endcode + * */ +void ccnxInterest_Display(const CCNxInterest *interest, int indentation); + +/** + * Increase the number of references to a `CCNxInterest`. + * + * Note that a new `CCNxInterest` is not created, + * only that the given `CCNxInterest` reference count is incremented. + * Discard the reference by invoking {@link ccnxInterest_Release}. + * + * @param [in] instance A pointer to the original instance. + * @return The value of the input parameter @p instance. + * + * Example: + * @code + * { + * CCNxInterest *reference = ccnxInterest_Acquire(interest); + * + * ... + * + * ccnxInterest_Release(&reference); + * + * } + * @endcode + * + * @see ccnxInterest_Release + */ +CCNxInterest *ccnxInterest_Acquire(const CCNxInterest *instance); + +/** + * Release a previously acquired reference to the specified instance, + * decrementing the reference count for the instance. + * + * The pointer to the instance is set to NULL as a side-effect of this function. + * + * If the invocation causes the last reference to the instance to be released, + * the instance is deallocated and the instance's implementation will perform + * additional cleanup and release other privately held references. + * + * @param [in,out] instanceP A pointer to a pointer to the instance to release. + * + * Example: + * @code + * { + * CCNxInterest *reference = ccnxInterest_Acquire(contentObject); + * + * ... + * + * ccnxInterest_Release(&reference); + * } + * @endcode + * + * @see {@link ccnxInterest_Acquire} + */ +void ccnxInterest_Release(CCNxInterest **instanceP); + +#ifdef Libccnx_DISABLE_VALIDATION +# define ccnxInterest_OptionalAssertValid(_instance_) +#else +# define ccnxInterest_OptionalAssertValid(_instance_) ccnxInterest_AssertValid(_instance_) +#endif +/** + * Assert that an instance of `CCNxInterest` is valid. + * + * If the instance is not valid, terminate via {@link trapIllegalValue} + * + * Valid means the internal state of the type is consistent with its + * required current or future behaviour. + * This may include the validation of internal instances of types. + * + * @param [in] interest A pointer to the instance to check. + * + * Example: + * @code + * { + * CCNxName *name = ccnxName_CreateFromCString("lci:/parc/csl/sensors/radiation/11"); + * CCNxInterest *interest = ccnxInterest_CreateSimple(name); + * + * ccnxInterest_AssertValid(interest); + * + * ccnxName_Release(&name); + * ccnxInterest_Release(&instance); + * } + * @endcode + */ +void ccnxInterest_AssertValid(const CCNxInterest *interest); + + +/** + * Set the payload on an `CCnxInterest` instance. A reference to the supplied payload is + * acquired by the Interest, so the caller may release the payload after setting it on + * the CCnxInterest. The payload type must be specified, and must be one of the types + * defined in {@link CCNxPayloadType}. + * + * This will not append a payloadId segment to the Interest's name. If you want to do that, + * see {@link ccnxInterest_SetPayloadAndId} and {@link ccnxInterest_SetPayloadWithId}. + * + * @param [in] interest A pointer to the `CCNxInstance` instance to update. + * @param [in] payload A pointer to the PARCBuffer payload to be applied to the `CCNxInterest`. + * + * Example: + * @code + * { + * CCNxName *name = ccnxName_CreateFromCString("lci:/parc/csl/sensors/radiation/11"); + * CCNxInterest *interest = ccnxInterest_CreateSimple(name); + * + * PARCBuffer *payload = parcBuffer_Allocate(8); + * parcBuffer_PutUint64(payload, 5432L); + * + * ccnxInterest_SetPayload(interest, payload); + * + * parcBuffer_Release(&payload); + * ccnxName_Release(&name); + * ccnxInterest_Release(&instance); + * } + * @endcode + * @see `CCNxPayloadType` + */ +bool ccnxInterest_SetPayload(CCNxInterest *interest, const PARCBuffer *payload); + +/** + * Set the payload on an `CCnxInterest` instance. A reference to the supplied payload is + * acquired by the Interest, so the caller may release the payload after setting it on + * the CCnxInterest. The payload type must be specified, and must be one of the types + * defined in {@link CCNxPayloadType}. + * + * This will also generate and append a payloadId name segment to the interest name using a + * sha256 hash (the default) of the payload buffer. If you'd like to take responsibility + * for generating a payloadId, use {@link ccnxInterest_SetPayloadAndId} instead. + * + * @param [in] interest A pointer to the `CCNxInstance` instance to update. + * @param [in] payload A pointer to the PARCBuffer payload to be applied to the `CCNxInterest`. + * + * Example: + * @code + * { + * CCNxName *name = ccnxName_CreateFromCString("lci:/parc/csl/sensors/radiation/11"); + * CCNxInterest *interest = ccnxInterest_CreateSimple(name); + * + * PARCBuffer *payload = parcBuffer_Allocate(8); + * parcBuffer_PutUint64(payload, 5432L); + * + * ccnxInterest_SetPayload(interest, payload); + * + * parcBuffer_Release(&payload); + * ccnxName_Release(&name); + * ccnxInterest_Release(&instance); + * } + * @endcode + * @see `CCNxPayloadType` + */ +bool ccnxInterest_SetPayloadAndId(CCNxInterest *interest, const PARCBuffer *payload); + + +/** + * Set the payload and specified payloadId on an `CCnxInterest` instance. A reference to the + * supplied payload is acquired by the Interest, so the caller may release the payload after + * setting it on the CCnxInterest. The payload type must be specified, and must be one of the types + * defined in {@link CCNxPayloadType}. + * + * This will append the supplied payloadId to the interest name, supplying a NULL payloadId will result + * in not payloadId being appended to the interest name. See {@link CCNxInterestPayloadId} + * + * @param [in] interest A pointer to the `CCNxInstance` instance to update. + * @param [in] payload A pointer to the PARCBuffer payload to be applied to the `CCNxInterest`. + * @param [in] payloadId A pointer to a CCNxInterestPayloadId to use instead of the default. The value may + * be NULL of no payload id is desired. + * + * Example: + * @code + * { + * CCNxName *name = ccnxName_CreateFromCString("lci:/parc/csl/sensors/radiation/11"); + * CCNxInterest *interest = ccnxInterest_CreateSimple(name); + * + * PARCBuffer *payload = parcBuffer_Allocate(8); + * parcBuffer_PutUint64(payload, 5432L); + * + * CCNxInterestPayloadId *payloadId = ccnxInterestPayloadId_CreateAsSHA256Hash(payload); + * ccnxInterest_SetPayloadWithId(interest, payload, payloadId); + * ccnxInterestPayloadId_Release(&payloadId); + * + * parcBuffer_Release(&payload); + * ccnxName_Release(&name); + * ccnxInterest_Release(&instance); + * } + * @endcode + * @see `CCNxPayloadType` + */ +bool ccnxInterest_SetPayloadWithId(CCNxInterest *interest, const PARCBuffer *payload, const CCNxInterestPayloadId *payloadId); + + +/** + * Return a pointer to the payload attached to the specified `CCNxInterest`, if any. It is up to the + * caller to acquire a reference to the payload if necessary. + * + * @param [in] interest A pointer to the `CCNxInstance` instance to update. + * @return A pointer to the payload buffer attached to the supplied `CCNxInterest` instance. Will be NULL + * if the `CCNxInterest` instance has no payload. + * + * Example: + * @code + * { + * CCNxName *name = ccnxName_CreateFromCString("lci:/parc/csl/sensors/radiation/11"); + * CCNxInterest *interest = ccnxInterest_CreateSimple(name); + * + * PARCBuffer *payload = parcBuffer_Allocate(8); + * parcBuffer_PutUint64(payload, 5432L); + * + * ccnxInterest_SetPayload(interest, CCNxPayloadType_DATA, payload); + * + * PARCBuffer *reference = ccnxInterest_GetPayload(interest); + * + * parcBuffer_Release(&payload); + * ccnxName_Release(&name); + * ccnxInterest_Release(&instance); + * } + * @endcode + */ +PARCBuffer *ccnxInterest_GetPayload(const CCNxInterest *interest); + +/** + * Set the Hop Limit for the specified `CCNxInterest` instance. + * The Interest HopLimit element is a counter that is decremented with each hop. It limits the distance an Interest may travel. + * The node originating the Interest may put in any value - up to the maximum - in network byte order. Each node that receives + * an Interest with a HopLimit decrements the value upon reception. If the value is 0 after the decrement, the Interest cannot + * be forwarded off the node. + * + * @param [in] interest A pointer to a `CCNxInterest` instance to update. + * @param [in] hopLimit The hop limit to set. + * + * Example: + * @code + * { + * CCNxName *name = ccnxName_CreateFromCString("lci:/parc/csl/sensors/radiation/11"); + * CCNxInterest *interest = ccnxInterest_CreateSimple(name); + * + * ccnxInterest_SetHopLimit(interest, 30); + * + * uint32_t hopLimit = ccnxInterest_GetHopLimit(interest); + * assertTrue(hopLimit = 30, "Expected 30 for the hopLimit"); + * + * ccnxName_Release(&name); + * ccnxInterest_Release(&instance); + * } + * @endcode + * @see `ccnxInterest_GetHopLimit` + */ +bool ccnxInterest_SetHopLimit(CCNxInterest *interest, uint32_t hopLimit); + +/** + * Get the Hop Limit for the specified `CCNxInterest` instance. + * The Interest HopLimit element is a counter that is decremented with each hop. It limits the distance an Interest may travel. + * The node originating the Interest may put in any value - up to the maximum - in network byte order. Each node that receives + * an Interest with a HopLimit decrements the value upon reception. If the value is 0 after the decrement, the Interest cannot + * be forwarded off the node. + * + * @param [in] interest A pointer to the `CCNxInterest` from which to retrieve the Hop Limit + * + * Example: + * @code + * { + * CCNxName *name = ccnxName_CreateFromCString("lci:/parc/csl/sensors/radiation/11"); + * CCNxInterest *interest = ccnxInterest_CreateSimple(name); + * + * ccnxInterest_SetHopLimit(interest, 30); + * + * uint32_t hopLimit = ccnxInterest_GetHopLimit(interest); + * assertTrue(hopLimit = 30, "Expected 30 for the hopLimit"); + * + * ccnxName_Release(&name); + * ccnxInterest_Release(&instance); + * } + * @endcode + * @see `ccnxInterest_SetHopLimit` + */ +uint32_t ccnxInterest_GetHopLimit(const CCNxInterest *interest); +#endif // libccnx_ccnx_Interest_h diff --git a/libccnx-common/ccnx/common/ccnx_InterestPayloadId.c b/libccnx-common/ccnx/common/ccnx_InterestPayloadId.c new file mode 100755 index 00000000..84e653f7 --- /dev/null +++ b/libccnx-common/ccnx/common/ccnx_InterestPayloadId.c @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ +#include <config.h> + +#include <LongBow/runtime.h> + +#include <ccnx/common/ccnx_InterestPayloadId.h> +#include <ccnx/common/ccnx_NameSegment.h> + +#include <parc/algol/parc_Buffer.h> +#include <parc/algol/parc_Object.h> + +#include <parc/security/parc_CryptoHashType.h> +#include <parc/security/parc_CryptoHash.h> +#include <parc/security/parc_CryptoHasher.h> + +struct ccnx_interest_payload_id { + CCNxNameSegment *nameSegment; + uint8_t type; +}; + +static void +_destroy(CCNxInterestPayloadId **idP) +{ + CCNxInterestPayloadId *id = *idP; + ccnxNameSegment_Release(&id->nameSegment); +} + +parcObject_ExtendPARCObject(CCNxInterestPayloadId, _destroy, ccnxInterestPayloadId_Copy, ccnxInterestPayloadId_ToString, + ccnxInterestPayloadId_Equals, ccnxInterestPayloadId_Compare, NULL, NULL); + +parcObject_ImplementAcquire(ccnxInterestPayloadId, CCNxInterestPayloadId); + +parcObject_ImplementRelease(ccnxInterestPayloadId, CCNxInterestPayloadId); + +CCNxInterestPayloadId * +ccnxInterestPayloadId_Create(const PARCBuffer *data, uint8_t type) +{ + CCNxInterestPayloadId *result = parcObject_CreateInstance(CCNxInterestPayloadId); + parcBuffer_AssertValid(data); + + PARCBuffer *buffer = parcBuffer_Allocate(parcBuffer_Capacity(data) + 1); + assertTrue(type > CCNxInterestPayloadId_TypeCode_App, "App type must be greater than 0x80"); + + parcBuffer_PutUint8(buffer, type); + parcBuffer_PutBuffer(buffer, data); + parcBuffer_Flip(buffer); + result->nameSegment = ccnxNameSegment_CreateTypeValue(CCNxNameLabelType_PAYLOADID, buffer); + parcBuffer_Release(&buffer); + + return result; +} + +CCNxInterestPayloadId * +ccnxInterestPayloadId_CreateAsSHA256Hash(const PARCBuffer *data) +{ + CCNxInterestPayloadId *result = parcObject_CreateInstance(CCNxInterestPayloadId); + + PARCCryptoHasher *hasher = parcCryptoHasher_Create(PARCCryptoHashType_SHA256); + parcCryptoHasher_Init(hasher); + parcCryptoHasher_UpdateBuffer(hasher, data); + PARCCryptoHash *hash = parcCryptoHasher_Finalize(hasher); + parcCryptoHasher_Release(&hasher); + + PARCBuffer *hashData = parcCryptoHash_GetDigest(hash); + PARCBuffer *codedHash = parcBuffer_Allocate(parcBuffer_Capacity(hashData) + 1); + parcBuffer_PutUint8(codedHash, CCNxInterestPayloadId_TypeCode_RFC6920_SHA256); + parcBuffer_PutBuffer(codedHash, hashData); + parcBuffer_Flip(codedHash); + + result->nameSegment = + ccnxNameSegment_CreateTypeValue(CCNxNameLabelType_PAYLOADID, codedHash); + + parcBuffer_Release(&codedHash); + parcCryptoHash_Release(&hash); + + return result; +} + +static CCNxInterestPayloadId * +_ccnxInterestPayloadId_CreateFromNameSegment(const CCNxNameSegment *nameSegment) +{ + ccnxNameSegment_AssertValid(nameSegment); + assertTrue(ccnxNameSegment_GetType(nameSegment) == CCNxNameLabelType_PAYLOADID, + "ccnxInterestPayloadId_CreateFromNameSegment: supplied nameSegment is not a PayloadId"); + if (ccnxNameSegment_GetType(nameSegment) != CCNxNameLabelType_PAYLOADID) { + return NULL; + } + CCNxInterestPayloadId *result = parcObject_CreateInstance(CCNxInterestPayloadId); + result->nameSegment = ccnxNameSegment_Acquire(nameSegment); + + return result; +} + +CCNxInterestPayloadId * +ccnxInterestPayloadId_CreateFromSegmentInName(const CCNxName *name) +{ + ccnxName_AssertValid(name); + + CCNxInterestPayloadId *result = NULL; + size_t count = ccnxName_GetSegmentCount(name); + for (size_t i = 0; i < count; ++i) { + CCNxNameSegment *segment = ccnxName_GetSegment(name, i); + if (ccnxNameSegment_GetType(segment) == CCNxNameLabelType_PAYLOADID) { + result = _ccnxInterestPayloadId_CreateFromNameSegment(segment); + break; + } + } + + return result; +} + + + +const CCNxNameSegment * +ccnxInterestPayloadId_GetNameSegment(const CCNxInterestPayloadId *id) +{ + return id->nameSegment; +} + +PARCBuffer * +ccnxInterestPayloadId_GetValue(const CCNxInterestPayloadId *id) +{ + PARCBuffer *data = ccnxNameSegment_GetValue(id->nameSegment); + parcBuffer_Rewind(data); + parcBuffer_SetPosition(data, 1); + return data; +} + +uint8_t +ccnxInterestPayloadId_GetType(const CCNxInterestPayloadId *id) +{ + PARCBuffer *data = ccnxNameSegment_GetValue(id->nameSegment); + parcBuffer_Rewind(data); + uint8_t type = parcBuffer_GetUint8(data); + return type; +} + + +void +ccnxInterestPayloadId_AssertValid(const CCNxInterestPayloadId *id) +{ + trapIllegalValueIf(ccnxInterestPayloadId_IsValid(id) == false, "CCNxName instance is not valid."); +} + +bool +ccnxInterestPayloadId_IsValid(const CCNxInterestPayloadId *id) +{ + bool result = false; + if (ccnxNameSegment_IsValid(id->nameSegment)) { + result = true; + } + return result; +} + +char * +ccnxInterestPayloadId_ToString(const CCNxInterestPayloadId *id) +{ + ccnxInterestPayloadId_AssertValid(id); + + return ccnxNameSegment_ToString(id->nameSegment); +} + +CCNxInterestPayloadId * +ccnxInterestPayloadId_Copy(const CCNxInterestPayloadId *sourceId) +{ + ccnxInterestPayloadId_AssertValid(sourceId); + + CCNxInterestPayloadId *result = + _ccnxInterestPayloadId_CreateFromNameSegment(sourceId->nameSegment); + return result; +} + +bool +ccnxInterestPayloadId_Equals(const CCNxInterestPayloadId *id1, const CCNxInterestPayloadId *id2) +{ + ccnxInterestPayloadId_AssertValid(id1); + ccnxInterestPayloadId_AssertValid(id2); + + bool result = ((id1 == id2) || + (ccnxNameSegment_Equals(id1->nameSegment, id2->nameSegment))); + return result; +} + +uint32_t +ccnxInterestPayloadId_HashCode(const CCNxInterestPayloadId *id) +{ + ccnxInterestPayloadId_AssertValid(id); + return ccnxNameSegment_HashCode(id->nameSegment); +} + + +int +ccnxInterestPayloadId_Compare(const CCNxInterestPayloadId *id1, const CCNxInterestPayloadId *id2) +{ + ccnxInterestPayloadId_AssertValid(id1); + ccnxInterestPayloadId_AssertValid(id2); + + int result = ccnxNameSegment_Compare(id1->nameSegment, id2->nameSegment); + return result; +} diff --git a/libccnx-common/ccnx/common/ccnx_InterestPayloadId.h b/libccnx-common/ccnx/common/ccnx_InterestPayloadId.h new file mode 100755 index 00000000..cd3d6fce --- /dev/null +++ b/libccnx-common/ccnx/common/ccnx_InterestPayloadId.h @@ -0,0 +1,518 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file ccnx_InterestPayloadId.h + * @ingroup Naming + * @brief The basic CCNx InterestPayloadId. + * + * + */ +#ifndef libccnx_ccnx_InterestPayloadId_h +#define libccnx_ccnx_InterestPayloadId_h + +#include <parc/algol/parc_Buffer.h> +#include <ccnx/common/ccnx_NameSegment.h> +#include <ccnx/common/ccnx_Name.h> +//#include <parc/security/parc_CryptoHashType.h> + +struct ccnx_interest_payload_id; +/** + * @typedef CCNxInterestPayloadId + * @brief An RFC3986 URI compliant identifier in which each path segment carries a label. + */ +typedef struct ccnx_interest_payload_id CCNxInterestPayloadId; + + +typedef enum { + //RFC 6920 + CCNxInterestPayloadId_TypeCode_RFC6920_Reserved = 0x00, + CCNxInterestPayloadId_TypeCode_RFC6920_SHA256 = 0x01, //256 bits + CCNxInterestPayloadId_TypeCode_RFC6920_SHA256_128 = 0X02, //128 bits + CCNxInterestPayloadId_TypeCode_RFC6920_SHA256_120 = 0X03, //120 bits + CCNxInterestPayloadId_TypeCode_RFC6920_SHA256_96 = 0X04, //96 bits + CCNxInterestPayloadId_TypeCode_RFC6920_SHA256_64 = 0X05, //64 bits + CCNxInterestPayloadId_TypeCode_RFC6920_SHA256_32 = 0x06, //32 bits + //PARC + CCNxInterestPayloadId_TypeCode_App = 0x80, + CCNxInterestPayloadId_TypeCode_Nonce = 0x81 +} CCNxInterestPayloadId_TypeCode; + + +/** + * Create a new instance of a `CCNxInterestPayloadId` that uses the provided + * PARCBuffer as the ID. The instance is dynamically allocated and must be released by + * calling {@link ccnxInterestPayloadId_Release}. + * + * @param [in] data a PARCBuffer to be used as the body of the KeyId name segment. + * @param [in] type a uint8_t, nominally one of the CCNxInterestPayloadId_TypeCodes, + * which is prepended to the data. + * + * @return A pointer to a `CCNxInterestPayloadId` instance. + * + * Example: + * @code + * { + * PARCBuffer *somePaylaod = .... + * CCNxInterest *someInterest = .... + * + * PARCBuffer *buffer = parcBuffer_WrapCString("Key_42"); + * CCNxInterestPayloadId *interestPayloadId = + * ccnxInterestPayloadId_Create(data, InterestPayloadIdType_App); + * parcBuffer_Release(&buffer); + * + * ccnxInterest_SetPayloadAndId(someInterest, somePayload, interestPayloadId); + * ccnxInterestPayloadId_Release(&interestPayloadId); + * parcBuffer_Release(&somePayload); + * + * ... + * + * } + * @endcode + */ +CCNxInterestPayloadId * +ccnxInterestPayloadId_Create(const PARCBuffer *data, uint8_t type); + + +/** + * Create a new instance of a `CCNxInterestPayloadId` that is a sha256 hash of the + * input data. The instance is dynamically allocated and must be released by + * calling {@link ccnxInterestPayloadId_Release}. + * + * @param [in] data a PARCBuffer to be hashed to obtain the ID bytes. + * + * @return A pointer to a `CCNxInterestPayloadId` instance. + * + * Example: + * @code + * { + * PARCBuffer *somePaylaod = .... + * CCNxInterest *someInterest = .... + * + * CCNxInterestPayloadId *interestPayloadId = + * ccnxInterestPayloadId_CreateAsSHA256Hash(somePayload); + * + * ccnxInterest_SetPayloadAndId(someInterest, somePayload, interestPayloadId); + * ccnxInterestPayloadId_Release(&interestPayloadId); + * parcBuffer_Release(&somePayload); + * + * ... + * + * } + * @endcode + */ +CCNxInterestPayloadId * +ccnxInterestPayloadId_CreateAsSHA256Hash(const PARCBuffer *data); + +/** + * Create a new instance of a `CCNxInterestPayloadId` from an Interest Payload Id name + * segment if one is present in the provided {@link CCNxName}, otherwise return + * NULL. The instance is dynamically allocated and must be released by calling {@link + * ccnxInterestPayloadId_Release}. + * + * @param [in] name a CCNxName that may or may not carry an Interest Payload Id CCNxNameSegment. + * + * @return A pointer to a `CCNxInterestPayloadId` instance or NULL if no + * Interest-Payload-Id typed CCNxNameSegment is found. + * + * Example: + * @code + * { + * CCNxName *someName = ... + * + * CCNxInterestPayloadId *interestPayloadId = + * ccnxInterestPayloadId_CreateFromSegmentInName(someName); + * + * if (interestPayloadId != NULL) { + * .... + * ccnxInterestPayloadId_Release(&interestPayloadId); + * } + * + * ... + * + * ccnxName_Release(&someName); + * } + * @endcode + */ +CCNxInterestPayloadId * +ccnxInterestPayloadId_CreateFromSegmentInName(const CCNxName *name); + +/** + * Get a pointer to a CCNxNameSegment derived from the provided + * CCNxInterestPayloadId. There is no need to release the returned CCNxNameSegment + * object. + * + * @param [in] interestPayloadId A pointer to the `CCNxInterestPayloadId` instance. + * + * @return A pointer to a CCNxNameSegment. + * + * Example: + * @code + * { + * CCNxName *someName = ... + * + * CCNxInterestPayloadId *interestPayloadId = + * ccnxInterestPayloadId_CreateFromSegmentInName(someName); + * + * if (interestPayloadId != NULL) { + * CCNxNameSegmemt *nameSegment = ccnxInterestPayloadId_GetNameSegment(interestPayloadId); + * .... + * ccnxInterestPayloadId_Release(&interestPayloadId); + * } + * + * ... + * + * ccnxName_Release(&someName); + * } + * @endcode + */ +const CCNxNameSegment * +ccnxInterestPayloadId_GetNameSegment(const CCNxInterestPayloadId *interestPayloadId); + +/** + * Get the CCNxInterestPayloadId's value as a PARCBuffer. There is no need to release + * the returned PARCBuffer object. + * + * @param [in] interestPayloadId A pointer to the `CCNxInterestPayloadId` instance. + * + * @return A pointer to a PARCBuffer object containing the Id bytes. + * + * Example: + * @code + * { + * CCNxName *someName = ... + * + * CCNxInterestPayloadId *interestPayloadId = + * ccnxInterestPayloadId_CreateFromSegmentInName(someName); + * + * if (interestPayloadId != NULL) { + * PARCBuffer *ipIdValue = ccnxInterestPayloadId_GetValue(interestPayloadId); + * .... + * ccnxInterestPayloadId_Release(&interestPayloadId); + * } + * + * ... + * + * ccnxName_Release(&someName); + * } + * @endcode + */ +PARCBuffer * +ccnxInterestPayloadId_GetValue(const CCNxInterestPayloadId *interestPayloadId); + +/** + * Get the CCNxInterestPayloadId's uint8 type. + * + * @param [in] interestPayloadId A pointer to the `CCNxInterestPayloadId` instance. + * + * @return A uint8_t containing the type value. + * + * Example: + * @code + * { + * CCNxName *someName = ... + * + * CCNxInterestPayloadId *interestPayloadId = + * ccnxInterestPayloadId_CreateFromSegmentInName(someName); + * + * if (interestPayloadId != NULL) { + * uint8_t type = ccnxInterestPayloadId_GetType(interestPayloadId); + * .... + * ccnxInterestPayloadId_Release(&interestPayloadId); + * } + * + * ... + * + * ccnxName_Release(&someName); + * } + * @endcode + */ +uint8_t +ccnxInterestPayloadId_GetType(const CCNxInterestPayloadId *interestPayloadId); + +/** + * Increase the number of references to a `CCNxInterestPayloadId` instance. + * + * Note that a new `CCNxInterestPayloadId` is not created, only that the given + * `CCNxInterestPayloadId` reference count is incremented. Discard the reference by + * invoking {@link ccnxInterestPayloadId_Release}. + * + * @param [in] interestPayloadId A pointer to the original `CCNxInterestPayloadId`. + * + * @return The value of the input parameter @p interestPayloadId. + * + * Example: + * @code + * { + * CCNxInterestPayloadId *original = ccnxInterestPayloadId_CreateAsSHA256Hash(...); + * + * CCNxInterestPayloadId *reference = ccnxInterestPayloadId_Acquire(original); + * + * ccnxInterestPayloadId_Release(&original); + * ccnxInterestPayloadId_Release(&reference); //Triggers final cleanup + * } + * @endcode + * + * @see ccnxInterestPayloadId_Release + */ +CCNxInterestPayloadId * +ccnxInterestPayloadId_Acquire(const CCNxInterestPayloadId *interestPayloadId); + +/** + * Release a previously acquired reference to the specified instance, + * decrementing the reference count for the instance. + * + * The pointer to the instance is set to NULL as a side-effect of this function. + * + * If the invocation causes the last reference to the instance to be released, + * the instance is deallocated and the instance's implementation will perform + * additional cleanup and release other privately held references. + * + * @param [in] interestPayloadIdP A pointer to a pointer to the instance to release. + * + * Example: + * @code + * { + * CCNxInterestPayloadId *interestPayloadId = ccnxInterestPayloadId_CreateAsSHA256Hash(...); + * + * ccnxInterestPayloadId_Release(&interestPayloadId); + * } + * @endcode + * + * @see {@link ccnxInterestPayloadId_Acquire} + */ +void +ccnxInterestPayloadId_Release(CCNxInterestPayloadId **interestPayloadIdP); + +/** + * Assert that an instance of `CCNxInterestPayloadId` is valid. If the instance is not + * valid, terminate via {@link trapIllegalValue}. Valid means the internal state of the + * type is consistent with its required current or future behavior. This may include the + * validation of internal instances of types. + * + * @param [in] interestPayloadId A pointer to a `CCNxInterestPayloadId` instance. + * + * Example: + * @code + * { + * CCNxInterestPayloadId *interestPayloadId = ccnxInterestPayloadId_CreateAsSHA256Hash(...); + * + * ccnxInterestPayloadId_AssertValid(interestPayloadId); + * + * ccnxInterestPayloadId_Release(&interestPayloadId); + * } + * @endcode + */ +void +ccnxInterestPayloadId_AssertValid(const CCNxInterestPayloadId *interestPayloadId); + +/** + * Determine if an instance of `CCNxInterestPayloadId` is valid. + * + * Valid means the internal state of the type is consistent with its required current + * or future behavior. This may include the validation of internal instances of + * types. + * + * @param [in] interestPayloadId A pointer to a `CCNxInterestPayloadId` instance. + * + * @return true If the instance is valid. + * @return false if the instance is not valid. + * + * Example: + * @code + * { + * CCNxInterestPayloadId *interestPayloadId = ccnxInterestPayloadId_CreateAsSHA256Hash(...); + * + * if (ccnxInterestPayloadId_IsValid(interestPayloadId) == true) { + * ... + * } + * + * ccnxInterestPayloadId_Release(&interestPayloadId); + * } + * @endcode + */ +bool +ccnxInterestPayloadId_IsValid(const CCNxInterestPayloadId *interestPayloadId); + +/** + * Produce a null-terminated string representation of the specified instance. The + * non-null result must be freed by the caller via {@link parcMemory_Deallocate}. + * + * @param [in] interestPayloadId A pointer to the `CCNxInterestPayloadId` instance. + * + * @return NULL Cannot allocate memory. + * @return non-NULL A pointer to an allocated, null-terminated C string that must be + * deallocated via `parcMemory_Deallocate()`. + * + * Example: + * @code + * { + * CCNxInterestPayloadId *instance = ccnxInterestPayloadId_CreateAsSHA256Hash(...); + * + * char *string = ccnxInterestPayloadId_ToString(instance); + * + * if (string != NULL) { + * printf("Hello: %s\n", string); + * parcMemory_Deallocate(string); + * } else { + * printf("Cannot allocate memory\n"); + * } + * + * ccnxInterestPayloadId_Release(&instance); + * } + * @endcode + */ +char * +ccnxInterestPayloadId_ToString(const CCNxInterestPayloadId *interestPayloadId); + +/** + * Create a copy of the specified `CCNxInterestPayloadId` instance, producing a new, + * independent, instance from dynamically allocated memory. This a deep copy, all + * referenced memory is copied. The created instance of `CCNxInterestPayloadId` must be + * released by calling {@link ccnxInterestPayloadId_Release}(). + * + * @param [in] interestPayloadId The `CCNxInterestPayloadId` to copy + * + * @return A new, independent copy of the given `CCNxInterestPayloadId`. + * + * Example: + * @code + * { + * CCNxInterestPayloadId *interestPayloadId = ccnxInterestPayloadId_CreateAsSHA256Hash(...); + * CCNxInterestPayloadId *copy = ccnxInterestPayloadId_Copy(interestPayloadId); + * + * ... + * + * ccnxInterestPayloadId_Release(&interestPayloadId); + * ccnxInterestPayloadId_Release(©); + * } + * @endcode + */ +CCNxInterestPayloadId * +ccnxInterestPayloadId_Copy(const CCNxInterestPayloadId *interestPayloadId); + +/** + * Determine if two `CCNxInterestPayloadId` instances are equal. + * + * The following equivalence relations on non-null `CCNxInterestPayloadId` instances are maintained: + * + * * It is reflexive: for any non-null reference value x, `ccnxInterestPayloadId_Equals(x, x)` must return true. + * + * * It is symmetric: for any non-null reference values x and y, `ccnxInterestPayloadId_Equals(x, y)` must return true if and only if + * `ccnxInterestPayloadId_Equals(y, x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `ccnxInterestPayloadId_Equals(x, y)` returns true and + * `ccnxInterestPayloadId_Equals(y, z)` returns true, + * then `ccnxInterestPayloadId_Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple invocations of `ccnxInterestPayloadId_Equals(x, y)` + * consistently return true or consistently return false. + * + * * For any non-null reference value x, `ccnxInterestPayloadId_Equals(x, NULL)` must return false. + * + * @param [in] interestPayloadId1 A pointer to a `CCNxInterestPayloadId` instance. + * @param [in] interestPayloadId2 A pointer to a `CCNxInterestPayloadId` instance. + * + * @return True If the given `CCNxInterestPayloadId` instances are equal + * @return False Otherwise + * + * Example: + * @code + * { + * CCNxInterestPayloadId *a = ccnxInterestPayloadId_CreateAsSHA256Hash(...); + * CCNxInterestPayloadId *b = ccnxInterestPayloadId_CreateAsSHA256Hash(...); + * + * if (ccnxInterestPayloadId_Equals(a, b)) { + * // true + * } else { + * // false + * } + * ccnxInterestPayloadId_Release(&a); + * ccnxInterestPayloadId_Release(&b); + * } + * @endcode + * + * @see {@link ccnxInterestPayloadId_Compare} + */ +bool +ccnxInterestPayloadId_Equals(const CCNxInterestPayloadId *interestPayloadId1, const CCNxInterestPayloadId *interestPayloadId2); + +/** + * Compare @p interestPayloadId1 to @p interestPayloadId2 using CCNx canonical ordering (shortlex). + * + * `NULL` is considered the shortest interestPayloadId, so `(NULL, non-NULL) -> -1` and + * `(NULL, NULL) -> 0`, `(non-NULL, NULL) -> +1`. + * + * @param [in] interestPayloadId1 A pointer to a `CCNxInterestPayloadId` instance. + * @param [in] interestPayloadId2 A pointer to a `CCNxInterestPayloadId` instance. + * + * Returns: + * <ul> + * <li>-1 for interestPayloadId1 < interestPayloadId2</li> + * <li> 0 for interestPayloadId1 = interestPayloadId2</li> + * <li>+1 for interestPayloadId1 > interestPayloadId2</li> + * </ul> + * + * Example: + * @code + * { + * CCNxInterestPayloadId *a = ccnxInterestPayloadId_CreateAsSHA256Hash(...); + * CCNxInterestPayloadId *b = ccnxInterestPayloadId_CreateAsSHA256Hash(...); + * + * if (ccnxInterestPayloadId_Compare(a, b) == 0) { + * ... + * } + * + * ccnxInterestPayloadId_Release(&a); + * ccnxInterestPayloadId_Release(&b); + * } + * @endcode + * + * @see {@link ccnxInterestPayloadId_Equals} + */ +int +ccnxInterestPayloadId_Compare(const CCNxInterestPayloadId *interestPayloadId1, const CCNxInterestPayloadId *interestPayloadId2); + +/** + * Retrieve the hashcode of the CCNxInterestPayloadId. + * + * @param [in] id A pointer to a `CCNxINterestPayloadId` instance. + * + * @return uint32_t The hashcode + * + * Example: + * @code + * { + * CCNxName *someName = ... + * + * CCNxInterestPayloadId *interestPayloadId = + * ccnxInterestPayloadId_CreateFromSegmentInName(someName); + * + * if (interestPayloadId != NULL) { + * uint32_t hashcode = ccnxInterestPayloadId_HashCode(interestPayloadId); + * .... + * ccnxInterestPayloadId_Release(&interestPayloadId); + * } + * + * ... + * + * ccnxName_Release(&someName); + * } + * @endcode + */ +uint32_t +ccnxInterestPayloadId_HashCode(const CCNxInterestPayloadId *id); +#endif // libccnx_ccnx_InterestPayloadId_h diff --git a/libccnx-common/ccnx/common/ccnx_InterestReturn.c b/libccnx-common/ccnx/common/ccnx_InterestReturn.c new file mode 100755 index 00000000..26527f58 --- /dev/null +++ b/libccnx-common/ccnx/common/ccnx_InterestReturn.c @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ +#include <config.h> +#include <stdio.h> + +#include <LongBow/runtime.h> + +#include <ccnx/common/ccnx_InterestReturn.h> +#include <ccnx/common/ccnx_Interest.h> +#include <ccnx/common/internal/ccnx_InterestReturnFacadeV1.h> + +#include <parc/algol/parc_Memory.h> + +#include <parc/algol/parc_Object.h> + +static const CCNxInterestReturnInterface *_defaultImpl = &CCNxInterestReturnFacadeV1_Implementation; + +CCNxInterestReturn * +ccnxInterestReturn_CreateWithImpl(const CCNxInterestReturnInterface *impl, + const CCNxInterest *interest, + CCNxInterestReturn_ReturnCode returnCode) +{ + //The impl must do an Acquire + + CCNxInterestReturn *result = impl->Create(interest, returnCode); + + // And set the dictionary's interface pointer to the one we just used to create this. + ccnxTlvDictionary_SetMessageInterface(result, impl); + + return result; +} + + +// Canonical Functions +CCNxInterestReturn * +ccnxInterestReturn_Create(const CCNxInterest *interest, CCNxInterestReturn_ReturnCode returnCode) +{ + CCNxInterestReturn *result = + ccnxInterestReturn_CreateWithImpl(_defaultImpl, interest, returnCode); + return result; +} + +void +ccnxInterestReturn_AssertValid(const CCNxInterestReturn *interestReturn) +{ + assertNotNull(interestReturn, "Must be a non-null pointer to a CCNxInterestReturn."); + // Check for required fields in the underlying dictionary. Case 1036 + CCNxInterestReturnInterface *impl = ccnxInterestReturnInterface_GetInterface(interestReturn); + assertNotNull(impl, "Interest must have an valid implementation pointer."); + + if (impl->AssertValid) { + impl->AssertValid(interestReturn); + } else { + trapNotImplemented("ccnxInterest_GetName"); + } +} + +CCNxInterestReturn * +ccnxInterestReturn_Acquire(const CCNxInterestReturn *instance) +{ + return ccnxTlvDictionary_Acquire(instance); +} + +void +ccnxInterestReturn_Release(CCNxInterestReturn **instance) +{ + ccnxTlvDictionary_Release(instance); +} + +bool +ccnxInterestReturn_Equals(const CCNxInterestReturn *a, const CCNxInterestReturn *b) +{ + if (a == NULL || b == NULL) { + return false; + } + + if (a == b) { + return true; + } + + CCNxInterestReturnInterface *impl = ccnxInterestReturnInterface_GetInterface(a); + CCNxInterestReturnInterface *implB = ccnxInterestReturnInterface_GetInterface(b); + + if (impl != implB) { + return false; + } + + if (impl->GetReturnCode(a) != impl->GetReturnCode(b)) { + return false; + } + + return ccnxInterest_Equals(a, b); +} + +char * +ccnxInterestReturn_ToString(const CCNxInterestReturn *interestReturn) +{ + ccnxInterestReturn_AssertValid(interestReturn); + + char *name = ccnxName_ToString(ccnxInterest_GetName(interestReturn)); + + CCNxInterestReturnInterface *impl = ccnxInterestReturnInterface_GetInterface(interestReturn); + CCNxInterestReturn_ReturnCode code = impl->GetReturnCode(interestReturn); + + char *string; + int len = asprintf(&string, "CCNxInterestReturn{.code=%dms .name=\"%s\"}", code, name); + if (len < 0) { + //We have serious problems. + return name; + } + + parcMemory_Deallocate((void **) &name); + + char *result = parcMemory_StringDuplicate(string, strlen(string)); + free(string); + + return result; +} + +// Accessors Functions +CCNxInterestReturn_ReturnCode +ccnxInterestReturn_GetReturnCode(const CCNxInterestReturn *interestReturn) +{ + ccnxInterestReturn_AssertValid(interestReturn); + CCNxInterestReturnInterface *impl = ccnxInterestReturnInterface_GetInterface(interestReturn); + CCNxInterestReturn_ReturnCode code = impl->GetReturnCode(interestReturn); + return code; +} + diff --git a/libccnx-common/ccnx/common/ccnx_InterestReturn.h b/libccnx-common/ccnx/common/ccnx_InterestReturn.h new file mode 100755 index 00000000..44bb48aa --- /dev/null +++ b/libccnx-common/ccnx/common/ccnx_InterestReturn.h @@ -0,0 +1,299 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file ccnx_InterestReturn.h + * @ingroup InterestRetrun + * @brief A CCNx InterestReturn is an optional error response for an Interest that can't be satisfied by the returning entity + * + * An InterestReturn is a convience type for a returned Interest. It is created from an CCNxInterest and a return code + * with the intent of returning the interest to the previous hop. Other than modifing the PacketType to indicate that it + * is a Interest Return, it wraps and preserves the state of the provided CCNxInterest and can be used with CCNxInterest + * functions as if it were a CCNxInterest type. + * + * The possible return codes are: + * + * +--------------------+ + * | No Route | + * | | + * | Hop Limit Exceeded | + * | | + * | No Resources | + * | | + * | Path Error | + * | | + * | Prohibited | + * | | + * | Congested | + * | | + * | MTU too large | + * +--------------------+ + * + * @see {@link CCNxInterest} + * + */ + +#ifndef libccnx_ccnx_InterestReturn_h +#define libccnx_ccnx_InterestReturn_h + +#include <ccnx/common/internal/ccnx_TlvDictionary.h> +#include <ccnx/common/ccnx_Interest.h> + +/** + * @typedef CCNxInterestReturn + * @brief The CCNx InterestReturn Message + */ +typedef CCNxTlvDictionary CCNxInterestReturn; + +/** + * @typedef CCNxInterestReturn_ReturnCode + * @brief The CCNx InterestReturn ReturnCode options + */ +typedef enum { + CCNxInterestReturn_ReturnCode_NoRoute = 1, + CCNxInterestReturn_ReturnCode_HopLimitExceeded = 2, + CCNxInterestReturn_ReturnCode_NoResources = 3, + CCNxInterestReturn_ReturnCode_PathError = 4, + CCNxInterestReturn_ReturnCode_Prohibited = 5, + CCNxInterestReturn_ReturnCode_Congestion = 6, + CCNxInterestReturn_ReturnCode_MTUTooLarge = 7, + CCNxInterestReturn_ReturnCode_END = 8 +} CCNxInterestReturn_ReturnCode; + +/** + * Create a new instance of `CCNxInterestRetrun` from the specified CCNxInterest, with the specified return code. + * + * The created instance of `CCNxInterestReturn` must be released by calling {@link ccnxInterestReturn_Release}(). + * + * @param [in] interest A pointer to the {@link CCNxInterest} to be returned. + * @param [in] returnCode The {@link CCNxInterestReturn_ReturnCode} to return. + * + * @return A new instance of a `CCNxInterestReturn`. + * + * Example: + * @code + * { + * CCNxInterest *interest = ... + * + * CCNxInterestReturn *interestToReturn = ccnxInterest_Create(interest, CCNxInterestReturn_ReturnCode_NoRoute); + * + * ... + * + * ccnxInterestReturn_Release(&interestToReturn); + * } + * @endcode + * + * @see {@link ccnxInterestReturn_Release} + */ +CCNxInterestReturn * +ccnxInterestReturn_Create(const CCNxInterest *interest, CCNxInterestReturn_ReturnCode returnCode); + + +/** + * Retrieve the specified `CCNxInterestReturn`'s {@link CCNxInterestReturn_ReturnCode} + * + * @return The return code for the specified CCNxInterestReturn. + * + * Example: + * @code + * { + * CCNxInterest *interest = ccnxInterest_CreateSimple(...); + * CCNxInterestReturn *interestReturn = ccnxInterestReturn_Create(interest, CCNxInterestReturn_ReturnCode_NoRoute); + * + * ... + * + * CCNxInterestReturn_ReturnCode returnCode = ccnxInterest_GetReturnCode(interestReturn); + * + * ... + * + * ccnxInterestReturn_Release(&interestReturn); + * } + * @endcode + * + */ +CCNxInterestReturn_ReturnCode +ccnxInterestReturn_GetReturnCode(const CCNxInterestReturn *interestReturn); + + +// Canonocal +/** + * Increase the number of references to a `CCNxInterestReturn`. + * + * Note that a new `CCNxInterestReturn` is not created, + * only that the given `CCNxInterestReturn` reference count is incremented. + * Discard the reference by invoking {@link ccnxInterestReturn_Release}. + * + * @param [in] instance A pointer to the original instance. + * @return The value of the input parameter @p instance. + * + * Example: + * @code + * { + * CCNxInterestReturn *reference = ccnxInterestReturn_Acquire(interestReturn); + * + * ... + * + * ccnxInterestReturn_Release(&reference); + * + * } + * @endcode + * + * @see {@link ccnxInterestReturn_Release} + */ + +CCNxInterestReturn * +ccnxInterestReturn_Acquire(const CCNxInterestReturn *instance); + +/** + * Release a previously acquired reference to the specified instance, + * decrementing the reference count for the instance. + * + * The pointer to the instance is set to NULL as a side-effect of this function. + * + * If the invocation causes the last reference to the instance to be released, + * the instance is deallocated and the instance's implementation will perform + * additional cleanup and release other privately held references. + * + * @param [in,out] instanceP A pointer to a pointer to the instance to release. + * + * Example: + * @code + * { + * CCNxInterestReturn *reference = ccnxInterestReturn_Acquire(contentObject); + * + * ... + * + * ccnxInterestReturn_Release(&reference); + * } + * @endcode + * + * @see {@link ccnxInterestReturn_Acquire} + */ +void +ccnxInterestReturn_Release(CCNxInterestReturn **instanceP); + +/** + * Determine if two `CCNxInterestReturn` instances are equal. + * + * The following equivalence relations on non-null `CCNxInterestReturn` instances are maintained: + * + * * It is reflexive: for any non-null reference value x, `ccnxInterestReturn_Equals(x, x)` + * must return true. + * + * * It is symmetric: for any non-null reference values x and y, + * `ccnxInterestReturn_Equals(x, y)` must return true if and only if + * `ccnxInterestReturn_Equals(y, x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `ccnxInterestReturn_Equals(x, y)` returns true and + * `ccnxInterestReturn_Equals(y, z)` returns true, + * then `ccnxInterestReturn_Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple + * invocations of `ccnxInterestReturn_Equals(x, y)` consistently return true or + * consistently return false. + * + * * For any non-null reference value x, `ccnxInterestReturn_Equals(x, NULL)` must + * return false. + * + * @param interestReturnA A pointer to a `CCNxInterestReturn` instance. + * @param interestReturnB A pointer to a `CCNxInterestReturn` instance. + * @return true if the two `CCNxInterestReturn` instances are equal. + * + * Example: + * @code + * { + * + * CCNxInterestReturn *interestReturnA = ccnxInterestReturn_Create(interestA, CCNxInterestReturn_ReturnCode_NoRoute); + * CCNxInterestReturn *interestReturnB = ccnxInterestReturn_Create(interestA, CCNxInterestReturn_ReturnCode_NoRoute); // same as A + * CCNxInterestReturn *interestReturnC = ccnxInterestReturn_Create(interestA, CCNxInterestReturn_ReturnCode_HopLimitExceeded ); // different + * + * if (ccnxInterestReturn_Equals(interestReturnA, interestReturnB)) { + * // this is expected... + * } + * + * if (ccnxInterestReturn_Equals(interestReturnA, interestReturnC)) { + * // this is NOT expected + * } + * + * ... + * + * ccnxInterestReturn_Release(&interestReturnA); + * ccnxInterestReturn_Release(&interestReturnB); + * ccnxInterestReturn_Release(&interestReturnC); + * } + * @endcode + */ +bool +ccnxInterestReturn_Equals(const CCNxInterestReturn *a, const CCNxInterestReturn *b); + +/** + * Produce a null-terminated string representation of the specified instance. + * + * The result must be freed by the caller via {@link parcMemory_Deallocate()}. + * + * @param [in] interestReturn A pointer to the instance. + * + * @return NULL Cannot allocate memory. + * @return non-NULL A pointer to an allocated, nul-terminated C string that must be deallocated via `parcMemory_Deallocate()`. + * + * Example: + * @code + * { + * CCNxInterestReturn *interestReturn = ccnxInterestReturn_Create(...); + * + * char *string = ccnxInterestReturn_ToString(interestReturn); + * + * if (string != NULL) { + * printf("InterestReturn looks like: %s\n", string); + * parcMemory_Deallocate(string); + * } else { + * printf("Cannot allocate memory\n"); + * } + * + * ccnxInterestReturn_Release(&instance); + * } + * @endcode + * + */ +char * +ccnxInterestReturn_ToString(const CCNxInterestReturn *interestReturn); + +/** + * Assert that an instance of `CCNxInterestReturn` is valid. + * + * If the instance is not valid, terminate via {@link trapIllegalValue} + * + * Valid means the internal state of the type is consistent with its + * required current or future behaviour. + * This may include the validation of internal instances of types. + * + * @param [in] interestReturn A pointer to the instance to check. + * + * Example: + * @code + * { + * CCNxInterestReturn *interestReturn = ccnxInterest_Create(...); + * ccnxInterestReturn_AssertValid(interestReturn); + * + * ... + * + * ccnxInterestReturn_Release(&instance); + * } + * @endcode + */ +void +ccnxInterestReturn_AssertValid(const CCNxInterestReturn *interestReturn); +#endif //libccnx_ccnx_InterestReturn_h diff --git a/libccnx-common/ccnx/common/ccnx_KeyLocator.c b/libccnx-common/ccnx/common/ccnx_KeyLocator.c new file mode 100755 index 00000000..ea61a0c1 --- /dev/null +++ b/libccnx-common/ccnx/common/ccnx_KeyLocator.c @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ +#include <config.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include <LongBow/runtime.h> + +#include <ccnx/common/ccnx_KeyLocator.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Object.h> + +#include <parc/security/parc_Key.h> + +struct ccnx_key_locator { + union { + PARCKey *key; + CCNxLink *keyLink; + } locator; + CCNxKeyLocatorType type; +}; + + +static void +_Destroy(CCNxKeyLocator **keyLocatorP) +{ + if (keyLocatorP != NULL) { + CCNxKeyLocator *keyLocator = *keyLocatorP; + if (ccnxKeyLocator_IsKey(keyLocator)) { + parcKey_Release(&keyLocator->locator.key); + } else if (ccnxKeyLocator_IsKeyLink(keyLocator)) { + ccnxLink_Release(&keyLocator->locator.keyLink); + } else { + assertTrue(0, "KeyLocator is not one of %s.", "Key or KeyLink"); + } + } +} + +parcObject_ExtendPARCObject(CCNxKeyLocator, _Destroy, ccnxKeyLocator_Copy, ccnxKeyLocator_ToString, ccnxKeyLocator_Equals, NULL, NULL, NULL); + +parcObject_ImplementAcquire(ccnxKeyLocator, CCNxKeyLocator); + +parcObject_ImplementRelease(ccnxKeyLocator, CCNxKeyLocator); + +static CCNxKeyLocator * +_Create(void) +{ + CCNxKeyLocator *result = parcObject_CreateInstance(CCNxKeyLocator); + if (NULL != result) { + result->locator.key = NULL; + result->type = CCNxKeyLocatorType_None; + } + return result; +} + +CCNxKeyLocator * +ccnxKeyLocator_Copy(const CCNxKeyLocator *original) +{ + CCNxKeyLocator *copy = NULL; + + switch (original->type) { + case CCNxKeyLocatorType_Key: { + PARCKey *keyCopy = parcKey_Copy(original->locator.key); + copy = ccnxKeyLocator_CreateFromKey(keyCopy); + parcKey_Release(&keyCopy); + } + break; + + case CCNxKeyLocatorType_Link: { + CCNxLink *keyLinkCopy = ccnxLink_Copy(original->locator.keyLink); + copy = ccnxKeyLocator_CreateFromKeyLink(keyLinkCopy); + ccnxLink_Release(&keyLinkCopy); + } + break; + + case CCNxKeyLocatorType_None: + copy = _Create(); + break; + } + return copy; +} + +CCNxKeyLocator * +ccnxKeyLocator_CreateFromKey(PARCKey *key) +{ + assertNotNull(key, "Parameter must be a non-null CCNxKey pointer."); + + CCNxKeyLocator *result = _Create(); + if (result != NULL) { + result->locator.key = parcKey_Acquire(key); + result->type = CCNxKeyLocatorType_Key; + } + + return result; +} + +CCNxKeyLocator * +ccnxKeyLocator_CreateFromKeyLink(CCNxLink *keyLink) +{ + CCNxKeyLocator *result = _Create(); + if (result != NULL) { + result->locator.keyLink = ccnxLink_Acquire(keyLink); + result->type = CCNxKeyLocatorType_Link; + } + + return result; +} + +bool +ccnxKeyLocator_Equals(const CCNxKeyLocator *a, const CCNxKeyLocator *b) +{ + if (a == b) { + return true; + } + if (a == NULL || b == NULL) { + return false; + } + + if (a->type == b->type) { + switch (a->type) { + case CCNxKeyLocatorType_Key: + return parcKey_Equals(a->locator.key, b->locator.key); + + case CCNxKeyLocatorType_Link: + return ccnxLink_Equals(a->locator.keyLink, b->locator.keyLink); + + default: + break; + // fall through + } + } + return false; +} + +CCNxKeyLocatorType +ccnxKeyLocator_GetType(const CCNxKeyLocator *ccnxKeyLocator) +{ + assertNotNull(ccnxKeyLocator, "Parameter must be a non-null CCNxKeyLocator pointer."); + + return ccnxKeyLocator->type; +} + +bool +ccnxKeyLocator_IsKey(const CCNxKeyLocator *keyLocator) +{ + return (keyLocator->type == CCNxKeyLocatorType_Key); +} + +bool +ccnxKeyLocator_IsKeyLink(const CCNxKeyLocator *keyLocator) +{ + return (keyLocator->type == CCNxKeyLocatorType_Link); +} + +char * +ccnxKeyLocator_ToString(const CCNxKeyLocator *keyLocator) +{ + char *result; + + if (keyLocator == NULL) { + char *nullString = "NULL"; + result = parcMemory_StringDuplicate(nullString, strlen(nullString)); + } else { + char *locator; + if (ccnxKeyLocator_IsKey(keyLocator)) { + locator = parcKey_ToString(keyLocator->locator.key); + } else if (ccnxKeyLocator_IsKeyLink(keyLocator)) { + locator = ccnxLink_ToString(keyLocator->locator.keyLink); + } else { + char *initString = "not initialised."; + locator = parcMemory_StringDuplicate(initString, strlen(initString)); + } + char *string; + int failure = asprintf(&string, "KeyLocator { %s }", locator); + assertTrue(failure > -1, "Error asprintf"); + + parcMemory_Deallocate((void **) &locator); + + result = parcMemory_StringDuplicate(string, strlen(string)); + free(string); + } + + return result; +} + +CCNxLink * +ccnxKeyLocator_GetKeyLink(const CCNxKeyLocator *keyLocator) +{ + assertTrue(keyLocator->type == CCNxKeyLocatorType_Link, "Key Locator must be of type CCNxKeyLocatorType_Link"); + + if (keyLocator->type == CCNxKeyLocatorType_Link) { + return keyLocator->locator.keyLink; + } + return NULL; +} + +PARCKey * +ccnxKeyLocator_GetKey(const CCNxKeyLocator *keyLocator) +{ + assertTrue(keyLocator->type == CCNxKeyLocatorType_Key, "Key Locator must be of type CCNxKeyLocatorType_Key"); + + if (keyLocator->type == CCNxKeyLocatorType_Key) { + return keyLocator->locator.key; + } + return NULL; +} + +void +ccnxKeyLocator_AssertValid(const CCNxKeyLocator *keyLocator) +{ + assertNotNull(keyLocator, "CCNxKeyLocator pointer was null"); + if (ccnxKeyLocator_IsKey(keyLocator)) { + parcKey_AssertValid(keyLocator->locator.key); + } else if (ccnxKeyLocator_IsKeyLink(keyLocator)) { + ccnxLink_AssertValid(keyLocator->locator.keyLink); + } else { + assertTrue(0, "KeyLocator is not one of %s.", "Key or Key Link"); + } +} diff --git a/libccnx-common/ccnx/common/ccnx_KeyLocator.h b/libccnx-common/ccnx/common/ccnx_KeyLocator.h new file mode 100755 index 00000000..e48b80a7 --- /dev/null +++ b/libccnx-common/ccnx/common/ccnx_KeyLocator.h @@ -0,0 +1,376 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file ccnx_KeyLocator.h + * @ingroup Signature + * @brief A `CCNxKeyLocator` encapsulates the information and/or data necessary to retrieve a `PARCKey`. + * + * There are at least two ways in which a `PARCKey` can be instantiated: + * + * (1) By embedding and subsequently extracting the raw key data in a message, and + * (2) By specifying a link for a key so that an interest can be issued to obtain the key content + * + * A key locator encapsulates both methods. The API provides functions to create key locators + * for each key retrieval type and use them to retrieve `PARCKey` methods. + * + */ +#ifndef libccnx_ccnx_KeyLocator_h +#define libccnx_ccnx_KeyLocator_h + +#include <stdbool.h> + +#include <ccnx/common/ccnx_Link.h> + +#include <parc/security/parc_Key.h> + +/** + * @typedef CCNxKeyLocatorType + * @brief Locator types for finding keys. + */ +typedef enum { + CCNxKeyLocatorType_None = 0, + CCNxKeyLocatorType_Link = 1, + CCNxKeyLocatorType_Key = 2 +} CCNxKeyLocatorType; + +struct ccnx_key_locator; + +/** + * @typedef CCNxKeyLocator + * @brief A `CCNxKeyLocator` encapsulates the information and/or data necessary to retrieve a {@link PARCKey}. + */ +typedef struct ccnx_key_locator CCNxKeyLocator; + +/** + * Create a `CCNxKeyLocator` instance from a {@link PARCKey} instance. + * + * @param [in] key The `PARCKey` instance which is used to construct the `CCNxKeyLocator` instance. + * + * @return `CCNxKeyLocator` A new `CCNxKeyLocator` instance. + * + * Example: + * @code + * { + * PARCKey *key = ... + * CCNxKeyLocator *keyLocator = ccnxKeyLocator_CreateFromKey(key); + * // use the keyLocator instance + * } + * @endcode + */ +CCNxKeyLocator *ccnxKeyLocator_CreateFromKey(PARCKey *key); + +/** + * Create a `CCNxKeyLocator` instance from a {@link CCNxName} instance. + * + * @param [in] keyLink The `CCNxLink` instance used to create the `CCNxKeyLocator` instance + * + * @return `CCNxKeyLocator` A new `CCNxKeyLocator` instance. + * + * Example: + * @code + * { + * CCNxName *name = ccnxName_CreateFromCString("lci//name"); + * CCNxLink *keyURILink = ccnxLink_Create(name, NULL, NULL); + * + * CCNxKeyLocator *keyLocator = ccnxKeyLocator_CreateFromKeyLink(keyURILink); + * // use the keyLocator instance + * } + * @endcode + */ +CCNxKeyLocator *ccnxKeyLocator_CreateFromKeyLink(CCNxLink *keyLink); + +/** + * Retrieve the {@link CCNxKeyLocatorType} associated with this `CCNxKeyLocator`. + * + * The locator type will specify one of the three methods used to obtain a + * PARCKey: (1) embedded keys, (2) certificates, or (3) key (content) names. + * + * @param [in] keyLocator The `CCNxKeyLocator` instance from which the location + * type is retrieved. + * + * @return A `CCNxKeyLocatorType` value. + * + * Example: + * @code + * { + * CCNxName *name = ccnxLink_Create("lci//name"); + * CCNxLink *keyURILink = ccnxName_CreateFromCString(name, NULL, NULL); + * + * CCNxKeyLocator *keyLocator = ccnxKeyLocator_CreateFromKeyLink(keyURILink); + * CCNxKeyLocatorType locatorType = ccnxKeyLocator_GetType(keyLocator); + * // use the locator type + * } + * @endcode + */ +CCNxKeyLocatorType ccnxKeyLocator_GetType(const CCNxKeyLocator *keyLocator); + +/** + * Determine if the key locator type is type `CCNxKeyLocatorType_Key`. + * + * @param [in] keyLocator `CCNxKeyLocator` instance being examined. + * + * @return true If the key locator type is type `CCNxKeyLocatorType_Key` + * @return false Otherwise + * + * Example: + * @code + * { + * ... + * PARCKey *key = ... + * CCNxKeyLocator *keyLocator = ccnxKeyLocator_CreateFromKey(key); + * bool isKeyType = ccnxKeyLocator_IsKey(keyLocator); + * // isKeyType will be true + * } + * @endcode + */ +bool ccnxKeyLocator_IsKey(const CCNxKeyLocator *keyLocator); + +/** + * Determine if the key locator type is type `CCNxKeyLocatorType_Link`. + * + * @param [in] keyLocator `CCNxKeyLocator` instance being examined. + * + * @return true If the key locator type is type `CCNxKeyLocatorType_Link` + * @return false Otherwise + * + * Example: + * @code + * { + * ... + * CCNxName *name = ccnxName_CreateFromCString("lci:/name"); + * CCNxLink *keyURILink = ccnxLink_Create(name, NULL, NULL); + * CCNxKeyLocator *keyLocator = ccnxKeyLocator_CreateFromKeyLink(keyURILink); + * bool isKeyNameType = ccnxKeyLocator_IsKeyLink(keyLocator); + * // isKeyNameType will be true + * } + * @endcode + */ +bool ccnxKeyLocator_IsKeyLink(const CCNxKeyLocator *keyLocator); + +/** + * Produce a null-terminated C-string representation of the specified instance. + * + * The non-null result must be freed by the caller via {@link parcMemory_Deallocate}. + * + * @param [in] instance A pointer to the instance. + * + * @return NULL Memory could not be allocated. + * @return non-NULL A null-terminated string that must be deallocated via `parcMemory_Deallocate`. + * + * Example: + * @code + * { + * ... + * CCNxName *name = ccnxName_CreateFromCString("lci:/name"); + * CCNxKeyLocator *keyLocator = ccnxKeyLocator_FromKeyName(name); + * char *stringRep = ccnxKeyLocator_ToString(keyLocator); + * } + * @endcode + */ +char *ccnxKeyLocator_ToString(const CCNxKeyLocator *instance); + +/** + * Retrieve the {@link CCNxLink} instance from the specified `CCNxKeyLocator` instance. + * + * The type of the key locator must be `CCNxKeyLocatorType_Link` prior to invocation, + * else a trap will be invoked. + * + * @param [in] keyLocator The `CCNxKeyLocator` from which the name is extracted. + * + * @return A non-NULL `CCNxName` instance. + * + * Example: + * @code + * { + * CCNxName *name = ccnxName_Create("lci//name"); + * CCNxLink *keyURILink = ccnxLink_CreateFromURI(name, NULL, NULL); + * + * CCNxKeyLocator *keyLocator = ccnxKeyLocator_CreateFromKeyLink(keyURILink); + * CCNxLink *copy = ccnxKeyLocator_GetKeyLink(keyLocator); + * // use the link copy + * } + * @endcode + */ +CCNxLink *ccnxKeyLocator_GetKeyLink(const CCNxKeyLocator *keyLocator); + +/** + * Retrieve the {@link PARCKey} instance from the specified `CCNxKeyLocator` instance. + * + * The type of the key locator must be `CCNxKeyLocatorType_Key` prior to invocation, + * else a trap will be invoked. + * + * @param [in] keyLocator The `CCNxKeyLocator` from which the key is extracted. + * + * @return A non-NULL `PARCKey` instance. + * + * Example: + * @code + * { + * PARCKey *key = ...; + * CCNxKeyLocator *keyLocator = ccnxKeyLocator_FromKey(key); + * CCNxKey *copy = ccnxKeyLocator_GetKey(keyLocator); + * // use the key copy + * } + * @endcode + */ +PARCKey *ccnxKeyLocator_GetKey(const CCNxKeyLocator *keyLocator); + +/** + * Determine if two `CCNxKeyLocators` are equal. + * + * The following equivalence relations on non-null `CCNxKeyLocator` instances are maintained: + * + * * It is reflexive: for any non-null reference value x, `ccnxKeyLocator_Equals(x, x)` must return true. + * + * * It is symmetric: for any non-null reference values x and y, `ccnxKeyLocator_Equals(x, y)` must return true if and only if + * `ccnxKeyLocator_Equals(y x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `ccnxKeyLocator_Equals(x, y)` returns true and + * `ccnxKeyLocator_Equals(y, z)` returns true, + * then `ccnxKeyLocator_Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple invocations of `ccnxKeyLocator_Equals(x, y)` + * consistently return true or consistently return false. + * + * * For any non-null reference value x, `ccnxKeyLocator_Equals(x, NULL)` must return false. + * + * @param a A pointer to a `CCNxKeyLocator` instance. + * @param b A pointer to a `CCNxKeyLocator` instance. + * @return True if the referenced `CCNxKeyLocators` are equal. + * + * Example: + * @code + * { + * CCNxName *name1 = ccnxLink_Create("lci//name"); + * CCNxLink *keyURILink1 = ccnxName_CreateFromCString(name1, NULL, NULL); + * + * CCNxKeyLocator *keyLocator1 = ccnxKeyLocator_FromKeyName(keyURILink1); + * + * CCNxName *name2 = ccnxLink_Create("lci//name"); + * CCNxLink *keyURILink2 = ccnxName_CreateFromCString(name2, NULL, NULL); + * + * CCNxKeyLocator *keyLocator2 = ccnxKeyLocator_FromKeyName(keyURILink2); + * + * if (ccnxKeyLocator_Equals(keyLocator1, keyLocator2)) { + * // true + * } else { + * // false + * } + * } + * @endcode + */ +bool ccnxKeyLocator_Equals(const CCNxKeyLocator *a, const CCNxKeyLocator *b); + +/** + * Create a copy of the given `CCNxKeyLocator` instance. + * + * This creates a deep copy of the `CCNxKeyLocator` instance, acquiring handles to internal object + * references when needed. + * + * @param [in] original The `CCNxKeyLocator` instance which is being copied + * + * @return `CCNxKeyLocator` A copy of the specified `CCNxKeyLocator` + * + * Example: + * @code + * { + * ... + * CCNxName *keyURIName = ccnxName_CreateFromCString("lci://name"); + * CCNxKeyLocator *keyLocator = ccnxKeyLocator_FromKeyName(keyURIName); + * CCNxKeyLocator *copy = ccnxKeyLocator_Copy(keyLocator); + * // use the copied instance + * } + * @endcode + */ +CCNxKeyLocator *ccnxKeyLocator_Copy(const CCNxKeyLocator *original); + +/** + * Increase the number of references to a `CCNxKeyLocator`. + * + * Note that new `CCNxKeyLocator` is not created, + * only that the given `CCNxKeyLocator` reference count is incremented. + * Discard the reference by invoking {@link ccnxKeyLocator_Release}. + * + * @param instance A pointer to the original instance. + * @return The value of the input parameter @p instance. + * + * Example: + * @code + * { + * ... + * + * CCNxKeyLocator *keyLocator = ccnxKeyLocator_Acquire(instance); + * + * ccnxKeyLocator_Release(&keyLocator); + * + * } + * @endcode + * + * @see `ccnxKeyLocator_Release` + */ +CCNxKeyLocator *ccnxKeyLocator_Acquire(const CCNxKeyLocator *instance); + +/** + * Release a previously acquired reference to the specified instance, + * decrementing the reference count for the instance. + * + * The pointer to the instance is set to NULL as a side-effect of this function. + * + * If the invocation causes the last reference to the instance to be released, + * the instance is deallocated and the instance's implementation will perform + * additional cleanup and release other privately held references. + * + * @param [in,out] object A pointer to a pointer to the instance to release. + * + * + * Example: + * @code + * { + * ... + * + * CCNxKeyLocator *cert = ccnxKeyLocator_Acquire(instance); + * + * ccnxKeyLocator_Release(&cert); + * + * } + * @endcode + */ +void ccnxKeyLocator_Release(CCNxKeyLocator **object); + +#ifdef Libccnx_DISABLE_VALIDATION +# define ccnxKeyLocator_OptionalAssertValid(_instance_) +#else +# define ccnxKeyLocator_OptionalAssertValid(_instance_) ccnxKeyLocator_AssertValid(_instance_) +#endif +/** + * Check that the pointer to the `KeyLocator` is valid. It should be non-null, + * and any required referenced data should be valid. + * + * @param [in] object A pointer to the instance to check. + * + * Example: + * @code + * { + * ... + * + * ccnxKeyLocator_AssertValid(keyLocator); + * + * } + * @endcode + */ +void ccnxKeyLocator_AssertValid(const CCNxKeyLocator *object); +#endif // libccnx_ccnx_KeyLocator_h diff --git a/libccnx-common/ccnx/common/ccnx_KeystoreUtilities.c b/libccnx-common/ccnx/common/ccnx_KeystoreUtilities.c new file mode 100755 index 00000000..1ae94ef6 --- /dev/null +++ b/libccnx-common/ccnx/common/ccnx_KeystoreUtilities.c @@ -0,0 +1,308 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <getopt.h> +#include <string.h> + +#include <pwd.h> +#include <unistd.h> + +#include <sys/types.h> +#include <pwd.h> +#include <sys/stat.h> + +#include <sys/param.h> + +#include <LongBow/runtime.h> + +#include <parc/algol/parc_Memory.h> +#include <ccnx/common/ccnx_KeystoreUtilities.h> +#include <parc/security/parc_Pkcs12KeyStore.h> +#include <parc/security/parc_PublicKeySigner.h> +#include <parc/security/parc_Signer.h> + + +struct keystore_params { + char filename[1024]; + char password[1024]; + PARCSigner *signer; +}; + +#define OPT_KEYSTORE 'k' +#define OPT_PASSWORD 'p' +#define OPT_BITS 'b' +#define OPT_DAYS 'y' + +static const char *emptyPassword = ""; + +static char * +ccnxKeystoreUtilities_ConstructPath(const char *dir, const char *file) +{ + char *path = parcMemory_AllocateAndClear(MAXPATHLEN); + assertNotNull(path, "parcMemory_AllocateAndClear(%u) returned NULL", MAXPATHLEN); + strncpy(path, dir, MAXPATHLEN); + strncat(path, "/", MAXPATHLEN); + strncat(path, file, MAXPATHLEN); + return path; +} + +static char * +ccnxKeystoreUtilities_HomeDirectoryFromEnv() +{ + char *home = getenv("HOME"); + if (home != NULL) { + home = parcMemory_StringDuplicate(home, strlen(home) + 1); + } + return home; +} + +static char * +ccnxKeystoreUtilities_HomeDirectoryFromPasswd() +{ + struct passwd *pw = getpwuid(getuid()); + const char *homedir = pw->pw_dir; + return parcMemory_StringDuplicate(homedir, strlen(homedir) + 1); +} + +static KeystoreParams * +ccnxKeystoreUtilities_OpenFromPath(const char *path, const char *password) +{ + KeystoreParams *params = NULL; + + // If the file exists, try to open it as a keystore + struct stat filestat; + int failure = stat(path, &filestat); + if (!failure) { + PARCPkcs12KeyStore *keyStore = parcPkcs12KeyStore_Open(path, password, PARCCryptoHashType_SHA256); + PARCKeyStore *publicKeyStore = parcKeyStore_Create(keyStore, PARCPkcs12KeyStoreAsKeyStore); + parcPkcs12KeyStore_Release(&keyStore); + PARCPublicKeySigner *pksigner = parcPublicKeySigner_Create(publicKeyStore, PARCSigningAlgorithm_RSA, PARCCryptoHashType_SHA256); + PARCSigner *signer = parcSigner_Create(pksigner, PARCPublicKeySignerAsSigner); + parcPublicKeySigner_Release(&pksigner); + + if (signer) { + params = ccnxKeystoreUtilities_Create(signer, path, password); + + parcSigner_Release(&signer); + parcKeyStore_Release(&publicKeyStore); + } + } + + return params; +} + +static KeystoreParams * +ccnxKeystoreUtilities_CreateInPath(const char *path, const char *password, int keystoreBits, int keystoreDays) +{ + KeystoreParams *params = NULL; + + bool success = parcPkcs12KeyStore_CreateFile(path, password, "ccnxuser", keystoreBits, keystoreDays); + if (success) { + PARCPkcs12KeyStore *keyStore = parcPkcs12KeyStore_Open(path, password, PARCCryptoHashType_SHA256); + PARCKeyStore *publicKeyStore = parcKeyStore_Create(keyStore, PARCPkcs12KeyStoreAsKeyStore); + parcPkcs12KeyStore_Release(&keyStore); + PARCPublicKeySigner *pksigner = parcPublicKeySigner_Create(publicKeyStore, PARCSigningAlgorithm_RSA, PARCCryptoHashType_SHA256); + PARCSigner *signer = parcSigner_Create(pksigner, PARCPublicKeySignerAsSigner); + parcPublicKeySigner_Release(&pksigner); + + if (signer) { + params = ccnxKeystoreUtilities_Create(signer, path, password); + + parcSigner_Release(&signer); + parcKeyStore_Release(&publicKeyStore); + } + } + return params; +} + + +static char * +ccnxKeystoreUtilities_GetHomeDirectory() +{ + char *homedir = ccnxKeystoreUtilities_HomeDirectoryFromEnv(); + if (homedir == NULL) { + homedir = ccnxKeystoreUtilities_HomeDirectoryFromPasswd(); + } + return homedir; +} + +static KeystoreParams * +ccnxKeystoreUtilities_OpenFromHomeDirectory(const char *password) +{ + KeystoreParams *params = NULL; + + char *homedir = ccnxKeystoreUtilities_GetHomeDirectory(); + char *ccnxdir = ccnxKeystoreUtilities_ConstructPath(homedir, ".ccnx"); + + + char *path = ccnxKeystoreUtilities_ConstructPath(ccnxdir, ".ccnx_keystore.p12"); + params = ccnxKeystoreUtilities_OpenFromPath(path, password); + parcMemory_Deallocate((void **) &path); + + if (params == NULL) { + // try the older filename + char *path = ccnxKeystoreUtilities_ConstructPath(ccnxdir, ".ccnx_keystore"); + params = ccnxKeystoreUtilities_OpenFromPath(path, password); + parcMemory_Deallocate((void **) &path); + } + + parcMemory_Deallocate((void **) &ccnxdir); + parcMemory_Deallocate((void **) &homedir); + return params; +} + +static KeystoreParams * +ccnxKeystoreUtilities_CreateInHomeDirectory(const char *keystorePassword, int keystoreBits, int keystoreDays) +{ + char *homedir = ccnxKeystoreUtilities_GetHomeDirectory(); + char *ccnxdir = ccnxKeystoreUtilities_ConstructPath(homedir, ".ccnx"); + // Needs to check the return value to ensure that it was created. See case 1003. + mkdir(ccnxdir, 0700); + + char *path = ccnxKeystoreUtilities_ConstructPath(ccnxdir, ".ccnx_keystore.p12"); + KeystoreParams *params = ccnxKeystoreUtilities_CreateInPath(path, keystorePassword, keystoreBits, keystoreDays); + + parcMemory_Deallocate((void **) &path); + parcMemory_Deallocate((void **) &ccnxdir); + parcMemory_Deallocate((void **) &homedir); + return params; +} + +#ifdef _ANDROID_ +static char * +getpass(const char *prompt) +{ + trapNotImplemented("getpass() is not implemented on Android. See BugzId: 3864"); +} +#endif + +/** + * Read a password, then zero the static memory + * + * <#Discussion#> + * + * @param <#param1#> + * @return An allocated string, use <code>parcMemory_Deallocate()</code> on it. + * + * Example: + * @code + * <#example#> + * @endcode + */ +static char * +_readPassword(const char *prompt) +{ + char *staticBuffer = getpass(prompt); + char *password = parcMemory_StringDuplicate(staticBuffer, strlen(staticBuffer) + 1); + memset(staticBuffer, 0, strlen(staticBuffer)); + return password; +} + +KeystoreParams * +ccnxKeystoreUtilities_OpenFile(const char *keystoreFile, const char *keystorePassword) +{ + if (keystorePassword == NULL) { + keystorePassword = emptyPassword; + } + + KeystoreParams *params = NULL; + + if (keystoreFile == NULL) { + params = ccnxKeystoreUtilities_OpenFromHomeDirectory(keystorePassword); + } else { + params = ccnxKeystoreUtilities_OpenFromPath(keystoreFile, keystorePassword); + } + + return params; +} + +KeystoreParams * +ccnxKeystoreUtilities_CreateFile(const char *keystoreFile, const char *keystorePassword, int keystoreBits, int keystoreDays) +{ + if (keystorePassword == NULL) { + keystorePassword = emptyPassword; + } + + KeystoreParams *params = NULL; + + if (keystoreFile == NULL) { + params = ccnxKeystoreUtilities_CreateInHomeDirectory(keystorePassword, keystoreBits, keystoreDays); + } else { + params = ccnxKeystoreUtilities_CreateInPath(keystoreFile, keystorePassword, keystoreBits, keystoreDays); + } + + return params; +} + +KeystoreParams * +ccnxKeystoreUtilities_Create(PARCSigner *signer, const char *path, const char *password) +{ + KeystoreParams *params = parcMemory_AllocateAndClear(sizeof(KeystoreParams)); + assertNotNull(params, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(KeystoreParams)); + params->signer = parcSigner_Acquire(signer); + strncpy(params->filename, path, 1024); + strncpy(params->password, password, 1024); + return params; +} + +void +keystoreParams_Destroy(KeystoreParams **paramsPtr) +{ + KeystoreParams *params = *paramsPtr; + if (params->signer != NULL) { + parcSigner_Release(¶ms->signer); + } + parcMemory_Deallocate((void **) ¶ms); + *paramsPtr = NULL; +} + +char * +ccnxKeystoreUtilities_ReadPassword(void) +{ + return _readPassword("Password: "); +} + +bool +ccnxKeystoreUtilities_ConfirmPassword(const char *mustEqualPassword) +{ + bool equals = false; + + char *b = _readPassword("Confirm : "); + if (strcmp(mustEqualPassword, b) == 0) { + equals = true; + } + + memset(b, 0, strlen(b)); + parcMemory_Deallocate((void **) &b); + return equals; +} + +const char * +ccnxKeystoreUtilities_GetFileName(const KeystoreParams *params) +{ + return params->filename; +} + +const char * +ccnxKeystoreUtilities_GetPassword(const KeystoreParams *params) +{ + return params->password; +} diff --git a/libccnx-common/ccnx/common/ccnx_KeystoreUtilities.h b/libccnx-common/ccnx/common/ccnx_KeystoreUtilities.h new file mode 100755 index 00000000..20d733cd --- /dev/null +++ b/libccnx-common/ccnx/common/ccnx_KeystoreUtilities.h @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file ccnx_KeystoreUtilities.h + * @ingroup Signature + * @brief A set of tools for working with the CCNx keystore. + * + */ +#ifndef libccnx_ccnx_KeystoreUtilities_h +#define libccnx_ccnx_KeystoreUtilities_h + +#include <parc/security/parc_Signer.h> + +struct keystore_params; +/** + * @typedef KeystoreParams + * @brief Parameters for the KeyStore. + */ + +typedef struct keystore_params KeystoreParams; + +/** + * Create a new `KeystoreParams` from a @p path, @p password, and a {@link PARCSigner}. + * + * @param [in] signer A pointer to an instance of `PARCSigner`. + * @param [in] path The path to use. + * @param [in] password The password to use. + * + * @return A pointer to a new instance of `KeystoreParams`. + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + * + */ +KeystoreParams *ccnxKeystoreUtilities_Create(PARCSigner *signer, const char *path, const char *password); + +/** + * Destroy the `KeystoreParams`. + * + * @param [in,out] paramsPtr A pointer to the pointer to the `KeystoreParams` to destroy. + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + * + */ +void keystoreParams_Destroy(KeystoreParams **paramsPtr); + +/** + * Opens a PKCS12 keystore for use with CCNx, or creates it if missing. + * + * All options may be NULL. + * keystoreFile is the filename and path to use. If the option is omitted, then the default location is used. + * The default location is in ~/.ccnx/.ccnx_keystore.p12, which is a PKCS12 keystore. For compatability + * with older implementations, will also look for ~/.ccnx/.ccnx_keystore without the file extension. + * keystorePassword is the password to use. If missing, will prompt with getpass(3). + * + * This function uses the equivalent of getopt_long(3). It does not change the argv. + * + * @param [in] keystoreFile The full path to the keystore, may be NULL to use ~/.ccnx/.ccnx_keystore.p12 + * @param [in] keystorePassword The keystore password, may be NULL for no password. + * @return The `KeystoreParams`, including the path used, password used, and the `PARCSigner`, NULL if cannot be opened. + * + * Example: + * @code + * <#example#> + * @endcode + */ +KeystoreParams *ccnxKeystoreUtilities_OpenFile(const char *keystoreFile, const char *keystorePassword); + +/** + * Creates a PKCS12 keystore. + * + * @param [in] keystoreFile may be NULL to use the default location + * @param [in] keystorePassword The keystore password, may be NULL for no password. + * @param [in] keystoreBits The keystore bits, may be NULL for no password. + * @param [in] keystoreDays The keystore days, may be NULL for no password. + * @return The keystore parameters, including the path used, password used, and the `PARCSigner`, NULL if cannot be created. + * + * Example: + * @code + * <#example#> + * @endcode + */ +KeystoreParams *ccnxKeystoreUtilities_CreateFile(const char *keystoreFile, const char *keystorePassword, int keystoreBits, int keystoreDays); + +/** + * Returns an allocated buffer with password + * + * Reads a password from stdin, then scrubs the static memory + * + * @return Free with {@link parcMemory_Deallocate()} + * + * Example: + * @code + * <#example#> + * @endcode + */ +char *ccnxKeystoreUtilities_ReadPassword(void); + +/** + * Returns an allocated buffer with password + * + * Reads a password from stdin, then scrubs the static memory. + * Confirms that it equals the provided password. + * + * @param [in] mustEqualPassword The password that must match. + * + * @return `true` if the password from stdin matches. + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool ccnxKeystoreUtilities_ConfirmPassword(const char *mustEqualPassword); + +/** + * Get the file name from the given {@link KeystoreParams} instance. + * + * @param [in] params A pointer to a valid `KeystoreParams` instance. + * + * @return A pointer to a null-terminated C string containing the file name. + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + * + */ +const char *ccnxKeystoreUtilities_GetFileName(const KeystoreParams *params); + +/** + * Get the password from the given `KeyStoreParams` instance. + * + * @param [in] params A pointer to a valid `KeystoreParams` instance. + * + * @return A pointer to a null-terminated C string containing the password. + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + * + */ +const char *ccnxKeystoreUtilities_GetPassword(const KeystoreParams *params); +#endif // libccnx_ccnx_KeystoreUtilities_h diff --git a/libccnx-common/ccnx/common/ccnx_Link.c b/libccnx-common/ccnx/common/ccnx_Link.c new file mode 100755 index 00000000..ce1ee68f --- /dev/null +++ b/libccnx-common/ccnx/common/ccnx_Link.c @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ +#include <config.h> + +#include <LongBow/runtime.h> + +#include <stdio.h> +#include <string.h> + +#include <ccnx/common/ccnx_Link.h> + +#include <parc/algol/parc_Object.h> + +struct ccnx_link { + const CCNxName *name; + PARCBuffer *keyId; + PARCBuffer *contentHash; +}; + +static void +_destroy(CCNxLink **linkP) +{ + ccnxName_Release((CCNxName **) &(*linkP)->name); + if ((*linkP)->keyId != NULL) { + parcBuffer_Release(&(*linkP)->keyId); + } + if ((*linkP)->contentHash != NULL) { + parcBuffer_Release(&(*linkP)->contentHash); + } +} + +parcObject_ExtendPARCObject(CCNxLink, _destroy, NULL, ccnxLink_ToString, ccnxLink_Equals, NULL, NULL, NULL); + +parcObject_ImplementAcquire(ccnxLink, CCNxLink); + +parcObject_ImplementRelease(ccnxLink, CCNxLink); + +CCNxLink * +ccnxLink_Create(const CCNxName *name, PARCBuffer *keyId, PARCBuffer *contentObjectHash) +{ + CCNxLink *link = parcObject_CreateInstance(CCNxLink); + + if (link != NULL) { + link->name = ccnxName_Acquire(name); + link->keyId = (keyId == NULL) ? NULL : parcBuffer_Acquire(keyId); + link->contentHash = (contentObjectHash == NULL) ? NULL : parcBuffer_Acquire(contentObjectHash); + } + + return link; +} + +CCNxLink * +ccnxLink_Copy(const CCNxLink *original) +{ + CCNxLink *link = parcObject_CreateInstance(CCNxLink); + + if (link != NULL) { + link->name = ccnxName_Copy(original->name); + link->keyId = (original->keyId == NULL) ? NULL : parcBuffer_Copy(original->keyId); + link->contentHash = (original->contentHash == NULL) ? NULL : parcBuffer_Copy(original->contentHash); + } + + return link; +} + +const CCNxName * +ccnxLink_GetName(const CCNxLink *link) +{ + return link->name; +} + +PARCBuffer * +ccnxLink_GetKeyID(const CCNxLink *link) +{ + return link->keyId; +} + +PARCBuffer * +ccnxLink_GetContentObjectHash(const CCNxLink *link) +{ + return link->contentHash; +} + +bool +ccnxLink_Equals(const CCNxLink *objectA, const CCNxLink *objectB) +{ + bool result = false; + + if (objectA == objectB) { + result = true; + } else if (objectA != NULL && objectB != NULL) { + if (ccnxName_Equals(objectA->name, objectB->name)) { + if (parcBuffer_Equals(objectA->keyId, objectB->keyId)) { + if (parcBuffer_Equals(objectA->contentHash, objectB->contentHash)) { + return true; + } + } + } + } + return result; +} + +char * +ccnxLink_ToString(const CCNxLink *link) +{ + char *nameString = ccnxName_ToString(link->name); + char *keyIdString = "NULL"; + bool nullKeyIdString = true; + + if (link->keyId != NULL) { + keyIdString = parcBuffer_ToString(link->keyId); + nullKeyIdString = false; + } + + char *contentObjectHashString = NULL; + bool nullContentObjectHashString = true; + if (link->contentHash != NULL) { + contentObjectHashString = parcBuffer_ToString(link->contentHash); + nullContentObjectHashString = false; + } + + char *string; + int failure = asprintf(&string, "CCNxLink { .name=\"%s\", .KeyID=\"%s\", .ContentObjectHash=\"%s\" }", + nameString, + keyIdString, + contentObjectHashString); + assertTrue(failure > -1, "Error asprintf"); + + parcMemory_Deallocate((void **) &nameString); + if (!nullKeyIdString) { + parcMemory_Deallocate((void **) &keyIdString); + } + if (!nullContentObjectHashString) { + parcMemory_Deallocate((void **) &contentObjectHashString); + } + + char *result = parcMemory_StringDuplicate(string, strlen(string)); + free(string); + + return result; +} + +bool +ccnxLink_IsValid(const CCNxLink *link) +{ + bool result = false; + + if (link != NULL) { + if (ccnxName_IsValid(link->name)) { + if (link->keyId == NULL || parcBuffer_IsValid(link->keyId)) { + if (link->contentHash == NULL || parcBuffer_IsValid(link->contentHash)) { + result = true; + } + } + } + } + + return result; +} + +void +ccnxLink_AssertValid(const CCNxLink *link) +{ + assertTrue(ccnxLink_IsValid(link), "CCNxLink instance is not valid."); +} diff --git a/libccnx-common/ccnx/common/ccnx_Link.h b/libccnx-common/ccnx/common/ccnx_Link.h new file mode 100755 index 00000000..cd2b62ff --- /dev/null +++ b/libccnx-common/ccnx/common/ccnx_Link.h @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file ccnx_Link.h + * @ingroup ContentObject + * @brief A generic CCNx link that contains a hash, name, KeyID. + * + * + * Example: + * @code + * <#example#> + * @endcode + */ +#ifndef libccnx_ccnx_Link_h +#define libccnx_ccnx_Link_h + +#include <ccnx/common/ccnx_Name.h> + +#include <parc/security/parc_Signature.h> +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Buffer.h> + +struct ccnx_link; + +/** + * @typedef CCNxLink + * @brief A generic CCNx link that contains a hash, name, KeyID. + * @see {@link ccnxLink_Create} + */ +typedef struct ccnx_link CCNxLink; + +/** + * Create a new `CCNxLink` instance. + * + * @param [in] name The {@link CCNxName} for the new `CCNxLink`. + * @param [in] keyId A {@link PARCBuffer} containing the KeyID for the new `CCNxLink`. + * @param [in] contentObjectHash A `PARCBuffer` containing the Content Object Hash for the new `CCNxLink`. + * @return A pointer to a new `CCNxLink` instance, or NULL if an error or out of memory. + * + * Example: + * @code + * <#example#> + * @endcode + */ +CCNxLink *ccnxLink_Create(const CCNxName *name, PARCBuffer *keyId, PARCBuffer *contentObjectHash); + +/** + * Create a new `CCNxLink` instance as a copy of the original. + * + * @param [in] original The {@link CCNxLink} to copy. + * @return A pointer to a new `CCNxLink` instance, or NULL if an error or out of memory. + * + * Example: + * @code + * <#example#> + * @endcode + */ +CCNxLink *ccnxLink_Copy(const CCNxLink *original); + +/** + * Fetch the name associated with this `CCNxLink`. + * + * @param [in] link A pointer to a `CCNxLink` instance + * @return The {@link CCNxName} associated with the @p link. + * + * Example: + * @code + * <#example#> + * @endcode + */ +const CCNxName *ccnxLink_GetName(const CCNxLink *link); + +/** + * Fetch the KeyID associated with this `CCNxLink`. + * + * @param [in] link A pointer to a `CCNxLink` instance + * @return a {@link PARCBuffer} containing the KeyID associated with the @p link. + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCBuffer *ccnxLink_GetKeyID(const CCNxLink *link); + +/** + * Fetch the ContentObjectHash associated with this `CCNxLink`. + * + * @param [in] link A pointer to a `CCNxLink` instance + * @return a {@link PARCBuffer} containing the Content Object Hash associated with the @p link. + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCBuffer *ccnxLink_GetContentObjectHash(const CCNxLink *link); + +/** + * Determine if two `CCNxLink` instances are equal. + * + * The following equivalence relations on non-null `CCNxLink` instances are maintained: + * + * * It is reflexive: for any non-null reference value x, `ccnxLink_Equals(x, x)` must return true. + * + * * It is symmetric: for any non-null reference values x and y, `ccnxLink_Equals(x, y)` must return true if and only if + * `ccnxLink_Equals(y x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `ccnxLink_Equals(x, y)` returns true and + * `ccnxLink_Equals(y, z)` returns true, + * then `ccnxLink_Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple invocations of `ccnxLink_Equals(x, y)` + * consistently return true or consistently return false. + * + * * For any non-null reference value x, `ccnxLink_Equals(x, NULL)` must return false. + * + * + * @param [in] x A pointer to a `CCNxLink` instance. + * @param [in] y A pointer to a `CCNxLink` instance. + * @return `true` if the referenced `CCNxLink` instances are equal. + * + * Example: + * @code + * { + * CCNxLink *a = ccnxLink_Create(); + * CCNxLink *b = ccnxLink_Create(); + * + * if (ccnxLink_Equals(a, b)) { + * // true + * } else { + * // false + * } + * } + * @endcode + */ +bool ccnxLink_Equals(const CCNxLink *x, const CCNxLink *y); + +/** + * Create a null-terminated string representation of the given `CCNxLink`. + * + * The returned value must be freed by the caller using {@link parcMemory_Deallocate()}. + * + * @param [in] link A pointer to a `CCNxLink` instance. + * @return A pointer to null-terminated string of characters that must be freed by the caller by `parcMemory_Deallocate()`. + * + * Example: + * @code + * <#example#> + * @endcode + */ +char *ccnxLink_ToString(const CCNxLink *link); + +/** + * Increase the number of references to an instance of this object. + * + * Note that new instance is not created, + * only that the given instance's reference count is incremented. + * Discard the reference by invoking {@link ccnxLink_Release()}. + * + * @param [in] link A pointer to a `CCNxLink` instance to acquire. + * @return The value of the input parameter @p link. + * + * Example: + * @code + * { + * ... + * + * CCNxLink *link = ccnxLink_Acquire(instance); + * + * ccnxLink_Release(&link); + * + * } + * @endcode + * + * @see ccnxLink_Release + */ +CCNxLink *ccnxLink_Acquire(const CCNxLink *link); + +/** + * Release a previously acquired reference to the specified instance, + * decrementing the reference count for the instance. + * + * The pointer to the instance is set to NULL as a side-effect of this function. + * + * If the invocation causes the last reference to the instance to be released, + * the instance is deallocated and the instance's implementation will perform + * additional cleanup and release other privately held references. + * + * @param [in,out] linkP A pointer to a pointer to the instance to release. + * + * + * Example: + * @code + * { + * ... + * + * CCNxLink *link = ccnxLink_Acquire(instance); + * + * ccnxLink_Release(&link); + * + * } + * @endcode + */ +void ccnxLink_Release(CCNxLink **linkP); + +/** + * Check that the pointer to the `CCNxLink` is valid. It should be non-null, + * and any referenced data should also be valid. + * + * @param [in] link A pointer to a `CCNxLink` instance + * + * + * Example: + * @code + * { + * ... + * + * CCNxLink *link = ccnxLink_Acquire(instance); + * + * ccnxLink_AssertValid(link); + * + * } + * @endcode + */ +void ccnxLink_AssertValid(const CCNxLink *link); + +/** + * Determine if a `CCNxLink` instance is valid. + * + * @param [in] link A pointer to a `CCNxLink` instance + * @return true The instance is valid. + * @return false The instance is invalid. + * + * + * Example: + * @code + * { + * ... + * + * CCNxLink *link = ccnxLink_Acquire(instance); + * + * ccnxLink_AssertValid(link); + * + * } + * @endcode + */ +bool ccnxLink_IsValid(const CCNxLink *link); +#endif // libccnx_ccnx_Link_h diff --git a/libccnx-common/ccnx/common/ccnx_Manifest.c b/libccnx-common/ccnx/common/ccnx_Manifest.c new file mode 100755 index 00000000..6690e5d7 --- /dev/null +++ b/libccnx-common/ccnx/common/ccnx_Manifest.c @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ +#include <config.h> +#include <stdio.h> +#include <string.h> + +#include <LongBow/runtime.h> + +#include <parc/algol/parc_Object.h> + +#include <ccnx/common/ccnx_Manifest.h> +#include <ccnx/common/ccnx_ContentObject.h> + +static const CCNxManifestInterface *_defaultImplementation = &CCNxManifestFacadeV1_Interface; + +static CCNxManifest * +_ccnxManifest_InternalCreate(const CCNxManifestInterface *impl, const CCNxName *name) +{ + CCNxManifest *result = NULL; + + if (impl->create != NULL) { + result = impl->create(name); + + // And set the dictionary's interface pointer to the one we just used to create this. + ccnxTlvDictionary_SetMessageInterface(result, impl); + } else { + trapNotImplemented("Manifest implementations must implement create()"); + } + + return result; +} + +parcObject_ImplementAcquire(ccnxManifest, CCNxManifest); +parcObject_ImplementRelease(ccnxManifest, CCNxManifest); + +CCNxManifest * +ccnxManifest_Create(const CCNxName *name) +{ + return _ccnxManifest_InternalCreate(_defaultImplementation, name); +} + +CCNxManifest * +ccnxManifest_CreateNameless(void) +{ + return _ccnxManifest_InternalCreate(_defaultImplementation, NULL); +} + +void +ccnxManifest_AddHashGroup(CCNxManifest *manifest, const CCNxManifestHashGroup *group) +{ + CCNxManifestInterface *interface = ccnxManifestInterface_GetInterface(manifest); + interface->addHashGroup(manifest, group); +} + +CCNxManifestHashGroup * +ccnxManifest_GetHashGroupByIndex(const CCNxManifest *manifest, size_t index) +{ + CCNxManifestInterface *interface = ccnxManifestInterface_GetInterface(manifest); + return interface->getHashGroup(manifest, index); +} + +size_t +ccnxManifest_GetNumberOfHashGroups(const CCNxManifest *manifest) +{ + CCNxManifestInterface *impl = ccnxManifestInterface_GetInterface(manifest); + size_t result = 0; + if (impl->getNumberOfHashGroups != NULL) { + result = (impl->getNumberOfHashGroups)(manifest); + } + return result; +} + +const CCNxName * +ccnxManifest_GetName(const CCNxManifest *manifest) +{ + CCNxManifestInterface *impl = ccnxManifestInterface_GetInterface(manifest); + + const CCNxName *result = NULL; + + if (impl->getName != NULL) { + result = impl->getName(manifest); + } else { + trapNotImplemented("ccnxManifest_GetName"); + } + + return result; +} + +bool +ccnxManifest_Equals(const CCNxManifest *objectA, const CCNxManifest *objectB) +{ + if (objectA == objectB) { + return true; + } + if (objectA == NULL || objectB == NULL) { + return false; + } + + CCNxManifestInterface *impl = ccnxManifestInterface_GetInterface(objectA); + return (impl->equals)(objectA, objectB); + + return false; +} + +PARCJSON * +ccnxManifest_ToJSON(const CCNxManifest *manifest) +{ + PARCJSON *root = parcJSON_Create(); + + char *nameString = ccnxName_ToString(ccnxManifest_GetName(manifest)); + parcJSON_AddString(root, "locator", nameString); + parcMemory_Deallocate(&nameString); + + PARCJSONArray *array = parcJSONArray_Create(); + for (size_t i = 0; i < ccnxManifest_GetNumberOfHashGroups(manifest); i++) { + CCNxManifestHashGroup *group = ccnxManifest_GetHashGroupByIndex(manifest, i); + PARCJSON *groupJson = ccnxManifestHashGroup_ToJson(group); + PARCJSONValue *jsonValue = parcJSONValue_CreateFromJSON(groupJson); + + parcJSONArray_AddValue(array, jsonValue); + + parcJSONValue_Release(&jsonValue); + parcJSON_Release(&groupJson); + ccnxManifestHashGroup_Release(&group); + } + parcJSON_AddArray(root, "HashGroups", array); + parcJSONArray_Release(&array); + + return root; +} + +char * +ccnxManifest_ToString(const CCNxManifest *manifest) +{ + PARCJSON *json = ccnxManifest_ToJSON(manifest); + char *string = parcJSON_ToString(json); + parcJSON_Release(&json); + return string; +} + +void +ccnxManifest_AssertValid(const CCNxManifest *manifest) +{ + assertNotNull(manifest, "Non-NULL manifest"); +} + +PARCLinkedList * +ccnxManifest_CreateInterestList(const CCNxManifest *manifest, const CCNxName *locator) +{ + PARCLinkedList *interests = parcLinkedList_Create(); + + for (size_t i = 0; i < ccnxManifest_GetNumberOfHashGroups(manifest); i++) { + CCNxManifestHashGroup *group = ccnxManifest_GetHashGroupByIndex(manifest, i); + + const CCNxName *name = ccnxManifest_GetName(manifest) == NULL ? locator : ccnxManifest_GetName(manifest); + PARCLinkedList *groupInterests = ccnxManifestHashGroup_CreateInterestList(group, name); + parcLinkedList_AppendAll(interests, groupInterests); + parcLinkedList_Release(&groupInterests); + + ccnxManifestHashGroup_Release(&group); + } + + return interests; +} diff --git a/libccnx-common/ccnx/common/ccnx_Manifest.h b/libccnx-common/ccnx/common/ccnx_Manifest.h new file mode 100755 index 00000000..4e3b1076 --- /dev/null +++ b/libccnx-common/ccnx/common/ccnx_Manifest.h @@ -0,0 +1,307 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file ccnx_Manifest.h + * @ingroup ContentObject + * @brief The generic manifest. + * + */ +#ifndef libccnx_ccnx_Manifest_h +#define libccnx_ccnx_Manifest_h + +#include <ccnx/common/ccnx_Name.h> +#include <ccnx/common/ccnx_ManifestHashGroup.h> + +#include <ccnx/common/internal/ccnx_ManifestInterface.h> +#include <ccnx/common/internal/ccnx_TlvDictionary.h> + +#include <parc/security/parc_Signature.h> +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_LinkedList.h> +#include <parc/algol/parc_Buffer.h> + +struct ccnx_manifest; + +/** + * @typedef CCNxManifest + * @brief Structure of the CCNxManifest + */ +typedef CCNxTlvDictionary CCNxManifest; + +/** + * Create a new `CCNxManifest` instance. + * + * @param [in] nameLink A pointer to a `CCNxName` + * @param [in] payload A pointer to a `CCNxManifestSection` + * + * @return A pointer to a `CCNxManifest` instance, or NULL if an error or out of memory. + * + * Example: + * @code + * { + * CCNxName *name = ccnxName_CreateFromURI("lci:/foo/bar/manifest"); + * + * + * CCNxManifest *object = ccnxManifest_Create(name, payload); + * } + * @endcode + */ +CCNxManifest *ccnxManifest_Create(const CCNxName *name); + +/** + * Create a new nameless `CCNxManifest` instance. + * + * @return A pointer to a `CCNxManifest` instance, or NULL if an error or out of memory. + * + * Example: + * @code + * { + * CCNxManifest *object = ccnxManifest_CreateNameless(); + * } + * @endcode + */ +CCNxManifest *ccnxManifest_CreateNameless(void); + +/** + * Increase the number of references to an instance of this object. + * + * Note that new instance is not created, + * only that the given instance's reference count is incremented. + * Discard the reference by invoking {@link ccnxManifest_Release()}. + * + * @param [in] manifest A pointer to the instance of `CCNxManifest` to acquire. + * + * @return The value of the input parameter @p instance. + * + * Example: + * @code + * { + * CCNxManifest *manifest = ccnxManifest_Acquire(instance); + * + * ccnxManifest_Release(&manifest); + * } + * @endcode + * + * @see `ccnxManifest_Release` + */ +CCNxManifest *ccnxManifest_Acquire(const CCNxManifest *manifest); + +/** + * Release a previously acquired reference to the specified instance, + * decrementing the reference count for the instance. + * + * The pointer to the instance is set to NULL as a side-effect of this function. + * + * If the invocation causes the last reference to the instance to be released, + * the instance is deallocated and the instance's implementation will perform + * additional cleanup and release other privately held references. + * + * @param [in,out] manifestP A pointer to a pointer to the instance to release. + * + * Example: + * @code + * { + * CCNxManifest *manifest = ccnxManifest_Acquire(instance); + * + * ccnxManifest_Release(&manifest); + * + * } + * @endcode + */ +void ccnxManifest_Release(CCNxManifest **manifestP); + +/** + * Check that the pointer to the `CCNxManifest` is valid. It should be non-null, + * and any referenced data should also be valid. + * + * @param [in] manifest A pointer to an instance of `CCNxManifest` to check. + * + * Example: + * @code + * { + * CCNxManifest *manifest = ccnxManifest_Acquire(instance); + * + * ccnxManifest_AssertValid(manifest); + * + * } + * @endcode + */ +void ccnxManifest_AssertValid(const CCNxManifest *manifest); + +/** + * Add a HashGroup to the given `CCNxManifest`. + * + * @param [in] manifest A pointer to an instance of `CCNxManifest`. + * @param [in] group A pointer to an instance of `CCNxManifestHashGroup`. + * + * Example: + * @code + * { + * CCNxManifest *manifest = ... + * CCNxManifestHashGroup *group = ...; + * + * ccnxManifest_AddHashGroup(manifest, group); + * } + * @endcode + */ +void ccnxManifest_AddHashGroup(CCNxManifest *manifest, const CCNxManifestHashGroup *group); + +/** + * Get the `CCNxManifestHashGroup` corresponding to the specified index. + * + * @param [in] manifest A pointer to an instance of `CCNxManifest`. + * @param [in] index The index of the `CCNxManifestHashGroup` to retrieve. + * + * @return A pointer to a `CCNxManifestHashGroup`. + * + * Example: + * @code + * { + * CCNxManifest *manifest = ... + * CCNxManifestHashGroup *group = ...; + * + * // Add the first group + * ccnxManifest_AddHashGroup(manifest, group); + * + * CCNxManifestHashGroup *expected = ccnxManifest_GetHashGroup(manifest, 0); + * } + * @endcode + */ +CCNxManifestHashGroup *ccnxManifest_GetHashGroupByIndex(const CCNxManifest *manifest, size_t index); + +/** + * Get the number of {@link CCNxManifestHashGroup} instances in the specified manifest. + * + * @param [in] manifest A pointer to an instance of {@link CCNxManifest}. + * + * @return A pointer to a {@link CCNxManifestHashGroup}. + * + * Example: + * @code + * { + * CCNxManifest *manifest = ... + * CCNxManifestHashGroup *group = ...; + * + * // Add the first group + * ccnxManifest_AddHashGroup(manifest, group); + * + * // Add more groups... + * + * printf("Number of hash groups: %d\n", ccnxManifest_GetNumberOfHashGroups(manifest)); + * } + * @endcode + */ +size_t ccnxManifest_GetNumberOfHashGroups(const CCNxManifest *manifest); + +/** + * Create a list of `CCNxInterest` instances that can be created from this single + * `CCNxManifest` instance. + * + * @param [in] manifest A pointer to an instance of `CCNxManifest`. + * @param [in] name A `CCNxName` locator for the interests in this list. + * + * @return A `PARCLinkedList` containing the set of all Interests that can be + * constructed from this Manifest + * + * Example: + * @code + * { + * CCNxManifest *manifest = ...; + * CCNxName *locator = ...; + * + * PARCLinkedList *interests = ccnxManifest_CreateInterestList(manifest, locator); + * } + * @endcode + */ +PARCLinkedList *ccnxManifest_CreateInterestList(const CCNxManifest *manifest, const CCNxName *name); + +/** + * Get the `CCNxName` for the given `CCNxManifest`. + * + * @param [in] manifest A pointer to an instance of `CCNxManifest`. + * @return A pointer to the `CCNxName`. + * + * Example: + * @code + * { + * CCNxManifest *manifest = ...; + * + * CCNxName *name = ccnxManifest_GetName(manifest); + * } + * @endcode + */ +const CCNxName *ccnxManifest_GetName(const CCNxManifest *manifest); + +/** + * Determine if two `CCNxManifest` instances are equal. + * + * The following equivalence relations on non-null `CCNxManifest` instances are maintained: + * + * * It is reflexive: for any non-null reference value x, `ccnxManifest_Equals(x, x)` must return true. + * + * * It is symmetric: for any non-null reference values x and y, `ccnxManifest_Equals(x, y)` must return true if and only if + * `ccnxManifest_Equals(y x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `ccnxManifest_Equals(x, y)` returns true and + * `ccnxManifest_Equals(y, z)` returns true, + * then `ccnxManifest_Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple invocations of `ccnxManifest_Equals(x, y)` + * consistently return true or consistently return false. + * + * * For any non-null reference value x, `ccnxManifest_Equals(x, NULL)` must return false. + * + * + * @param [in] x A pointer to a `CCNxManifest` instance. + * @param [in] y A pointer to a `CCNxManifest` instance. + * @return `true` if the referenced `CCNxManifest` instances are equal. + * + * Example: + * @code + * { + * CCNxManifest *a = CCNxManifest_Create(...); + * CCNxManifest *b = CCNxManifest_Create(...); + * + * if (CCNxManifest_Equals(a, b)) { + * // true + * } else { + * // false + * } + * } + * @endcode + */ +bool ccnxManifest_Equals(const CCNxManifest *x, const CCNxManifest *y); + +/** + * Create a null-terminated string representation of the given {@link CCNxManifest}. + * + * The returned value must be freed by the caller using {@link parcMemory_Deallocate()}. + * + * @param [in] manifest A pointer to an instance of {@link CCNxManifest}. + * @return A pointer to null-terminated string of characters that must be freed by the caller by `parcMemory_Deallocate()`. + * + * Example: + * @code + * { + * CCNxManifest *manifest = ...; + * + * char *stringForm = ccnxManifest_ToString(manifest); + * } + * @endcode + */ +char *ccnxManifest_ToString(const CCNxManifest *manifest); +#endif // libccnx_ccnx_Manifest_h diff --git a/libccnx-common/ccnx/common/ccnx_ManifestHashGroup.c b/libccnx-common/ccnx/common/ccnx_ManifestHashGroup.c new file mode 100644 index 00000000..cfe37742 --- /dev/null +++ b/libccnx-common/ccnx/common/ccnx_ManifestHashGroup.c @@ -0,0 +1,592 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ +#include <config.h> +#include <stdio.h> +#include <string.h> + +#include <LongBow/runtime.h> + +#include <parc/algol/parc_Object.h> +#include <parc/algol/parc_LinkedList.h> + +#include <ccnx/common/ccnx_Interest.h> +#include <ccnx/common/ccnx_ManifestHashGroup.h> +#include <ccnx/common/internal/ccnx_WireFormatMessageInterface.h> + +#define MAX_NUMBER_OF_POINTERS 1500 // loose upper bound imposed by packet format + +struct ccnx_manifest_hash_group { + PARCLinkedList *pointers; + + // Metadata + const PARCBuffer *overallDataDigest; // overall *application data* digest + size_t dataSize; // size for all pointers (last pointer might not be entrySize unless perfectly balanced) + size_t entrySize; // size per pointer + size_t blockSize; // size of nodes used in the tree (e.g., 4K for each Manifest or Data node) + size_t treeHeight; // height of sub-tree referred to by each pointer + const CCNxName *locator; // locator for the hash group +}; + +struct ccnx_manifest_hash_group_pointer { + CCNxManifestHashGroupPointerType pointerType; + PARCBuffer *digest; +}; + +static bool +CCNxManifestHashGroupPointer_FinalRelease(CCNxManifestHashGroupPointer **ptrP) +{ + if ((*ptrP)->digest != NULL) { + parcBuffer_Release(&(*ptrP)->digest); + } + return true; +} + +static bool +_CCNxManifestHashGroupPointer_Equals(const CCNxManifestHashGroupPointer *objectA, const CCNxManifestHashGroupPointer *objectB) +{ + if (objectA == objectB) { + return true; + } + if (objectA == NULL || objectB == NULL) { + return false; + } + if (objectA->pointerType == objectB->pointerType) { + if (parcBuffer_Equals(objectA->digest, objectB->digest)) { + return true; + } + } + return false; +} + +parcObject_Override(CCNxManifestHashGroupPointer, PARCObject, + .destructor = (PARCObjectDestructor *) CCNxManifestHashGroupPointer_FinalRelease, + .equals = (PARCObjectEquals *) _CCNxManifestHashGroupPointer_Equals); + +parcObject_ImplementAcquire(ccnxManifestHashGroupPointer, CCNxManifestHashGroupPointer); +parcObject_ImplementRelease(ccnxManifestHashGroupPointer, CCNxManifestHashGroupPointer); + +CCNxManifestHashGroupPointer * +ccnxManifestHashGroupPointer_Create(CCNxManifestHashGroupPointerType type, const PARCBuffer *digest) +{ + CCNxManifestHashGroupPointer *ptr = parcObject_CreateAndClearInstance(CCNxManifestHashGroupPointer); + if (ptr != NULL) { + ptr->pointerType = type; + ptr->digest = parcBuffer_Acquire(digest); + } + return ptr; +} + +CCNxManifestHashGroupPointerType +ccnxManifestHashGroupPointer_GetType(const CCNxManifestHashGroupPointer *ptr) +{ + return ptr->pointerType; +} + +const PARCBuffer * +ccnxManifestHashGroupPointer_GetDigest(const CCNxManifestHashGroupPointer *ptr) +{ + return ptr->digest; +} + +static bool +_ccnxManifestHashGroup_Destructor(CCNxManifestHashGroup **groupP) +{ + if ((*groupP)->pointers != NULL) { + parcLinkedList_Release(&(*groupP)->pointers); + } + if ((*groupP)->overallDataDigest != NULL) { + parcBuffer_Release((PARCBuffer **) &(*groupP)->overallDataDigest); + } + if ((*groupP)->locator != NULL) { + ccnxName_Release((CCNxName **) &(*groupP)->locator); + } + return true; +} + +parcObject_Override(CCNxManifestHashGroup, PARCObject, + .destructor = (PARCObjectDestructor *) _ccnxManifestHashGroup_Destructor, + .toString = (PARCObjectToString *) ccnxManifestHashGroup_ToString, + .equals = (PARCObjectEquals *) ccnxManifestHashGroup_Equals); + +parcObject_ImplementAcquire(ccnxManifestHashGroup, CCNxManifestHashGroup); +parcObject_ImplementRelease(ccnxManifestHashGroup, CCNxManifestHashGroup); + +CCNxManifestHashGroup * +ccnxManifestHashGroup_Create(void) +{ + CCNxManifestHashGroup *section = parcObject_CreateAndClearInstance(CCNxManifestHashGroup); + + if (section != NULL) { + section->pointers = parcLinkedList_Create(); + + section->overallDataDigest = NULL; + section->dataSize = 0; + section->entrySize = 0; + section->blockSize = 0; + section->treeHeight = 0; + section->locator = NULL; + } + + return section; +} + +bool +ccnxManifestHashGroup_AppendPointer(CCNxManifestHashGroup *group, CCNxManifestHashGroupPointerType type, const PARCBuffer *buffer) +{ + if (!ccnxManifestHashGroup_IsFull(group)) { + CCNxManifestHashGroupPointer *ptr = ccnxManifestHashGroupPointer_Create(type, buffer); + parcLinkedList_Append(group->pointers, ptr); + ccnxManifestHashGroupPointer_Release(&ptr); + return true; + } + return false; +} + +bool +ccnxManifestHashGroup_PrependPointer(CCNxManifestHashGroup *group, CCNxManifestHashGroupPointerType type, const PARCBuffer *buffer) +{ + if (!ccnxManifestHashGroup_IsFull(group)) { + CCNxManifestHashGroupPointer *ptr = ccnxManifestHashGroupPointer_Create(type, buffer); + parcLinkedList_Prepend(group->pointers, ptr); + ccnxManifestHashGroupPointer_Release(&ptr); + return true; + } + return false; +} + +void +ccnxManifestHashGroup_SetOverallDataDigest(CCNxManifestHashGroup *group, const PARCBuffer *digest) +{ + group->overallDataDigest = parcBuffer_Acquire(digest); +} + +const PARCBuffer * +ccnxManifestHashGroup_GetOverallDataDigest(const CCNxManifestHashGroup *group) +{ + return group->overallDataDigest; +} + +size_t +ccnxManifestHashGroup_GetTotalSize(const CCNxManifestHashGroup *group) +{ + return group->dataSize; +} + +size_t +ccnxManifestHashGroup_GetChildBlockSize(const CCNxManifestHashGroup *group) +{ + return group->entrySize; +} + +void +ccnxManifestHashGroup_SetLocator(CCNxManifestHashGroup *group, const CCNxName *locator) +{ + group->locator = ccnxName_Acquire(locator); +} + +const CCNxName * +ccnxManifestHashGroup_GetLocator(const CCNxManifestHashGroup *group) +{ + return group->locator; +} + +size_t +ccnxManifestHashGroup_GetNumberOfPointers(const CCNxManifestHashGroup *group) +{ + return parcLinkedList_Size(group->pointers); +} + +CCNxManifestHashGroupPointer * +ccnxManifestHashGroup_GetPointerAtIndex(const CCNxManifestHashGroup *group, size_t index) +{ + CCNxManifestHashGroupPointer *entry = parcLinkedList_GetAtIndex(group->pointers, index); + return entry; +} + +CCNxManifestHashGroupPointerType +ccnxManifestHashGroup_GetPointerTypeAtIndex(const CCNxManifestHashGroup *group, size_t index) +{ + CCNxManifestHashGroupPointer *entry = parcLinkedList_GetAtIndex(group->pointers, index); + return entry->pointerType; +} + +PARCBuffer * +ccnxManifestHashGroup_GetPointerDigestAtIndex(const CCNxManifestHashGroup *group, size_t index) +{ + CCNxManifestHashGroupPointer *entry = parcLinkedList_GetAtIndex(group->pointers, index); + return entry->digest; +} + +bool +ccnxManifestHashGroup_IsFull(const CCNxManifestHashGroup *group) +{ + size_t size = parcLinkedList_Size(group->pointers); + return size >= MAX_NUMBER_OF_POINTERS; +} + +bool +ccnxManifestHashGroup_Equals(const CCNxManifestHashGroup *objectA, const CCNxManifestHashGroup *objectB) +{ + if (objectA == objectB) { + return true; + } + if (objectA == NULL || objectB == NULL) { + return false; + } + + if (objectA->dataSize == objectB->dataSize) { + if (objectA->entrySize == objectB->entrySize) { + if (objectA->blockSize == objectB->blockSize) { + if (objectA->treeHeight == objectB->treeHeight) { + if (ccnxName_Equals(objectA->locator, objectB->locator)) { + if (parcBuffer_Equals(objectA->overallDataDigest, objectB->overallDataDigest)) { + if (parcLinkedList_Size(objectA->pointers) == parcLinkedList_Size(objectB->pointers)) { + for (size_t i = 0; i < parcLinkedList_Size(objectA->pointers); i++) { + CCNxManifestHashGroupPointer *ptrA = parcLinkedList_GetAtIndex(objectA->pointers, i); + CCNxManifestHashGroupPointer *ptrB = parcLinkedList_GetAtIndex(objectB->pointers, i); + if (!_CCNxManifestHashGroupPointer_Equals(ptrA, ptrB)) { + return false; + } + } + return true; + } + } + } + } + } + } + } + + return false; +} + +char * +ccnxManifestHashGroup_ToString(const CCNxManifestHashGroup *section) +{ + PARCJSON *json = ccnxManifestHashGroup_ToJson(section); + char *stringRep = parcJSON_ToString(json); + parcJSON_Release(&json); + return stringRep; +} + +PARCJSON * +ccnxManifestHashGroup_ToJson(const CCNxManifestHashGroup *group) +{ + PARCJSON *root = parcJSON_Create(); + + PARCJSONArray *ptrList = parcJSONArray_Create(); + for (size_t i = 0; i < parcLinkedList_Size(group->pointers); i++) { + CCNxManifestHashGroupPointer *ptr = (CCNxManifestHashGroupPointer *) parcLinkedList_GetAtIndex(group->pointers, i); + PARCJSON *ptrJson = parcJSON_Create(); + + // Type. + parcJSON_AddInteger(ptrJson, "type", ccnxManifestHashGroupPointer_GetType(ptr)); + + // Digest. + char *digestString = parcBuffer_ToHexString(ptr->digest); + parcJSON_AddString(ptrJson, "digest", digestString); + parcMemory_Deallocate(&digestString); + + // Add the tuple to the list. + PARCJSONValue *val = parcJSONValue_CreateFromJSON(ptrJson); + parcJSONArray_AddValue(ptrList, val); + + // Cleanup + parcJSONValue_Release(&val); + parcJSON_Release(&ptrJson); + } + root = parcJSON_AddArray(root, "HashGroup", ptrList); + parcJSONArray_Release(&ptrList); + + if (group->overallDataDigest != NULL) { + char *digestString = parcBuffer_ToHexString(group->overallDataDigest); + root = parcJSON_AddString(root, "overallDataDigest", digestString); + parcMemory_Deallocate((void **) &digestString); + } + + if (group->locator != NULL) { + char *locatorString = ccnxName_ToString(group->locator); + root = parcJSON_AddString(root, "locator", locatorString); + parcMemory_Deallocate((void **) &locatorString); + } + + if (group->entrySize > 0) { + root = parcJSON_AddInteger(root, "entrySize", group->entrySize); + } + + if (group->dataSize > 0) { + root = parcJSON_AddInteger(root, "dataSize", group->dataSize); + } + + if (group->blockSize > 0) { + root = parcJSON_AddInteger(root, "blockSize", group->blockSize); + } + + if (group->treeHeight > 0) { + root = parcJSON_AddInteger(root, "treeHeight", group->treeHeight); + } + + return root; +} + +CCNxManifestHashGroup * +ccnxManifestHashGroup_CreateFromJson(const PARCJSON *json) +{ + CCNxManifestHashGroup *group = ccnxManifestHashGroup_Create(); + + PARCJSONValue *ptrListValue = parcJSON_GetValueByName(json, "HashGroup"); + PARCJSONArray *ptrList = parcJSONValue_GetArray(ptrListValue); + size_t numberOfPointers = parcJSONArray_GetLength(ptrList); + for (size_t i = 0; i < numberOfPointers; i++) { + PARCJSONValue *pointerValue = parcJSONArray_GetValue(ptrList, i); + + PARCJSON *typeJson = parcJSONValue_GetJSON(pointerValue); + PARCJSONValue *typeValue = parcJSON_GetValueByName(typeJson, "type"); + CCNxManifestHashGroupPointerType type; + + if (parcJSONValue_GetInteger(typeValue) == 0) { + type = CCNxManifestHashGroupPointerType_Data; + } else { + type = CCNxManifestHashGroupPointerType_Manifest; + } + + PARCJSON *digestJson = parcJSONValue_GetJSON(pointerValue); + PARCJSONValue *digestValue = parcJSON_GetValueByName(digestJson, "digest"); + PARCBuffer *digestHex = parcJSONValue_GetString(digestValue); + + char *hexString = parcBuffer_ToString(digestHex); + PARCBuffer *digest = parcBuffer_Flip(parcBuffer_ParseHexString(hexString)); + parcMemory_Deallocate(&hexString); + + ccnxManifestHashGroup_AppendPointer(group, type, digest); + parcBuffer_Release(&digest); + } + + if (parcJSON_GetPairByName(json, "overallDataDigest") != NULL) { + PARCJSONValue *overallDataDigestValue = parcJSON_GetValueByName(json, "overallDataDigest"); + PARCBuffer *digestHex = parcJSONValue_GetString(overallDataDigestValue); + + char *hexString = parcBuffer_ToString(digestHex); + group->overallDataDigest = parcBuffer_Flip(parcBuffer_ParseHexString(hexString)); + parcMemory_Deallocate(&hexString); + } + + if (parcJSON_GetPairByName(json, "locator") != NULL) { + PARCJSONValue *locatorValue = parcJSON_GetValueByName(json, "locator"); + PARCBuffer *buffer = parcJSONValue_GetString(locatorValue); + char *locator = parcBuffer_ToString(buffer); + group->locator = ccnxName_CreateFromCString(locator); + parcMemory_Deallocate(&locator); + } + + if (parcJSON_GetPairByName(json, "entrySize") != NULL) { + PARCJSONValue *childBlockNodeSizeValue = parcJSON_GetValueByName(json, "entrySize"); + group->entrySize = parcJSONValue_GetInteger(childBlockNodeSizeValue); + } + + if (parcJSON_GetPairByName(json, "dataSize") != NULL) { + PARCJSONValue *totalSizeValue = parcJSON_GetValueByName(json, "dataSize"); + group->dataSize = parcJSONValue_GetInteger(totalSizeValue); + } + + if (parcJSON_GetPairByName(json, "blockSize") != NULL) { + PARCJSONValue *blockSizeValue = parcJSON_GetValueByName(json, "blockSize"); + group->blockSize = parcJSONValue_GetInteger(blockSizeValue); + } + + if (parcJSON_GetPairByName(json, "treeHeight") != NULL) { + PARCJSONValue *treeHeightValue = parcJSON_GetValueByName(json, "treeHeight"); + group->treeHeight = parcJSONValue_GetInteger(treeHeightValue); + } + + return group; +} + +struct _hashgroup_ccnxManifestHashGroupIterator_state { + size_t pointerNumber; + bool atEnd; +}; +typedef struct _hashgroup_ccnxManifestHashGroupIterator_state _HashgroupIteratorState; + +static void * +_ccnxManifestHashGroupIterator_Init(CCNxManifestHashGroup *group) +{ + _HashgroupIteratorState *state = parcMemory_Allocate(sizeof(_HashgroupIteratorState)); + state->pointerNumber = 0; + state->atEnd = false; + return state; +} + +static bool +_ccnxManifestHashGroupIterator_HasNext(CCNxManifestHashGroup *group, void *voidstate) +{ + _HashgroupIteratorState *state = (_HashgroupIteratorState *) voidstate; + return !state->atEnd; +} + +static void * +_ccnxManifestHashGroupIterator_Next(CCNxManifestHashGroup *group, void *state) +{ + _HashgroupIteratorState *thestate = (_HashgroupIteratorState *) state; + thestate->pointerNumber++; + + if (thestate->pointerNumber == parcLinkedList_Size(group->pointers)) { + thestate->atEnd = true; + } + + return thestate; +} + +static void +_ccnxManifestHashGroupIterator_RemoveAt(CCNxManifestHashGroup *group, void **state) +{ + // pass +} + +static void * +_ccnxManifestHashGroupIterator_GetElement(CCNxManifestHashGroup *group, void *state) +{ + _HashgroupIteratorState *thestate = (_HashgroupIteratorState *) state; + CCNxManifestHashGroupPointer *ptr = + (CCNxManifestHashGroupPointer *) parcLinkedList_GetAtIndex(group->pointers, thestate->pointerNumber - 1); + return ptr; +} + +static void +_ccnxManifestHashGroupIterator_Finish(CCNxManifestHashGroup *group, void *state) +{ + _HashgroupIteratorState *thestate = (_HashgroupIteratorState *) state; + parcMemory_Deallocate(&thestate); +} + +static void +_ccnxManifestHashGroupIterator_AssertValid(const void *state) +{ + // pass +} + +PARCIterator * +ccnxManifestHashGroup_Iterator(const CCNxManifestHashGroup *group) +{ + PARCIterator *iterator = parcIterator_Create((void *) group, + (void *(*)(PARCObject *))_ccnxManifestHashGroupIterator_Init, + (bool (*)(PARCObject *, void *))_ccnxManifestHashGroupIterator_HasNext, + (void *(*)(PARCObject *, void *))_ccnxManifestHashGroupIterator_Next, + (void (*)(PARCObject *, void **))_ccnxManifestHashGroupIterator_RemoveAt, + (void *(*)(PARCObject *, void *))_ccnxManifestHashGroupIterator_GetElement, + (void (*)(PARCObject *, void *))_ccnxManifestHashGroupIterator_Finish, + (void (*)(const void *))_ccnxManifestHashGroupIterator_AssertValid); + + return iterator; +} + +size_t +ccnxManifestHashGroup_GetBlockSize(const CCNxManifestHashGroup *group) +{ + return group->blockSize; +} + +void +ccnxManifestHashGroup_SetBlockSize(CCNxManifestHashGroup *group, size_t blockSize) +{ + group->blockSize = blockSize; +} + +size_t +ccnxManifestHashGroup_GetDataSize(const CCNxManifestHashGroup *group) +{ + return group->dataSize; +} + +void +ccnxManifestHashGroup_SetDataSize(CCNxManifestHashGroup *group, size_t dataSize) +{ + group->dataSize = dataSize; +} + +size_t +ccnxManifestHashGroup_GetEntrySize(const CCNxManifestHashGroup *group) +{ + return group->entrySize; +} + +void +ccnxManifestHashGroup_SetEntrySize(CCNxManifestHashGroup *group, size_t entrySize) +{ + group->entrySize = entrySize; +} + +size_t +ccnxManifestHashGroup_GetTreeHeight(const CCNxManifestHashGroup *group) +{ + return group->treeHeight; +} + +void +ccnxManifestHashGroup_SetTreeHeight(CCNxManifestHashGroup *group, size_t treeHeight) +{ + group->treeHeight = treeHeight; +} + +bool +ccnxManifestHashGroup_HasMetadata(const CCNxManifestHashGroup *group) +{ + // Check the existence of each metadata value. + if (group->blockSize > 0) { + return true; + } + if (group->dataSize > 0) { + return true; + } + if (group->entrySize > 0) { + return true; + } + if (group->locator != NULL) { + return true; + } + if (group->overallDataDigest != NULL) { + return true; + } + + return false; +} + +PARCLinkedList * +ccnxManifestHashGroup_CreateInterestList(const CCNxManifestHashGroup *group, const CCNxName *locator) +{ + PARCLinkedList *interestList = parcLinkedList_Create(); + + PARCIterator *itr = ccnxManifestHashGroup_Iterator(group); + while (parcIterator_HasNext(itr)) { + // Extract the name and digest + CCNxManifestHashGroupPointer *ptr = parcIterator_Next(itr); + const PARCBuffer *digest = ccnxManifestHashGroupPointer_GetDigest(ptr); + const CCNxName *name = group->locator == NULL ? locator : group->locator; + + // Build the interest and append it to the running list + if (name != NULL) { + CCNxInterest *interest = ccnxInterest_CreateSimple(name); + ccnxInterest_SetContentObjectHashRestriction(interest, digest); + parcLinkedList_Append(interestList, interest); + ccnxInterest_Release(&interest); + } + } + parcIterator_Release(&itr); + + return interestList; +} diff --git a/libccnx-common/ccnx/common/ccnx_ManifestHashGroup.h b/libccnx-common/ccnx/common/ccnx_ManifestHashGroup.h new file mode 100755 index 00000000..092434b1 --- /dev/null +++ b/libccnx-common/ccnx/common/ccnx_ManifestHashGroup.h @@ -0,0 +1,692 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file ccnx_ManifestHashGroup.h + * @brief A HashGroup in a FLIC manifest. + * + */ +#ifndef libccnx_ccnx_ManifestHashGroup_h +#define libccnx_ccnx_ManifestHashGroup_h + +#include <ccnx/common/ccnx_Name.h> +#include <ccnx/common/ccnx_ContentObject.h> + +#include <parc/algol/parc_Buffer.h> +#include <parc/algol/parc_List.h> +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_JSON.h> +#include <parc/algol/parc_Iterator.h> +#include <parc/algol/parc_LinkedList.h> + +#include <parc/security/parc_Signature.h> + +struct ccnx_manifest_hash_group; +/** + * @typedef CCNxManifestHashGroup + * @brief A FLIC HashGroup + */ +typedef struct ccnx_manifest_hash_group CCNxManifestHashGroup; + +typedef enum { + CCNxManifestHashGroupPointerType_Data, + CCNxManifestHashGroupPointerType_Manifest +} CCNxManifestHashGroupPointerType; + +/** + * @typedef CCNxManifestHashGroupPointer + * @brief A HashGroup pointer. + */ +struct ccnx_manifest_hash_group_pointer; +typedef struct ccnx_manifest_hash_group_pointer CCNxManifestHashGroupPointer; + +/** + * Retrieve the type of a `CCNxManifestHashGroupPointer`. + * + * @param [in] ptr A `CCNxManifestHashGroupPointer` instance. + * + * @retval The type of the `CCNxManifestHashGroupPointer` instance. + * + * Example: + * @code + * { + * CCNxManifestHashGroupPointer *pointer = ccnxManifestHashGroup_GetPointerAtIndex(group, 0); + * CCNxManifestHashGroupPointerType type = ccnxManifestHashGroupPointer_GetType(pointer); + * if (type == CCNxManifestHashGroupPointerType_Data) { + * // Data + * } else { + * // Manifest + * } + * } + * @endcode + */ +CCNxManifestHashGroupPointerType ccnxManifestHashGroupPointer_GetType(const CCNxManifestHashGroupPointer *ptr); + +/** + * Retrieve hash digest associated with the `CCNxManifestHashGroupPointer` value. + * + * @param [in] ptr A `CCNxManifestHashGroupPointer` instance. + * + * @retval The hash digest for a `CCNxManifestHashGroupPointer` in a `PARCBuffer`. + * + * Example: + * @code + * { + * CCNxManifestHashGroupPointer *pointer = ccnxManifestHashGroup_GetPointerAtIndex(group, 0); + * PARCBuffer *digest = ccnxManifestHashGroupPointer_GetDigest(pointer); + * // use the digest + * } + * @endcode + */ +const PARCBuffer *ccnxManifestHashGroupPointer_GetDigest(const CCNxManifestHashGroupPointer *ptr); + +/** + * Create a new and empty {@link CCNxManifestHashGroup} instance. + * + * @return A pointer to a {@link CCNxManifestHashGroup} instance, or NULL if an error or out of memory. + * + * Example: + * @code + * { + * CCNxManifestHashGroup *section = CCNxManifestHashGroup_Create(); + * + * ... + * + * CCNxManifestHashGroup_Release(§ion); + * } + * @endcode + */ +CCNxManifestHashGroup *ccnxManifestHashGroup_Create(); + +/** + * Create a new {@link CCNxManifestHashGroup} instance from a + * + * @param [in] jsonRepresentation - A pointer to a {@link PARCJSON} object representing the `CCNxManifestHashGroup`. + * + * @return A pointer to a {@link CCNxManifestHashGroup} instance, or NULL if an error or out of memory. + * + * Example: + * @code + * { + * PARCJSON *jsonRep = ccnxTlvDictionary_GetJson(dict, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_ManifestHashGroup); + * CCNxManifestHashGroup *section = CCNxManifestHashGroup_CreateFromJson(jsonRep); + * + * ... + * + * ccnxJson_Destroy(&jsonRep); + * CCNxManifestHashGroup_Release(§ion); + * } + * @endcode + */ +CCNxManifestHashGroup *ccnxManifestHashGroup_CreateFromJson(const PARCJSON *jsonRepresentation); + +/** + * Increase the number of references to an instance of this object. + * + * Note that new instance is not created, + * only that the given instance's reference count is incremented. + * Discard the reference by invoking {@link CCNxManifestHashGroup_Release()}. + * + * @param [in] group A pointer to the {@link CCNxManifestHashGroup} to acquire. + * @return The value of the input parameter @p group. + * + * Example: + * @code + * { + * CCNxManifest *manifest = CCNxManifestHashGroup_Acquire(instance); + * + * CCNxManifestHashGroup_Release(&manifest); + * } + * @endcode + * + * @see `CCNxManifestHashGroup_Release` + */ +CCNxManifestHashGroup *ccnxManifestHashGroup_Acquire(const CCNxManifestHashGroup *group); + +/** + * Release a previously acquired reference to the specified instance, + * decrementing the reference count for the instance. + * + * The pointer to the instance is set to NULL as a side-effect of this function. + * + * If the invocation causes the last reference to the instance to be released, + * the instance is deallocated and the instance's implementation will perform + * additional cleanup and release other privately held references. + * + * @param [in,out] sectionP A pointer to a pointer to the instance to release. + * + * + * Example: + * @code + * { + * CCNxManifestHashGroup *manifest = CCNxManifestHashGroup_Acquire(instance); + * + * CCNxManifestHashGroup_Release(&manifest); + * } + * @endcode + */ +void ccnxManifestHashGroup_Release(CCNxManifestHashGroup **sectionP); + +/** + * Check that the pointer to the {@link CCNxManifestHashGroup} is valid. It should be non-null, + * and any referenced data should also be valid. + * + * @param [in] manifest A pointer to the {@link CCNxManifestHashGroup} to check. + * + * + * Example: + * @code + * { + * CCNxManifest *manifest = CCNxManifestHashGroup_Acquire(instance); + * + * CCNxManifestHashGroup_AssertValid(manifest); + * } + * @endcode + */ +void ccnxManifestHashGroup_AssertValid(const CCNxManifestHashGroup *manifest); + +/** + * Add a new pointer to the {@link CCNxManifestHashGroup} with the specified type and hash digest. + * + * @param [in] group - A {@link CCNxManifestHashGroup} instance. + * @param [in] type - The {@link CCNxManifestHashGroupPointerType} type. + * @param [in] buffer - The {@link PARCBuffer} containing the pointer digest. + * + * Example: + * @code + * { + * CCNxManifestHashGroup *group = CCNxManifestHashGroup_Create(); + * + * PARCBuffer *hashDigest = ...; + * + * bool added = ccnxManifestHashGroup_AppendPointer(group, CCNxManifestHashGroupPointerType_Data, hashDigest); + * // added == true if the group was not full + * + * CCNxManifestHashGroup_Release(&group); + * } + * @endcode + */ +bool ccnxManifestHashGroup_AppendPointer(CCNxManifestHashGroup *group, CCNxManifestHashGroupPointerType type, const PARCBuffer *buffer); + +/** + * Prepend a new pointer to the {@link CCNxManifestHashGroup} with the specified type and hash digest. + * + * @param [in] group - A {@link CCNxManifestHashGroup} instance. + * @param [in] type - The {@link CCNxManifestHashGroupPointerType} type. + * @param [in] buffer - The {@link PARCBuffer} containing the pointer digest. + * + * Example: + * @code + * { + * CCNxManifestHashGroup *group = CCNxManifestHashGroup_Create(); + * + * PARCBuffer *hashDigest = ...; + * + * bool added = ccnxManifestHashGroup_PrependPointer(group, CCNxManifestHashGroupPointerType_Data, hashDigest); + * // added == true if the group was not full + * + * CCNxManifestHashGroup_Release(&group); + * } + * @endcode + */ +bool ccnxManifestHashGroup_PrependPointer(CCNxManifestHashGroup *group, CCNxManifestHashGroupPointerType type, const PARCBuffer *buffer); + +/** + * Retrieve the {@link CCNxManifestHashGroupPointer} in the {@link CCNxManifestHashGroup} at + * the specified index. + * + * @param [in] group - A {@link CCNxManifestHashGroup} instance. + * @param [in] index - The index of the `CCNxManifestHashGroupPointer` to retrieve. + * + * @retval The `CCNxManifestHashGroupPointer` at the specified index. + * + * Example: + * @code + * { + * CCNxManifestHashGroup *group = CCNxManifestHashGroup_Create(); + * + * PARCBuffer *hashDigest = ...; + * ccnxManifestHashGroup_AppendPointer(group, CCNxManifestHashGroupPointerType_Data, hashDigest); + * + * // ... + * CCNxManifestHashGroupPointer *pointer = ccnxManifestHashGroup_GetPointerAtIndex(group, 0); + * // use it as needed + * + * CCNxManifestHashGroup_Release(&group); + * } + * @endcode + */ +CCNxManifestHashGroupPointer *ccnxManifestHashGroup_GetPointerAtIndex(const CCNxManifestHashGroup *group, size_t index); + +/** + * Determine if two {@link CCNxManifestHashGroup} instances are equal. + * + * The following equivalence relations on non-null {@link CCNxManifestHashGroup} instances are maintained: + * * It is reflexive: for any non-null reference value x, `CCNxManifestHashGroup_Equals(x, x)` must return true. + * + * * It is symmetric: for any non-null reference values x and y, `CCNxManifestHashGroup_Equals(x, y)` must return true if and only if + * `CCNxManifestHashGroup_Equals(y x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `CCNxManifestHashGroup_Equals(x, y)` returns true and + * `CCNxManifestHashGroup_Equals(y, z)` returns true, + * then `CCNxManifestHashGroup_Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple invocations of `CCNxManifestHashGroup_Equals(x, y)` + * consistently return true or consistently return false. + * + * * For any non-null reference value x, `CCNxManifestHashGroup_Equals(x, NULL)` must return false. + * + * + * @param [in] x A pointer to a {@link CCNxManifestHashGroup} instance. + * @param [in] y A pointer to a {@link CCNxManifestHashGroup} instance. + * @return `true` if the referenced {@link CCNxManifestHashGroup} instances are equal. + * + * Example: + * @code + * { + * CCNxManifestHashGroup *a = CCNxManifestHashGroup_Create(...); + * CCNxManifestHashGroup *b = CCNxManifestHashGroup_Create(...); + * + * if (CCNxManifestHashGroup_Equals(a, b)) { + * // true + * } else { + * // false + * } + * } + * @endcode + */ +bool ccnxManifestHashGroup_Equals(const CCNxManifestHashGroup *x, const CCNxManifestHashGroup *y); + +/** + * Create a null-terminated string representation of the given {@link CCNxManifestHashGroup}. + * + * The returned value must be freed by the caller using {@link parcMemory_Deallocate()}. + * + * @param [in] group A pointer to a {@link CCNxManifestHashGroup} instance. + * + * @return A pointer to null-terminated string of characters repesenting the @p section that must be + * freed by the caller by `parcMemory_Deallocate()`. + * + * Example: + * @code + * { + * CCNxManifestHashGroup *group = CCNxManifestHashGroup_Create(); + * + * char *sectionDescription = CCNxManifestHashGroup_ToString(group); + * printf("Manifest: %s\n", sectionDescription); + * + * CCNxManifestHashGroup_Release(&group); + * } + * @endcode + */ +char *ccnxManifestHashGroup_ToString(const CCNxManifestHashGroup *group); + +/** + * Create a `CCNxJson` representation of the given {@link CCNxManifestHashGroup}. + * + * The returned value must be freed by the caller using {@link ccnxJson_Destroy()}. + * + * @param [in] section A pointer to a {@link CCNxManifestHashGroup} instance. + * + * @return A pointer to the JSON representation of the @p section that must be freed by the caller by `ccnxJson_Destroy()`. + * + * Example: + * @code + * { + * CCNxManifestHashGroup *group = CCNxManifestHashGroup_Create(); + * + * PARCJSON *jsonForm = CCNxManifestHashGroup_ToJson(group); + * printf("Manifest: %s\n", ccnxJson_ToString(jsonForm)); + * + * CCNxManifestHashGroup_Release(&group); + * } + * @endcode + */ +PARCJSON *ccnxManifestHashGroup_ToJson(const CCNxManifestHashGroup *section); + +/** + * Set the {@link CCNxName} locator for this {@link CCNxManifestHashGroup}. + * + * @param [in] group A {@link CCNxManifestHashGroup} instance. + * @param [in] locator A {@link CCNxName} instance. + * + * Example: + * @code + * { + * CCNxManifestHashGroup *group = ... + * CCNxName *name = ccnxName_CreateFromURI("lci:/some/place"); + * + * ccnxManifestHashGroup_setLocator(group, name); + * } + * @endcode + */ +void ccnxManifestHashGroup_SetLocator(CCNxManifestHashGroup *group, const CCNxName *locator); + +/** + * Retrieve the {@link CCNxName} locator for this {@link CCNxManifestHashGroup}. + * + * @param [in] CCNxManifestHashGroup A {@link CCNxManifestHashGroup} instance. + * + * @return non-NULL The {@link CCNxName} locator for this {@link CCNxManifestHashGroup}. + * @return NULL There is no locator. + * + * Example: + * @code + * { + * CCNxManifestHashGroup *group = ... + * + * CCNxName *locator = ccnxManifestHashGroup_GetLocator(group); + * printf("Manifest Locator: %s\n", ccnxName_ToString(locator)); + * + * CCNxManifestHashGroup_Release(&group); + * } + * @endcode + */ +const CCNxName *ccnxManifestHashGroup_GetLocator(const CCNxManifestHashGroup *group); + +/** + * Retrieve the number of pointers in this {@link CCNxManifestHashGroup}. + * + * @param [in] group A {@link CCNxManifestHashGroup} instance. + * + * @retval The number of pointers in the {@link CCNxManifestHashGroup} instance. + * + * Example: + * @code + * { + * CCNxManifestHashGroup *group = ... + * + * size_t numPointers = ccnxManifestHashGroup_GetNumberOfPointers(group); + * for (size_t i = 0; i < numPointers; i++) { + * // get i-th pointer and do something with it + * } + * + * CCNxManifestHashGroup_Release(&group); + * } + * @endcode + */ +size_t ccnxManifestHashGroup_GetNumberOfPointers(const CCNxManifestHashGroup *group); + +/** + * Determine if more pointers can be added to a {@link CCNxManifestHashGroup} instance. + * + * @param [in] group A {@link CCNxManifestHashGroup} instance. + * + * @return true More space is available + * @return false The manifest is full. + * + * Example: + * @code + * { + * CCNxManifestHashGroup *group = ... + * + * size_t numPointers= ccnxManifestHashGroup_GetNumberOfPointers(group); + * for (size_t i = 0; i < numPointers; i++) { + * // get i-th pointer and do something with it + * } + * + * CCNxManifestHashGroup_Release(&group); + * } + * @endcode + */ +bool ccnxManifestHashGroup_IsFull(const CCNxManifestHashGroup *group); + +/** + * Retrieve a {@link PARCIterator} to walk over each of the {@link CCNxManifestHashGroupPointer} + * instances in the {@link CCNxManifestHashGroup}. + * + * @param [in] group A {@link CCNxManifestHashGroup} instance. + * + * @retval A {@link PARCIterator} instance. + * + * Example: + * @code + * { + * CCNxManifestHashGroup *group = ... + * PARCIterator *itr = ccnxManifestHashGroup_Iterator(group); + * while (parcIterator_HasNext(itr)) { + * CCNxManifestHashGroupPointer *ptr = (CCNxManifestHashGroupPointer *) parcIterator_Next(itr); + * // use the pointer + * } + * } + * @endcode + */ +PARCIterator *ccnxManifestHashGroup_Iterator(const CCNxManifestHashGroup *group); + +/** + * Retrieve the block size of this {@link CCNxManifestHashGroup}. + * + * @param [in] group A {@link CCNxManifestHashGroup} instance. + * + * @retval The block size of the {@link CCNxManifestHashGroup} instance. + * + * Example: + * @code + * { + * CCNxManifestHashGroup *group = ... + * + * size_t blockSize = ccnxManifestHashGroup_GetBlockSize(group); + * // use it + * } + * @endcode + */ +size_t ccnxManifestHashGroup_GetBlockSize(const CCNxManifestHashGroup *group); + +/** + * Set the block size of this {@link CCNxManifestHashGroup}. + * + * @param [in] group A {@link CCNxManifestHashGroup} instance. + * @param [in] blockSize The block size. + * + * Example: + * @code + * { + * CCNxManifestHashGroup *group = ... + * size_t blockSize = + * + * ccnxManifestHashGroup_SetBlockSize(group, blockSize); + * } + * @endcode + */ +void ccnxManifestHashGroup_SetBlockSize(CCNxManifestHashGroup *group, size_t blockSize); + +/** + * Retrieve the data size of this {@link CCNxManifestHashGroup}. + * + * @param [in] group A {@link CCNxManifestHashGroup} instance. + * + * @retval The data size of the {@link CCNxManifestHashGroup} instance. + * + * Example: + * @code + * { + * CCNxManifestHashGroup *group = ... + * + * size_t dataSize = ccnxManifestHashGroup_GetDataSize(group); + * // use it + * } + * @endcode + */ +size_t ccnxManifestHashGroup_GetDataSize(const CCNxManifestHashGroup *group); + +/** + * Set the data size of this {@link CCNxManifestHashGroup}. + * + * @param [in] group A {@link CCNxManifestHashGroup} instance. + * @param [in] dataSize The data size + * + * Example: + * @code + * { + * CCNxManifestHashGroup *group = ... + * size_t dataSize = + * + * ccnxManifestHashGroup_SetDataSize(group, dataSize); + * } + * @endcode + */ +void ccnxManifestHashGroup_SetDataSize(CCNxManifestHashGroup *group, size_t dataSize); + +/** + * Retrieve the entry size of this {@link CCNxManifestHashGroup}. + * + * @param [in] group A {@link CCNxManifestHashGroup} instance. + * + * @retval The entry size of the {@link CCNxManifestHashGroup} instance. + * + * Example: + * @code + * { + * CCNxManifestHashGroup *group = ... + * + * size_t entrySize = ccnxManifestHashGroup_GetEntrySize(group); + * // use it + * } + * @endcode + */ +size_t ccnxManifestHashGroup_GetEntrySize(const CCNxManifestHashGroup *group); + +/** + * Set the entry size of this {@link CCNxManifestHashGroup}. + * + * @param [in] group A {@link CCNxManifestHashGroup} instance. + * @param [in] entrySize The entry size. + * + * Example: + * @code + * { + * CCNxManifestHashGroup *group = ... + * size_t entrySize = + * + * ccnxManifestHashGroup_SetEntrySize(group, entrySize); + * } + * @endcode + */ +void ccnxManifestHashGroup_SetEntrySize(CCNxManifestHashGroup *group, size_t entrySize); + +/** + * Retrieve the tree height of this {@link CCNxManifestHashGroup}. + * + * @param [in] group A {@link CCNxManifestHashGroup} instance. + * + * @retval The tree height of the {@link CCNxManifestHashGroup} instance. + * + * Example: + * @code + * { + * CCNxManifestHashGroup *group = ... + * + * size_t treeHeight = ccnxManifestHashGroup_GetTreeHeight(group); + * // use it + * } + * @endcode + */ +size_t ccnxManifestHashGroup_GetTreeHeight(const CCNxManifestHashGroup *group); + +/** + * Set the tree height of this {@link CCNxManifestHashGroup}. + * + * @param [in] group A {@link CCNxManifestHashGroup} instance. + * @param [in] treeHeight The tree height of the {@link CCNxManifestHashGroup} instance. + * + * Example: + * @code + * { + * CCNxManifestHashGroup *group = ... + * size_t treeHeight = ... + * ccnxManifestHashGroup_SetTreeHeight(group, treeHeight); + * } + * @endcode + */ +void ccnxManifestHashGroup_SetTreeHeight(CCNxManifestHashGroup *group, size_t treeHeight); + +/** + * Retrieve the overall data digest of this {@link CCNxManifestHashGroup}. + * + * @param [in] group A {@link CCNxManifestHashGroup} instance. + * + * @retval The overall data digest of the {@link CCNxManifestHashGroup} instance. + * + * Example: + * @code + * { + * CCNxManifestHashGroup *group = ... + * + * const PARCBuffer *digest = ccnxManifestHashGroup_GetOverallDataDigest(group); + * // use it + * } + * @endcode + */ +const PARCBuffer *ccnxManifestHashGroup_GetOverallDataDigest(const CCNxManifestHashGroup *group); + +/** + * Set the overall data digest of this {@link CCNxManifestHashGroup}. + * + * @param [in] group A {@link CCNxManifestHashGroup} instance. + * @param [in] digest The overall data digest of the {@link CCNxManifestHashGroup} instance. + * + * Example: + * @code + * { + * CCNxManifestHashGroup *group = ... + * const PARCBuffer *digest = ... + * + * ccnxManifestHashGroup_SetOverallDataDigest(group, digest); + * } + * @endcode + */ +void ccnxManifestHashGroup_SetOverallDataDigest(CCNxManifestHashGroup *group, const PARCBuffer *digest); + +/** + * Determine if this `CCNxManifestHashGroup` is carrying any metadata. + * + * @param [in] group A {@link CCNxManifestHashGroup} instance. + * + * @retval true If the `CCNxManifestHashGroup` has metadata. + * @retval false Otherwise + * + * Example: + * @code + * { + * CCNxManifestHashGroup *group = ... + * + * bool hasMetadata = ccnxManifestHashGroup_HasMetadata(group); + * } + * @endcode + */ +bool ccnxManifestHashGroup_HasMetadata(const CCNxManifestHashGroup *group); + +/** + * Create a list of `CCNxInterest` instances that can be created from this single + * `CCNxManifestHashGroup` instance. + * + * @param [in] group A pointer to an instance of `CCNxManifestHashGroup`. + * @param [in] name A `CCNxName` locator for the interests in this list. + * + * @return A `PARCLinkedList` containing the set of all Interests that can be + * constructed from this HashGroup. + * + * Example: + * @code + * { + * CCNxManifestHashGroup *group = ...; + * + * PARCLinkedList *interests = ccnxManifestHashGroup_CreateInterestList(group); + * } + * @endcode + */ +PARCLinkedList *ccnxManifestHashGroup_CreateInterestList(const CCNxManifestHashGroup *group, const CCNxName *locator); +#endif // libccnx_ccnx_ManifestHashGroup_h diff --git a/libccnx-common/ccnx/common/ccnx_Name.c b/libccnx-common/ccnx/common/ccnx_Name.c new file mode 100755 index 00000000..df4ead1b --- /dev/null +++ b/libccnx-common/ccnx/common/ccnx_Name.c @@ -0,0 +1,417 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ +#include <config.h> + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include <LongBow/runtime.h> + +#include <ccnx/common/ccnx_Name.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_LinkedList.h> +#include <parc/algol/parc_URI.h> +#include <parc/algol/parc_URIPath.h> +#include <parc/algol/parc_DisplayIndented.h> +#include <parc/algol/parc_Object.h> + +struct ccnx_name { + PARCLinkedList *segments; +}; + +static bool +_ccnxName_Destructor(CCNxName **pointer) +{ + CCNxName *name = *pointer; + + parcLinkedList_Release(&name->segments); + return true; +} + +parcObject_Override(CCNxName, PARCObject, + .destructor = (PARCObjectDestructor *) _ccnxName_Destructor, + .copy = (PARCObjectCopy *) ccnxName_Copy, + .equals = (PARCObjectEquals *) ccnxName_Equals, + .compare = (PARCObjectCompare *) ccnxName_Compare, + .hashCode = (PARCObjectHashCode *) ccnxName_HashCode, + .toString = (PARCObjectToString *) ccnxName_ToString, + .display = (PARCObjectDisplay *) ccnxName_Display); +CCNxName * +ccnxName_Create(void) +{ + CCNxName *result = parcObject_CreateInstance(CCNxName); + + if (result != NULL) { + result->segments = parcLinkedList_Create(); + } + + return result; +} + +parcObject_ImplementAcquire(ccnxName, CCNxName); + +parcObject_ImplementRelease(ccnxName, CCNxName); + +void +ccnxName_AssertValid(const CCNxName *name) +{ + trapIllegalValueIf(ccnxName_IsValid(name) == false, "CCNxName instance is not valid."); +} + +bool +ccnxName_IsValid(const CCNxName *name) +{ + bool result = false; + + if (name != NULL) { + result = parcLinkedList_IsValid(name->segments); + } + + return result; +} + +CCNxName * +ccnxName_Copy(const CCNxName *originalName) +{ + ccnxName_OptionalAssertValid(originalName); + + CCNxName *result = ccnxName_Create(); + + if (result != NULL) { + for (int i = 0; i < ccnxName_GetSegmentCount(originalName); i++) { + CCNxNameSegment *component = ccnxName_GetSegment(originalName, i); + CCNxNameSegment *copy = ccnxNameSegment_Copy(component); + ccnxName_Append(result, copy); + ccnxNameSegment_Release(©); + } + } + + return result; +} + +bool +ccnxName_Equals(const CCNxName *a, const CCNxName *b) +{ + if (a == b) { + return true; + } + if (a == NULL || b == NULL) { + return false; + } + + if (ccnxName_GetSegmentCount(a) == ccnxName_GetSegmentCount(b)) { + return parcLinkedList_Equals(a->segments, b->segments); + } + return false; +} + +CCNxName * +ccnxName_CreateFormatString(const char *restrict format, ...) +{ + va_list argList; + va_start(argList, format); + + PARCURI *uri = parcURI_CreateFromValist(format, argList); + + CCNxName *result = ccnxName_FromURI(uri); + parcURI_Release(&uri); + + return result; +} + +CCNxName * +ccnxName_FromURI(const PARCURI *uri) +{ + CCNxName *result = NULL; + + PARCURIPath *path = parcURI_GetPath(uri); + if (path != NULL) { + result = ccnxName_Create(); + + for (int i = 0; i < parcURIPath_Count(path); i++) { + CCNxNameSegment *segment = ccnxNameSegment_ParseURISegment(parcURIPath_Get(path, i)); + if (segment == NULL) { + ccnxName_Release(&result); + break; + } + parcLinkedList_Append(result->segments, segment); + ccnxNameSegment_Release(&segment); + } + } + + return result; +} + +CCNxName * +ccnxName_CreateFromCString(const char *uri) +{ + CCNxName *result = NULL; + + PARCURI *parcURI = parcURI_Parse(uri); + if (parcURI != NULL) { + const char *scheme = parcURI_GetScheme(parcURI); + if (strcmp("lci", scheme) == 0 || strcmp("ccnx", scheme) == 0) { + result = ccnxName_FromURI(parcURI); + } + parcURI_Release(&parcURI); + } + + return result; +} + +CCNxName * +ccnxName_CreateFromBuffer(const PARCBuffer *buffer) +{ + char *string = parcBuffer_ToString(buffer); + CCNxName *result = ccnxName_CreateFromCString(string); + parcMemory_Deallocate(&string); + + return result; +} + +CCNxName * +ccnxName_ComposeNAME(const CCNxName *name, const char *suffix) +{ + CCNxNameSegment *suffixSegment = ccnxNameSegment_CreateTypeValueArray(CCNxNameLabelType_NAME, strlen(suffix), suffix); + + CCNxName *result = ccnxName_Append(ccnxName_Copy(name), suffixSegment); + ccnxNameSegment_Release(&suffixSegment); + + return result; +} + +CCNxName * +ccnxName_Append(CCNxName *name, const CCNxNameSegment *segment) +{ + ccnxName_OptionalAssertValid(name); + ccnxNameSegment_OptionalAssertValid(segment); + + parcLinkedList_Append(name->segments, segment); + + return name; +} + +PARCBufferComposer * +ccnxName_BuildString(const CCNxName *name, PARCBufferComposer *composer) +{ + parcBufferComposer_PutString(composer, "ccnx:"); + + size_t count = ccnxName_GetSegmentCount(name); + if (count == 0) { + parcBufferComposer_PutString(composer, "/"); + } else { + for (size_t i = 0; i < count; i++) { + parcBufferComposer_PutString(composer, "/"); + CCNxNameSegment *component = ccnxName_GetSegment(name, i); + ccnxNameSegment_BuildString(component, composer); + } + } + return composer; +} + +char * +ccnxName_ToString(const CCNxName *name) +{ + char *result = NULL; + + PARCBufferComposer *composer = parcBufferComposer_Create(); + if (composer != NULL) { + ccnxName_BuildString(name, composer); + PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer(composer); + result = parcBuffer_ToString(tempBuffer); + parcBuffer_Release(&tempBuffer); + parcBufferComposer_Release(&composer); + } + + return result; +} + +CCNxNameSegment * +ccnxName_GetSegment(const CCNxName *name, size_t index) +{ + return parcLinkedList_GetAtIndex(name->segments, index); +} + +size_t +ccnxName_GetSegmentCount(const CCNxName *name) +{ + return parcLinkedList_Size(name->segments); +} + +int +ccnxName_Compare(const CCNxName *name1, const CCNxName *name2) +{ + if (name1 == NULL) { + if (name2 == NULL) { + return 0; + } + return -1; + } + + // name1 is not NULL + if (name2 == NULL) { + return +1; + } + + // neither is NULL + + size_t name1SegmentCount = ccnxName_GetSegmentCount(name1); + size_t name2SegmentCount = ccnxName_GetSegmentCount(name2); + + size_t mininimumSegments = name1SegmentCount < name2SegmentCount ? name1SegmentCount : name2SegmentCount; + + int result = 0; + + for (size_t i = 0; i < mininimumSegments; i++) { + CCNxNameSegment *segment1 = ccnxName_GetSegment(name1, i); + CCNxNameSegment *segment2 = ccnxName_GetSegment(name2, i); + result = ccnxNameSegment_Compare(segment1, segment2); + if (result != 0) { + break; + } + } + + if (result == 0) { + // we got to the end of the shortest name and they are still equal. + + // name1 is shorter than name 2 + if (name1SegmentCount < name2SegmentCount) { + result = -1; + } + + // name1 is longer than name2 + if (name1SegmentCount > name2SegmentCount) { + result = +1; + } + } + + return result; +} + +PARCHashCode +ccnxName_HashCode(const CCNxName *name) +{ + return ccnxName_LeftMostHashCode(name, ccnxName_GetSegmentCount(name)); +} + +PARCHashCode +ccnxName_LeftMostHashCode(const CCNxName *name, size_t count) +{ + if (count > ccnxName_GetSegmentCount(name)) { + count = ccnxName_GetSegmentCount(name); + } + + PARCHashCode result = 0; + for (int i = 0; i < count; i++) { + PARCHashCode hashCode = ccnxNameSegment_HashCode(ccnxName_GetSegment(name, i)); + result = parcHashCode_HashHashCode(result, hashCode); + } + + return result; +} + +CCNxName * +ccnxName_Trim(CCNxName *name, size_t numberToRemove) +{ + if (numberToRemove > ccnxName_GetSegmentCount(name)) { + numberToRemove = ccnxName_GetSegmentCount(name); + } + + for (int i = 0; i < numberToRemove; i++) { + CCNxNameSegment *segment = parcLinkedList_RemoveLast(name->segments); + ccnxNameSegment_Release(&segment); + } + + return name; +} + +bool +ccnxName_StartsWith(const CCNxName *name, const CCNxName *prefix) +{ + if (ccnxName_GetSegmentCount(prefix) > ccnxName_GetSegmentCount(name)) { + return false; + } + + for (int i = 0; i < ccnxName_GetSegmentCount(prefix); i++) { + CCNxNameSegment *prefix_comp = ccnxName_GetSegment(prefix, i); + CCNxNameSegment *other_comp = ccnxName_GetSegment(name, i); + + if (ccnxNameSegment_Compare(prefix_comp, other_comp) != 0) { + return false; + } + } + return true; +} + +void +ccnxName_Display(const CCNxName *name, int indentation) +{ + parcDisplayIndented_PrintLine(indentation, "CCNxName@%p {", name); + if (name != NULL) { + for (int i = 0; i < ccnxName_GetSegmentCount(name); i++) { + CCNxNameSegment *segment = ccnxName_GetSegment(name, i); + ccnxNameSegment_Display(segment, indentation + 1); + } + } + parcDisplayIndented_PrintLine(indentation, "}"); +} + +CCNxName * +ccnxName_ComposeFormatString(const CCNxName *baseName, const char *restrict format, ...) +{ + va_list argList; + va_start(argList, format); + + char *baseString = ccnxName_ToString(baseName); + + char *suffix; + vasprintf(&suffix, format, argList); + + char *uri; + asprintf(&uri, "%s/%s", baseString, suffix); + free(suffix); + + CCNxName *result = ccnxName_CreateFromCString(uri); + free(uri); + + return result; +} + +CCNxName * +ccnxName_CreatePrefix(const CCNxName *name, size_t length) +{ + CCNxName *result = ccnxName_Create(); + + if (result != NULL) { + if (length > 0) { + size_t numberOfSegmentsAvailable = parcLinkedList_Size(name->segments); + + if (length > numberOfSegmentsAvailable) { + length = numberOfSegmentsAvailable; + } + + for (size_t i = 0; i < length; i++) { + ccnxName_Append(result, ccnxName_GetSegment(name, i)); + } + } + } + + return result; +} diff --git a/libccnx-common/ccnx/common/ccnx_Name.h b/libccnx-common/ccnx/common/ccnx_Name.h new file mode 100755 index 00000000..83cb78ff --- /dev/null +++ b/libccnx-common/ccnx/common/ccnx_Name.h @@ -0,0 +1,738 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file ccnx_Name.h + * @ingroup Naming + * @brief The basic CCNx Name. + * + * This implements an RFC3986 URI compliant identifier in which each path segment carries a label. + * This allows differentiation between resources with otherwise similar identifiers that are not related. + * For example, one resource could be named `"/parc/csl/7"` meaning the 7th version of `"/parc/csl"`, + * while another could mean the 7th page of the resource. + * With labeled segments, the two resources would have unambiguous names, + * such as `"/parc/csl/version=7"` and `"/parc/csl/page=7"`. + * + */ +#ifndef libccnx_ccnx_Name_h +#define libccnx_ccnx_Name_h + +#include <parc/algol/parc_HashCode.h> +#include <ccnx/common/ccnx_NameSegment.h> + +struct ccnx_name; +/** + * @typedef CCNxName + * @brief An RFC3986 URI compliant identifier in which each path segment carries a label. + */ +typedef struct ccnx_name CCNxName; + +/** + * Create a new instance of `CCNxName`, initialized from the given string representation of an LCI URI, + * using dynamically allocated memory. + * + * The URI must be a well-formed URI. + * + * The `CCNxName` instance must be released by calling {@link ccnxName_Release}. + * + * @param [in] uri A null-terminated string representation of the CCNx Name. + * @return non-NULL A pointer to a `CCNxName` instance initialised from the given URI. + * @return NULL An error occured parsing the URI, or the URI contained an invalid specification. + * + * Example: + * @code + * { + * CCNxName *name = ccnxName_CreateFromCString("lci:/parc/csl/media/h2162"); + * + * ccnxName_Release(&name); + * } + * @endcode + */ +CCNxName *ccnxName_CreateFromCString(const char *uri); + +/** + * Create a new instance of `CCNxName`, initialized from the given `PARCURI` representation of an LCI URI, + * using dynamically allocated memory. + * + * The URI must be a well-formed LCI URI. + * + * The `CCNxName` instance must be released by calling {@link ccnxName_Release}. + * + * @param [in] uri A valid instance of PARCURI. + * @return non-NULL A pointer to a `CCNxName` instance initialised from the given URI. + * @return NULL An error occured parsing the URI, or the URI contained an invalid specification. + * + * Example: + * @code + * { + * PARCURI *uri = parcURI_Parse("lci:/parc/csl/media/h2162"); + * CCNxName *name = ccnxName_CreateFromCString(uri); + * + * parcURI_Release(&uri); + * + * ccnxName_Release(&name); + * } + * @endcode + */ +CCNxName *ccnxName_FromURI(const PARCURI *uri); + +/** + * Create a new instance of `CCNxName` from the given format string and variable number of parameters. + * + * @param [in] format A pointer to a nul-terminated printf format string + * @param [in] ... A variable number of parameters. + * + * @return non-NULL A pointer to a valid CCNxName instance. + * @return NULL An error occurred. + * + * Example: + * @code + * { + * CCNxName *name = ccnxName_CreateFormatString("lci://%s/object", "parc.com"); + * } + * @endcode + */ +CCNxName *ccnxName_CreateFormatString(const char *restrict format, ...); + +/** + * Create a new instance of `CCNxName`, initialized from a string representation of a LCI URI contained in the given PARCBuffer, + * starting at the current position and terminating with a zero-byte in the buffer. + * using dynamically allocated memory. + * + * The URI must be a well-formed URI. + * + * The `CCNxName` instance must be released by calling {@link ccnxName_Release}. + * + * @param [in] uri A null-terminated string representation of the CCNx Name. + * @return non-NULL A pointer to a `CCNxName` instance initialised from the given URI. + * @return NULL An error occured parsing the URI, or the URI contained an invalid specification. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_AllocateCString("lci:/parc/csl/media/h2162"); + * CCNxName *name = ccnxName_CreateFromBuffer(buffer); + * + * ccnxName_Release(&name); + * parcBuffer_Release(&buffer); + * } + * @endcode + */ +CCNxName *ccnxName_CreateFromBuffer(const PARCBuffer *buffer); + +/** + * Increase the number of references to a `CCNxName` instance. + * + * Note that new `CCNxName` is not created, + * only that the given `CCNxName` reference count is incremented. + * Discard the reference by invoking {@link ccnxName_Release}. + * + * @param [in] name A pointer to the original `CCNxName`. + * @return The value of the input parameter @p name. + * + * Example: + * @code + * { + * CCNxName *original = ccnxName_Create(); + * + * CCNxName *reference = ccnxName_Acquire(original); + * + * ccnxName_Release(&original); + * ccnxName_Release(&reference); + * } + * @endcode + * + * @see ccnxName_Release + */ +CCNxName *ccnxName_Acquire(const CCNxName *name); + +/** + * Release a previously acquired reference to the specified instance, + * decrementing the reference count for the instance. + * + * The pointer to the instance is set to NULL as a side-effect of this function. + * + * If the invocation causes the last reference to the instance to be released, + * the instance is deallocated and the instance's implementation will perform + * additional cleanup and release other privately held references. + * + * @param [in,out] nameP A pointer to a pointer to the instance to release. + * + * Example: + * @code + * { + * CCNxName *name = ccnxName_Create(...); + * + * ccnxName_Release(&name); + * } + * @endcode + * + * @see {@link ccnxName_Acquire} + */ +void ccnxName_Release(CCNxName **nameP); + +#ifdef Libccnx_DISABLE_VALIDATION +# define ccnxName_OptionalAssertValid(_instance_) +#else +# define ccnxName_OptionalAssertValid(_instance_) ccnxName_AssertValid(_instance_) +#endif + +/** + * Assert that an instance of `CCNxName` is valid. + * + * If the instance is not valid, terminate via {@link trapIllegalValue} + * + * Valid means the internal state of the type is consistent with its + * required current or future behaviour. + * This may include the validation of internal instances of types. + * + * @param [in] name A pointer to a `CCNxName` instance. + * + * Example: + * @code + * { + * CCNxName *name = ccnxName_CreateFromCString("lci:/parc/ccn/things/p1e"); + * + * ccnxName_AssertValid(name); + * + * ccnxName_Release(&name); + * } + * @endcode + */ +void ccnxName_AssertValid(const CCNxName *name); + +/** + * Determine if an instance of `CCNxName` is valid. + * + * Valid means the internal state of the type is consistent with its + * required current or future behaviour. + * This may include the validation of internal instances of types. + * + * @param [in] name A pointer to a `CCNxName` instance. + * @return true If the instance is valid. + * @return false if the instance is not valid. + * + * Example: + * @code + * { + * CCNxName *name = ccnxName_CreateFromCString("lci:/parc/ccn/things/p1e"); + * + * if (ccnxName_IsValid(name) == true) { + * ... + * } + * + * ccnxName_Release(&name); + * } + * @endcode + */ +bool ccnxName_IsValid(const CCNxName *name); + +/** + * Append a representation of the specified `CCNxName` instance to the given + * {@link PARCBufferComposer}. + * + * The CCN URI representing the {@link CCNxNameSegment}'s associated with this `CCNxName` is + * appended to the supplied {@link PARCBufferComposer}. + * + * @param [in] name A pointer to a `CCNxName` instance whose representation should be appended to the @p composer. + * @param [in,out] composer A pointer to a `PARCBufferComposer` instance to be modified. + * + * @return NULL Cannot allocate memory. + * @return non-NULL The @p composer. + * + * Example: + * @code + * { + * PARCBufferComposer *result = parcBufferComposer_Create(); + * + * ccnxName_BuildString(instance, result); + * + * char *string = parcBuffer_ToString(parcBufferComposer_ProduceBuffer(result)); + * printf("Hello: %s\n", string); + * parcMemory_Deallocate(string); + * + * parcBufferComposer_Release(&result); + * } + * @endcode + * + * @see `CCNxNameSegment` + */ +PARCBufferComposer *ccnxName_BuildString(const CCNxName *name, PARCBufferComposer *composer); + +/** + * Produce a null-terminated string representation of the specified instance. + * + * The non-null result must be freed by the caller via {@link parcMemory_Deallocate}. + * + * @param [in] name A pointer to the `CCNxName` instance. + * + * @return NULL Cannot allocate memory. + * @return non-NULL A pointer to an allocated, + * null-terminated C string that must be deallocated via `parcMemory_Deallocate()`. + * + * Example: + * @code + * { + * CCNxName *instance = ccnxName_Create(); + * + * char *string = ccnxName_ToString(instance); + * + * if (string != NULL) { + * printf("Hello: %s\n", string); + * parcMemory_Deallocate(string); + * } else { + * printf("Cannot allocate memory\n"); + * } + * + * ccnxName_Release(&instance); + * } + * @endcode + * + * @see {@link ccnxName_BuildString} + * @see {@link ccnxName_Display} + * @see {@link parcMemory_Deallocate} + */ +char *ccnxName_ToString(const CCNxName *name); + +/** + * Print a human readable representation of the given `CCNxName`. + * + * @param [in] name A pointer to the `CCNxName` to display. + * @param [in] indentation The level of indentation to use to pretty-print the output. + * + * Example: + * @code + * { + * CCNxName *name = ccnxName_CreateFromCString("lci:/parc/csl/sensors/radiation/11"); + * + * ccnxInterest_Display(name, 0); + * + * ccnxName_Release(&name); + * } + * @endcode + */ +void ccnxName_Display(const CCNxName *name, int indentation); + +/** + * Append a {@link CCNxNameSegment} to the given `CCNxName`. + * + * Append the `CCNxNameSegment` to the given `CCNxName`. + * The given `CCNxName` is modified. + * + * @param [in,out] name The base `CCNxName` to append the @p segment to. + * @param [in] segment The segment to append to @p name. + * @return The modifed @p name. + * + * Example: + * @code + * { + * CCNxName *name1 = ccnxName_CreateFromCString("lci:/a/b/c/d"); + * CCNxName *name2 = ccnxName_Create(); + * + * CCNxNameSegment *segment = ccnxName_GetSegment(name1, 0); + * + * ccnxName_Append(name2, segment); + * + * ccnxName_Release(&name1); + * ccnxName_Release(&name2); + * } + * @endcode + */ +CCNxName *ccnxName_Append(CCNxName *name, const CCNxNameSegment *segment); + +/** + * Determine if a `CCNxName` is starts with another. + * + * Each {@link CCNxNameSegment} in @p prefix is tested against the corresponding `CCNxNameSegment` + * in @p name. + * + * @param [in] name A pointer to a `CCNxName` instance. + * @param [in] prefix A pointer to a `CCNxName` instance to be tested as a prefix of `name`. + * + * @return `true` If the given @p name starts with @p prefix. + * @return `false` If the given @p name does not start with @p prefix. + * + * Example: + * @code + * { + * CCNxName *name = ccnxName_CreateFromCString("lci:/parc/csl/sensors/radiation/17"); + * CCNxName *prefix = ccnxName_CreateFromCString("lci:/parc/csl"); + * + * if (ccnxName_StartsWith(name, prefix)) { + * ... + * } + * + * ccnxName_Release(&name); + * ccnxName_Release(&prefix); + * } + * @endcode + */ +bool ccnxName_StartsWith(const CCNxName *name, const CCNxName *prefix); + +/** + * Create a copy of the specified `CCNxName` instance, producing a new, independent, instance + * from dynamically allocated memory. + * + * This a deep copy. All referenced memory is copied. The created instance of `CCNxName` must + * be released by calling {@link ccnxName_Release}(). + * + * @param [in] originalName The `CCNxName` to copy + * @return A new, independent copy of the given `CCNxName`. + * + * Example: + * @code + * { + * CCNxName *originalName = ccnxName_CreateFromCString("lci:/parc/csl/sensors/radiation/17"); + * CCNxName *copy = ccnxName_Copy(originalName); + * + * ... + * + * ccnxName_Release(&originalName); + * ccnxName_Release(©); + * } + * @endcode + */ +CCNxName *ccnxName_Copy(const CCNxName *originalName); + +/** + * Determine if two `CCNxName` instances are equal. + * + * The following equivalence relations on non-null `CCNxName` instances are maintained: + * + * * It is reflexive: for any non-null reference value x, `ccnxName_Equals(x, x)` must return true. + * + * * It is symmetric: for any non-null reference values x and y, `ccnxName_Equals(x, y)` must return true if and only if + * `ccnxName_Equals(y x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `ccnxName_Equals(x, y)` returns true and + * `ccnxName_Equals(y, z)` returns true, + * then `ccnxName_Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple invocations of `ccnxName_Equals(x, y)` + * consistently return true or consistently return false. + * + * * For any non-null reference value x, `ccnxName_Equals(x, NULL)` must return false. + * + * @param [in] name1 A pointer to a `CCNxName` instance. + * @param [in] name2 A pointer to a `CCNxName` instance. + * + * @return True If the given `CCNxName` instances are equal + * @return False Otherwise + * + * Example: + * @code + * { + * CCNxName *orig = ccnxName_CreateFromCString("lci:/parc/csl/sensors/radiation/17"); + * CCNxName *copy = ccnxName_Copy(orig); + * + * if (ccnxName_Equals(orig, copy)) { + * // true + * } else { + * // false + * } + * ccnxName_Release(&orig); + * ccnxName_Release(©); + * } + * @endcode + * + * @see {@link ccnxName_Compare} + */ +bool ccnxName_Equals(const CCNxName *name1, const CCNxName *name2); + +/** + * Create a new `CCNxName` object + * + * The instance is reference counted with an initial count of 1. + * Additional references are acquired via the function {@link ccnxName_Acquire} + * and these references are inidividually released via {@link ccnxName_Release} + * + * @return A pointer to a new instance of a `CCNxName`. + * + * Example: + * @code + * { + * CCNxName *name = ccnxName_Create(); + * + * ... + * + * ccnxName_Release(&name); + * } + * @endcode + */ +CCNxName *ccnxName_Create(void); + +/** + * Compare @p name1 to @p name2 using CCNx canonical ordering (shortlex). + * + * `NULL` is considered the shortest name, so `(NULL, non-NULL) -> -1` and + * `(NULL, NULL) -> 0`, `(non-NULL, NULL) -> +1`. + * + * @param [in] name1 A pointer to a `CCNxName` instance. + * @param [in] name2 A pointer to a `CCNxName` instance. + * + * Returns: + * <ul> + * <li>-1 for name1 < name2</li> + * <li> 0 for name1 = name2</li> + * <li>+1 for name1 > name2</li> + * </ul> + * + * Example: + * @code + * { + * CCNxName *orig = ccnxName_CreateFromCString("lci:/parc/csl/sensors/radiation/17"); + * CCNxName *copy = ccnxName_Copy(orig); + * + * if (ccnxName_Compare(orig, copy)) { + * ... + * } + * + * ccnxName_Release(&orig); + * ccnxName_Release(©); + * } + * @endcode + * + * @see {@link ccnxName_Equals} + */ +int ccnxName_Compare(const CCNxName *name1, const CCNxName *name2); + +/** + * Return a pointer to the {@link CCNxNameSegment} instance for the specified `CCNxName` at the given index. + * The index must be greater than or equal to zero and less than {@link ccnxName_GetSegmentCount}(). + * + * @param [in] name The target `CCNxName` + * @param [in] index The index into the @p name from which to retrieve the `CCNxNameSegment`. + * + * @return A pointer to a `CCNxNameSegment`. + * + * Example: + * @code + * { + * CCNxName *name = ccnxName_CreateFromCString("lci:/parc/csl/things/b00se"); + * CCNxNameSegment *segment2 = ccnxName_GetSegment(name, 2); + * + * ccnxName_Release(&name); + * } + * @endcode + */ +CCNxNameSegment *ccnxName_GetSegment(const CCNxName *name, size_t index); + +/** + * Get the number of `CCNxNameSegments` in the specified `CCNxName`. + * + * @param [in] name A pointer to an instance of `CCNxName`. + * + * @return The number of name segments in @p name. + * + * Example: + * @code + * { + * CCNxName *name = ccnxName_CreateFromCString("lci:/parc/csl/things/b00se"); + * size_t segmentCount = ccnxName_GetSegmentCount(name); + * + * printf("Number of segments: %d\n", segmentCount); + * + * ccnxName_Release(&name); + * } + * @endcode + */ +size_t ccnxName_GetSegmentCount(const CCNxName *name); + +/** + * Return a hashcode for the given `CCNxName`. + * + * Whenever `HashCode()` is invoked on the same object more than once within the same execution environment, + * the HashCode function must consistently return the same integer, provided no information used in Equals() + * comparisons on the same object is modified. + * + * This integer need not remain consistent from one execution of an application to another execution of the same application. + * + * If two objects are equal according to the Equals() function, + * then calling the hashCode method on each of the two objects must produce the same integer result. + * + * It is not required that if two objects are unequal according to the Equals() function, + * then calling the Equals() method on each of the two objects must produce distinct integer results. + * However, the programmer should be aware that producing distinct integer results for unequal objects + * may improve the performance of some data structures. + * + * @param name [in] A pointer to an instance of `CCNxName`. + * @return A uint32_t hash code. + * + * Example: + * @code + * { + * CCNxName *name = ccnxName_CreateFromCString("lci:/parc/ccn/things/r00"); + * + * uint32_t hashCode = ccnx_LeftMostHashCode(name); + * + * ccnxName_Release(&name); + * { + * @endcode + */ +PARCHashCode ccnxName_HashCode(const CCNxName *name); + +/** + * Return a hash of the leftmost @p count {@link CCNxNameSegment}s in a `CCNxName`. + * + * See @{link ccnxName_HashCode} for more information. + * + * @param [in] name A pointer to a `CCNxName` instance. + * @param [in] count The number, starting from the left, of path segments to use to compute the hash. + * + * @return A uint32_t hash code. + * + * Example: + * @code + * { + * CCNxName *name = ccnxName_CreateFromCString("lci:/parc/ccn/things/r00"); + * + * uint32_t hashCode = ccnx_LeftMostHashCode(name, 2); + * + * ccnxName_Release(&name); + * } + * @endcode + */ +PARCHashCode ccnxName_LeftMostHashCode(const CCNxName *name, size_t count); + +/** + * Trim @p numberToRemove segments from the end of the specified `CCNxName`. + * + * If @p numberToRemove is greater than the number of segments in the name, + * all segments are removed. + * If @p numberToRemove is 0, nothing happens. + * The name segments are destroyed. + * + * @param [in,out] name A pointer to a `CCNxName` instance to trim. + * @param [in] numberToRemove The number of rightmost segments to remove from the name. + * + * @return The value of @p name + * + * Example: + * @code + * { + * CCNxName *name = ccnxName_CreateFromCString("lci:/parc/ccn/things/b00se"); + * + * name = ccnxName_Trim(name, 2); + * + * ccnxName_Release(&name); + * } + * @endcode + * + */ +CCNxName *ccnxName_Trim(CCNxName *name, size_t numberToRemove); + +/** + * Compose a new CCNxName instance consisting of the given @prefix appended with @p suffix as a `CCNxNameLabelType_NAME`. + * + * @param [in] prefix A pointer to a valid CCNxName instance containing the prefix of the new name. + * @param [in] suffix A pointer to a nul-terminated C string that is appended to the new name. + * + * @return non-NULL A pointer to a valid CCNxName instance. + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +CCNxName *ccnxName_ComposeNAME(const CCNxName *prefix, const char *suffix); + + +/** + * Get the Nth segment number of the given `CCNxName` + * + * <#Paragraphs Of Explanation#> + * + * @param [in] name A pointer to a valid `CCNxName` instance. + * @param [in] nthNumber The nth segment number in the given name, or SIZE_MAX + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +uint64_t ccnxName_GetNthSegmentNumber(const CCNxName *name, size_t nthNumber); + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in#> | <#out#> | <#in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +uint64_t ccnxName_GetSegmentNumber(const CCNxName *name); + +/** + * Create a new CCNxName instance composed of the given CCNxName with the parsed result of the format string appended. + * + * @param [in] baseName The base name of the new CCNxName + * @param [in] format A printf(3) format string + * + * @return non-NULL A pointer to a valid `CCNxName` segment. + * @return NULL The object could not be created. + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +CCNxName *ccnxName_ComposeFormatString(const CCNxName *baseName, const char *restrict format, ...); + +/** + * Create a CCNxName that is a prefix of another. + * + * If the specified length is greater than the number of segments available, + * the result is a new name that is a copy of the old name. + * + * @param [in] name A pointer to a valid `CCNxName` instance. + * @param [in] length The number of `CCNxNameSegments` the prefix must include. + * + * @return non-NULL A pointer to a valid CCNxName. + * @return NULL An error occurred. + * + * Example: + * @code + * { + * CCNxName *a = ccnxName_CreateFromCString("ccnx:/a/b/c"); + * + * CCNxName *prefix = ccnxName_CreatePrefix(a, 1); + * + * // prefix is equal to the name "ccnx:/a" + * + * ccnxName_Release(&a); + * ccnxName_Release(&prefix); + * ccnxName_Release(&actual); + * } + * @endcode + */ +CCNxName *ccnxName_CreatePrefix(const CCNxName *name, size_t length); +#endif // libccnx_ccnx_Name_h diff --git a/libccnx-common/ccnx/common/ccnx_NameLabel.c b/libccnx-common/ccnx/common/ccnx_NameLabel.c new file mode 100755 index 00000000..290e6069 --- /dev/null +++ b/libccnx-common/ccnx/common/ccnx_NameLabel.c @@ -0,0 +1,316 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ +#include <config.h> +#include <stdio.h> +#include <stdint.h> +#include <ctype.h> + +#include <parc/algol/parc_Object.h> +#include <LongBow/runtime.h> + +#include <ccnx/common/ccnx_NameLabel.h> + +struct ccnx_name_label { + CCNxNameLabelType type; + PARCBuffer *parameter; +}; + +static struct CCNxNameLabelMnemonic { + const char *mnemonic; + const CCNxNameLabelType type; +} CCNxNameLabelMnemonic[] = { + { CCNxNameLabel_Name, CCNxNameLabelType_NAME }, + { CCNxNameLabel_InterestPayloadId, CCNxNameLabelType_PAYLOADID }, + { CCNxNameLabel_Serial, CCNxNameLabelType_SERIAL }, + { CCNxNameLabel_Chunk, CCNxNameLabelType_CHUNK }, + { CCNxNameLabel_ChunkMeta, CCNxNameLabelType_CHUNKMETA }, + { CCNxNameLabel_App, CCNxNameLabelType_APP0 }, + { CCNxNameLabel_Time, CCNxNameLabelType_TIME }, + { NULL, CCNxNameLabelType_Unknown }, +}; + +static bool +_ccnxNameLabel_Destructor(CCNxNameLabel **labelP) +{ + assertNotNull(labelP, "Parameter must be a non-null pointer to a CCNxNameLabels pointer."); + + CCNxNameLabel *label = *labelP; + if (label->parameter != NULL) { + parcBuffer_Release(&label->parameter); + } + return true; +} + +parcObject_Override(CCNxNameLabel, PARCObject, + .destructor = (PARCObjectDestructor *) _ccnxNameLabel_Destructor); + +parcObject_ImplementAcquire(ccnxNameLabel, CCNxNameLabel); + +parcObject_ImplementRelease(ccnxNameLabel, CCNxNameLabel); + +CCNxNameLabel * +ccnxNameLabel_Create(CCNxNameLabelType type, const PARCBuffer *parameter) +{ + CCNxNameLabel *result = NULL; + + if (type != CCNxNameLabelType_BADNAME && type != CCNxNameLabelType_Unknown) { + result = parcObject_CreateInstance(CCNxNameLabel); + if (result != NULL) { + result->type = type; + result->parameter = parameter == NULL ? NULL : parcBuffer_Acquire(parameter); + } + } + + return result; +} + +CCNxNameLabelType +ccnxNameLabel_GetType(const CCNxNameLabel *label) +{ + ccnxNameLabel_OptionalAssertValid(label); + + return label->type; +} + +PARCBuffer * +ccnxNameLabel_GetParameter(const CCNxNameLabel *label) +{ + ccnxNameLabel_OptionalAssertValid(label); + return label->parameter; +} + +static const char * +_ccnxNameLabelType_ToMnemonic(CCNxNameLabelType type) +{ + const char *result = NULL; + + for (struct CCNxNameLabelMnemonic *p = &CCNxNameLabelMnemonic[0]; p->mnemonic != NULL; p++) { + if (p->type == type) { + result = p->mnemonic; + break; + } + } + + return result; +} + +static CCNxNameLabelType +_ccnxNameLabelType_ResolveNumeric(PARCBuffer *label) +{ + CCNxNameLabelType result = (CCNxNameLabelType) parcBuffer_ParseNumeric(label); + + return result; +} + +static CCNxNameLabelType +_ccnxNameLabelType_ResolveMnemonic(const PARCBuffer *label) +{ + CCNxNameLabelType result = CCNxNameLabelType_Unknown; + + size_t labelLength = parcBuffer_Remaining(label); + char *labelAsBytes = parcBuffer_Overlay((PARCBuffer *) label, 0); + + for (struct CCNxNameLabelMnemonic *p = &CCNxNameLabelMnemonic[0]; p->mnemonic != NULL; p++) { + if (strncasecmp(p->mnemonic, labelAsBytes, labelLength) == 0) { + result = p->type; + break; + } + } + + return result; +} + +static CCNxNameLabelType +_ccnxNameLabelType_Resolve(PARCBuffer *label) +{ + CCNxNameLabelType result = CCNxNameLabelType_NAME; + + if (label != NULL) { + if (parcBuffer_Remaining(label) > 0) { + if (isdigit(parcBuffer_PeekByte(label))) { + result = _ccnxNameLabelType_ResolveNumeric(label); + } else { + result = _ccnxNameLabelType_ResolveMnemonic(label); + } + } else { + result = CCNxNameLabelType_NAME; + } + } + + return result; +} + +/* + * Parse the 'label [":" param] ' portion of an lpv-segment + * + * lpv-segment = label [":" param] "=" s-value + * v-segment = s-value + * label = alpha-t / num-t + * param = alpha-t / num-t + * s-value = *(s-pchar) + * + * number = + * number + */ +CCNxNameLabel * +ccnxNameLabel_Parse(PARCBuffer *buffer) +{ + PARCBuffer *label = NULL; + PARCBuffer *parameter = NULL; + + // When complete, the buffer's position will be set to the first byte of the value portion. + if (parcBuffer_SkipTo(buffer, 1, (uint8_t *) "=")) { + label = parcBuffer_Flip(parcBuffer_Duplicate(buffer)); + if (parcBuffer_Remaining(label) > 0) { + parcBuffer_SetPosition(buffer, parcBuffer_Position(buffer) + 1); + + if (parcBuffer_SkipTo(label, 1, (uint8_t *) ":")) { + size_t colon = parcBuffer_Position(label); + parcBuffer_SetPosition(label, colon + 1); + parameter = parcBuffer_Slice(label); + + parcBuffer_SetPosition(label, colon); + } + + label = parcBuffer_Flip(label); + } else { + parcBuffer_Release(&label); + return NULL; + } + } else { + parcBuffer_Rewind(buffer); + } + + CCNxNameLabelType type = CCNxNameLabelType_NAME; + + if (label != NULL) { + type = _ccnxNameLabelType_Resolve(label); + if (type == CCNxNameLabelType_App(0)) { + if (parameter != NULL) { + type = CCNxNameLabelType_App(parcBuffer_ParseNumeric(parameter)); + parcBuffer_Release(¶meter); + } + } + parcBuffer_Release(&label); + } + + CCNxNameLabel *result = ccnxNameLabel_Create(type, parameter); + + if (parameter != NULL) { + parcBuffer_Release(¶meter); + } + + return result; +} + +bool +ccnxNameLabel_Equals(const CCNxNameLabel *x, const CCNxNameLabel *y) +{ + bool result = false; + + if (x == y) { + result = true; + } else if (x == NULL || y == NULL) { + result = false; + } else { + if (x->type == y->type) { + if (parcBuffer_Equals(x->parameter, y->parameter)) { + result = true; + } + } + } + + return result; +} + +CCNxNameLabel * +ccnxNameLabel_Copy(const CCNxNameLabel *label) +{ + ccnxNameLabel_OptionalAssertValid(label); + + PARCBuffer *parameter = (label->parameter == NULL) ? NULL : parcBuffer_Copy(label->parameter); + CCNxNameLabel *result = ccnxNameLabel_Create(label->type, parameter); + if (parameter != NULL) { + parcBuffer_Release(¶meter); + } + + return result; +} + +bool +ccnxNameLabel_IsValid(const CCNxNameLabel *label) +{ + bool result = false; + + if (label != NULL) { + if (label->type != CCNxNameLabelType_Unknown && label->type != CCNxNameLabelType_BADNAME) { + if (label->parameter != NULL) { + if (parcBuffer_IsValid(label->parameter)) { + result = true; + } + } else { + result = true; + } + } + } + return result; +} + +void +ccnxNameLabel_AssertValid(const CCNxNameLabel *label) +{ + trapIllegalValueIf(ccnxNameLabel_IsValid(label) == false, "Encountered an invalid CCNxNameLabel instance."); +} + +PARCBufferComposer * +ccnxNameLabel_BuildString(const CCNxNameLabel *label, PARCBufferComposer *composer) +{ + ccnxNameLabel_OptionalAssertValid(label); + + if (label->type >= CCNxNameLabelType_App(0) && label->type <= CCNxNameLabelType_App(4096)) { + parcBufferComposer_Format(composer, "%s:%u=", CCNxNameLabel_App, label->type - CCNxNameLabelType_App(0)); + } else { + const char *mnemonic = _ccnxNameLabelType_ToMnemonic(label->type); + if (mnemonic == NULL) { + parcBufferComposer_Format(composer, "%u", label->type); + } else { + parcBufferComposer_PutString(composer, mnemonic); + } + + if (label->parameter != NULL) { + parcBufferComposer_PutString(composer, ":"); + parcBufferComposer_PutBuffer(composer, label->parameter); + } + parcBufferComposer_PutString(composer, "="); + } + + return composer; +} + +char * +ccnxNameLabel_ToString(const CCNxNameLabel *label) +{ + PARCBufferComposer *composer = parcBufferComposer_Create(); + ccnxNameLabel_BuildString(label, composer); + + char *result = parcBufferComposer_ToString(composer); + + parcBufferComposer_Release(&composer); + + return result; +} diff --git a/libccnx-common/ccnx/common/ccnx_NameLabel.h b/libccnx-common/ccnx/common/ccnx_NameLabel.h new file mode 100755 index 00000000..5a4ff324 --- /dev/null +++ b/libccnx-common/ccnx/common/ccnx_NameLabel.h @@ -0,0 +1,374 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file ccnx_NameLabel.h + * @ingroup Naming + * @brief The possible types of CCNx Name Segments, and utilities to extract and encode them. + * + * Every CCNxName is comprised of CCNxNameSegments, and each CCNxNameSegment has a type associated with it. + * For example, it may specify a simple name (`CCNxNameLabelType_NAME`), + * content chunk numbers (`CCNxNameLabelType_CHUNK`), or any other type defined in `CCNxNameLabelType`. + * + * The type of a name is comprised of a label and an option parameter. + * The label may be a decimal or hexadecimal representation of the type value, or a mnemonic like "Name", "Serial". + * + */ +#ifndef libccnx_ccnx_NameType_h +#define libccnx_ccnx_NameType_h + +#include <stdint.h> +#include <stdbool.h> + +#include <parc/algol/parc_Buffer.h> +#include <parc/algol/parc_BufferComposer.h> + +#define CCNxNameLabelType_APP0 ((CCNxNameLabelType) 0x1000) +#define CCNxNameLabelType_APP4096 ((CCNxNameLabelType) (CCNxNameLabelType_APP0 + 4096)) + +/** + * Compose a CCNx Name Label Type in the Application-specific type space. + */ +#define CCNxNameLabelType_App(_n_) (CCNxNameLabelType_APP0 + (CCNxNameLabelType) _n_) + +/** + * @typedef CCNxNameLabelType + * @brief An enumeration of possible CCNxName types. + */ +typedef enum { + CCNxNameLabelType_BADNAME = 0x0000, + CCNxNameLabelType_NAME = 0x0001, // Name: CCNx Messages in TLV Format + CCNxNameLabelType_PAYLOADID = 0x0002, // Payload Hash: CCNx Messages in TLV Format + CCNxNameLabelType_BINARY = 0x0003, // Binary segment + CCNxNameLabelType_CHUNK = 0x0010, // Segment Number: CCNx Content Object Segmentation + CCNxNameLabelType_CHUNKMETA = 0x0011, // Metadata + CCNxNameLabelType_TIME = 0x0012, // Time: CCNx Publisher Serial Versioning + CCNxNameLabelType_SERIAL = 0x0013, // Serial Number: CCNx Publisher Serial Versioning + CCNxNameLabelType_Unknown = 0xfffff +} CCNxNameLabelType; + +/** + * These definitions should agree with the CCNxNameLabelType enumeration to avoid confusion. + * These definitions must agree with the specification and with the CCNxNameLabelType_String definitions. + * + * Use these definition in constructing string representations of names. + * For example "lci://" CCNxNameLabel_Serial "/1" + * + * @def CCNxNameLabelType_NameLabel The name + */ +#define CCNxNameLabel_Name "Name" +#define CCNxNameLabel_InterestPayloadId "PayloadId" +#define CCNxNameLabel_Chunk "Chunk" +#define CCNxNameLabel_ChunkMeta "ChunkMeta" // "17" +#define CCNxNameLabel_Time "Time" +#define CCNxNameLabel_Serial "Serial" +#define CCNxNameLabel_App "App" + +#define CCNxNameLabelType_LabelApp(_n_) "App:" #_n_ + +struct ccnx_name_label; +typedef struct ccnx_name_label CCNxNameLabel; + +/** + * Parse a PARCBuffer containing a CCN LCI Name Label. + * + * When complete, the buffer's position will be set to the first byte of the value portion. + * + * @param [in] buffer A pointer to a valid PARCBuffer instance. + * + * @return NULL Memory could not be allocated + * @return non-NULL A pointer to a valid CCNxNameLabel instance. + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_WrapCString("App:1=value"); + * CCNxNameLabel *label = ccnxNameLabel_Parse(buffer); + * + * parcBuffer_Release(&buffer); + * ccnxNameLabel_Release(&label); + * } + * @endcode + */ +CCNxNameLabel *ccnxNameLabel_Parse(PARCBuffer *buffer); + +/** + * Create an instance of `CCNxNameLabel`. + * + * @param [in] type A CCNxNameLabelType value + * @param [in] parameter The value NULL or a pointer to a valid `PARCBuffer` instance. + * + * @return NULL Memory could not be allocated, or the given `CCNxNameLabelType` is one of `CCNxNameLabelType_BADNAME` or `CCNxNameLabelType_Unknown`. + * @return non-NULL A pointer to a valid CCNxNameLabelType instance. + * + * Example: + * @code + * { + * CCNxNameLabel *label = ccnxNameLabel_Create(CCNxNameLabelType_NAME, NULL); + * + * ccnxNameLabel_Release(&label); + * } + * @endcode + */ +CCNxNameLabel *ccnxNameLabel_Create(CCNxNameLabelType type, const PARCBuffer *parameter); + +/** + * Increase the number of references to a `CCNxNameLabel`. + * + * Note that new `CCNxNameLabel` is not created, + * only that the given `CCNxNameLabel` reference count is incremented. + * Discard the reference by invoking `ccnxNameLabel_Release`. + * + * @param [in] label A pointer to a `CCNxNameLabel` instance. + * + * @return The input `CCNxNameLabel` pointer. + * + * Example: + * @code + * { + * CCNxNameLabel *label = ccnxNameLabel_Create(CCNxNameLabelType_NAME, NULL); + * + * CCNxNameLabel *x_2 = ccnxNameLabel_Acquire(label); + * + * ccnxNameLabel_Release(&label); + * ccnxNameLabel_Release(&x_2); + * } + * @endcode + */ +CCNxNameLabel *ccnxNameLabel_Acquire(const CCNxNameLabel *label); + +/** + * Release a previously acquired reference to the specified instance, + * decrementing the reference count for the instance. + * + * The pointer to the instance is set to NULL as a side-effect of this function. + * + * If the invocation causes the last reference to the instance to be released, + * the instance is deallocated and the instance's implementation will perform + * additional cleanup and release other privately held references. + * + * @param [in,out] labelPtr A pointer to a pointer to the instance to release. + * + * Example: + * @code + * { + * CCNxNameLabel *label = ccnxNameLabel_Create(CCNxNameLabelType_NAME, NULL); + * + * ccnxNameLabel_Release(&label); + * } + * @endcode + */ +void ccnxNameLabel_Release(CCNxNameLabel **labelPtr); + +/** + * Get the CCNxNameLabelType for the given `CCNxNameLabel`. + * + * @param [in] label A pointer to a valid `CCNxNameLabel` instance. + * + * @return The CCNxNameLabelType for the given `CCNxNameLabel` + * + * Example: + * @code + * { + * CCNxNameLabel *label = ccnxNameLabel_Create(CCNxNameLabelType_NAME, NULL); + * + * CCNxNameLabelType type = ccnxNameLabel_GetType(label); + * + * ccnxNameLabel_Release(&label); + * } + * @endcode + */ +CCNxNameLabelType ccnxNameLabel_GetType(const CCNxNameLabel *label); + +/** + * Get the parameter for the given `CCNxNameLabel`. + * + * @param [in] label A pointer to a valid `CCNxNameLabel` instance. + * + * @return NULL, or a pointer to a valid `PARCBuffer` instance. + * + * Example: + * @code + * { + * PARCBuffer *parameter = parcBuffer_WrapCString("2"); + * CCNxNameLabel *label = ccnxNameLabel_Create(CCNxNameLabelType_APP, parameter); + * parcBuffer_Release(¶meter); + * + * PARCBuffer *param = ccnxNameLabel_GetParameter(label); + * + * ccnxNameLabel_Release(&label); + * } + * @endcode + */ +PARCBuffer *ccnxNameLabel_GetParameter(const CCNxNameLabel *label); + +/** + * Append a representation of the specified `CCNxNameLabel` instance to the given + * {@link PARCBufferComposer}. + * + * The representation is the canonical representation for a CCN LCI name segment. + * + * @param [in] name A pointer to a `CCNxNameLabel` instance whose representation should be appended to the @p composer. + * @param [in,out] composer A pointer to a `PARCBufferComposer` instance to be modified. + * + * @return NULL Cannot allocate memory. + * @return non-NULL The @p composer. + * + * Example: + * @code + * { + * PARCBufferComposer *result = parcBufferComposer_Create(); + * + * PARCBuffer *parameter = parcBuffer_WrapCString("2"); + * CCNxNameLabel *label = ccnxNameLabel_Create(CCNxNameLabelType_APP, parameter); + * parcBuffer_Release(¶meter); + * + * PARCBuffer *param = ccnxNameLabel_GetParameter(label); + * + * ccnxNameLabel_BuildString(label composer); + * + * ccnxNameLabel_Release(&label); + * + * char *string = parcBuffer_ToString(parcBufferComposer_ProduceBuffer(result)); + * printf("Hello: %s\n", string); + * parcMemory_Deallocate(string); + * + * parcBufferComposer_Release(&result); + * } + * @endcode + * + * @see `CCNxNameSegment` + */ +PARCBufferComposer *ccnxNameLabel_BuildString(const CCNxNameLabel *label, PARCBufferComposer *composer); + +/** + * Create a copy of the specified `CCNxNameLabel` instance, producing a new, independent, instance + * from dynamically allocated memory. + * + * This a deep copy. All referenced memory is copied. The created instance of `CCNxNameLabel` must + * be released by calling {@link ccnxNameLabel_Release}(). + * + * @param [in] label The `CCNxNameLabel` to copy. + * + * @return NULL Memory could not be allocated. + * @return non-NULL A pointer to a new, independent copy of the given `CCNxName`. + * + * Example: + * @code + * { + * CCNxNameLabel *original = ccnxNameLabel_Create(CCNxNameLabelType_NAME); + * CCNxNameLabel *copy = ccnxNameLabel_Copy(original); + * + * ... + * + * ccnxNameLabel(&original); + * ccnxNameLabel(©); + * } + * @endcode + */ +CCNxNameLabel *ccnxNameLabel_Copy(const CCNxNameLabel *label); + +/** + * Determine if two `CCNxNameLabel` instances are equal. + * + * The following equivalence relations on non-null `CCNxNameLabel` instances are maintained: + * + * * It is reflexive: for any non-null reference value x, `ccnxNameLabel_Equals(x, x)` must return true. + * + * * It is symmetric: for any non-null reference values x and y, `ccnxNameLabel_Equals(x, y)` must return true if and only if + * `ccnxNameLabel_Equals(y x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `ccnxNameLabel_Equals(x, y)` returns true and + * `ccnxNameLabel_Equals(y, z)` returns true, + * then `ccnxNameLabel_Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple invocations of `ccnxNameLabel_Equals(x, y)` + * consistently return true or consistently return false. + * + * * For any non-null reference value x, `ccnxNameLabel_Equals(x, NULL)` must return false. + * + * @param [in] x A pointer to a `CCNxNameLabel` instance. + * @param [in] y A pointer to a `CCNxNameLabel` instance to be compared to @p x. + * + * @return `true` if the given `CCNxNameLabel` instances are equal. + * + * Example: + * @code + * { + * CCNxNameLabel *x = ccnxNameLabel_Create(CCNxNameLabelType_SERIAL, NULL); + * CCNxNameLabel *y = ccnxNameLabel_Create(CCNxNameLabelType_SERIAL, NULL); + * + * if (ccnxNameSegment_Equals(x, y)) { + * // true + * } else { + * // false + * } + * ccnxNameLabel_Release(&segmentA); + * ccnxNameLabel_Release(&segmentB); + * } + * @endcode + */ +bool ccnxNameLabel_Equals(const CCNxNameLabel *x, const CCNxNameLabel *y); + +/** + * Determine if an instance of `CCNxNameLabel` is valid. + * + * Valid means the internal state of the type is consistent with its required current or future behaviour. + * This may include the validation of internal instances of types. + * + * @param [in] label A pointer to a `CCNxNameLabel` instance. + * + * @return true The instance is valid. + * @return false The instance is not valid. + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + * + */ +bool ccnxNameLabel_IsValid(const CCNxNameLabel *label); + +#ifdef Libccnx_DISABLE_VALIDATION +# define ccnxNameLabel_OptionalAssertValid(_instance_) +#else +# define ccnxNameLabel_OptionalAssertValid(_instance_) ccnxNameLabel_AssertValid(_instance_) +#endif + +/** + * Assert that an instance of `CCNxNameLabel` is valid. + * + * If the instance is not valid, terminate via `trapIllegalValue()` + * + * Valid means the internal state of the type is consistent with its required current or future behaviour. + * This may include the validation of internal instances of types. + * + * @param [in] label A pointer to a `CCNxNameLabel` instance. + * + * Example: + * @code + * { + * PARCByteArray *array = parcByteArray_Allocate(64); + * + * parcBuffer_AssertValid(array); + * } + * @endcode + * + */ +void ccnxNameLabel_AssertValid(const CCNxNameLabel *label); +#endif // libccnx_ccnx_NameType_h diff --git a/libccnx-common/ccnx/common/ccnx_NameSegment.c b/libccnx-common/ccnx/common/ccnx_NameSegment.c new file mode 100755 index 00000000..874da5ce --- /dev/null +++ b/libccnx-common/ccnx/common/ccnx_NameSegment.c @@ -0,0 +1,308 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ +#include <config.h> +#include <stdio.h> +#include <string.h> +#include <strings.h> + +#include <LongBow/runtime.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_URI.h> +#include <parc/algol/parc_URISegment.h> +#include <parc/algol/parc_Varint.h> +#include <parc/algol/parc_DisplayIndented.h> +#include <parc/algol/parc_Hash.h> +#include <parc/algol/parc_Object.h> + +#include <ccnx/common/ccnx_NameSegment.h> + +struct ccnx_name_segment { + const CCNxNameLabel *label; + CCNxNameLabelType type; + PARCBuffer *value; +}; + +static bool +_ccnxNameSegment_destructor(CCNxNameSegment **segmentP) +{ + assertNotNull(segmentP, "Parameter must be a non-null pointer to a CCNxNameSegment pointer."); + + CCNxNameSegment *segment = *segmentP; + ccnxNameLabel_Release((CCNxNameLabel **) &(segment->label)); + parcBuffer_Release(&segment->value); + return true; +} + +parcObject_Override(CCNxNameSegment, PARCObject, + .destructor = (PARCObjectDestructor *) _ccnxNameSegment_destructor, + .copy = (PARCObjectCopy *) ccnxNameSegment_Copy, + .equals = (PARCObjectEquals *) ccnxNameSegment_Equals, + .compare = (PARCObjectCompare *) ccnxNameSegment_Compare, + .hashCode = (PARCObjectHashCode *) ccnxNameSegment_HashCode, + .toString = (PARCObjectToString *) ccnxNameSegment_ToString); + +parcObject_ImplementAcquire(ccnxNameSegment, CCNxNameSegment); + +parcObject_ImplementRelease(ccnxNameSegment, CCNxNameSegment); + +CCNxNameSegment * +ccnxNameSegment_CreateLabelValue(const CCNxNameLabel *label, const PARCBuffer *value) +{ + CCNxNameSegment *result = parcObject_CreateInstance(CCNxNameSegment); + if (result != NULL) { + result->label = ccnxNameLabel_Acquire(label); + result->type = ccnxNameLabel_GetType(label); + result->value = parcBuffer_Acquire(value); + } + return result; +} + +CCNxNameSegment * +ccnxNameSegment_CreateTypeValue(CCNxNameLabelType type, const PARCBuffer *value) +{ + CCNxNameSegment *result = NULL; + CCNxNameLabel *label = ccnxNameLabel_Create(type, NULL); + + if (label != NULL) { + result = ccnxNameSegment_CreateLabelValue(label, value); + ccnxNameLabel_Release(&label); + } + return result; +} + +CCNxNameSegment * +ccnxNameSegment_CreateTypeValueArray(CCNxNameLabelType type, size_t length, const char array[length]) +{ + PARCBuffer *value = parcBuffer_PutArray(parcBuffer_Allocate(length), length, (const uint8_t *) array); + parcBuffer_Flip(value); + + CCNxNameSegment *result = ccnxNameSegment_CreateTypeValue(type, value); + parcBuffer_Release(&value); + + return result; +} + +CCNxNameSegment * +ccnxNameSegment_ParseURISegment(const PARCURISegment *uriSegment) +{ + CCNxNameSegment *result = NULL; + + PARCBuffer *buffer = parcURISegment_GetBuffer(uriSegment); + + size_t originalPosition = parcBuffer_Position(buffer); + CCNxNameLabel *label = ccnxNameLabel_Parse(buffer); + + if (ccnxNameLabel_IsValid(label)) { + PARCBuffer *value = parcBuffer_Slice(buffer); + + CCNxNameLabelType nameType = ccnxNameLabel_GetType(label); + if (nameType != CCNxNameLabelType_Unknown) { + result = ccnxNameSegment_CreateLabelValue(label, value); + } + + ccnxNameLabel_Release(&label); + parcBuffer_Release(&value); + + parcBuffer_SetPosition(buffer, originalPosition); + } + + return result; +} + +CCNxNameSegment * +ccnxNameSegment_Copy(const CCNxNameSegment *segment) +{ + PARCBuffer *value = parcBuffer_Copy(segment->value); + + CCNxNameLabel *label = ccnxNameLabel_Copy(segment->label); + + CCNxNameSegment *result = ccnxNameSegment_CreateLabelValue(label, value); + ccnxNameLabel_Release(&label); + + parcBuffer_Release(&value); + return result; +} + +bool +ccnxNameSegment_Equals(const CCNxNameSegment *segmentA, const CCNxNameSegment *segmentB) +{ + bool result = false; + + if (segmentA == segmentB) { + result = true; + } else if (segmentA == NULL || segmentB == NULL) { + result = false; + } else { + if (ccnxNameLabel_Equals(segmentA->label, segmentB->label)) { + if (parcBuffer_Equals(ccnxNameSegment_GetValue(segmentA), ccnxNameSegment_GetValue(segmentB))) { + result = true; + } + } + } + + return result; +} + +int +ccnxNameSegment_Compare(const CCNxNameSegment *segmentA, const CCNxNameSegment *segmentB) +{ + if (segmentA == NULL) { + if (segmentB == NULL) { + return 0; + } + return -1; + } else { + if (segmentB == NULL) { + return +1; + } + } + + if (ccnxNameSegment_Length(segmentA) < ccnxNameSegment_Length(segmentB)) { + return -1; + } + if (ccnxNameSegment_Length(segmentA) > ccnxNameSegment_Length(segmentB)) { + return +1; + } + + int result = parcBuffer_Compare(ccnxNameSegment_GetValue(segmentA), ccnxNameSegment_GetValue(segmentB)); + return result; +} + +CCNxNameLabelType +ccnxNameSegment_GetType(const CCNxNameSegment *segment) +{ + return ccnxNameLabel_GetType(segment->label); +} + +size_t +ccnxNameSegment_Length(const CCNxNameSegment *segment) +{ + return parcBuffer_Remaining(segment->value); +} + +PARCBuffer * +ccnxNameSegment_GetValue(const CCNxNameSegment *segment) +{ + return segment->value; +} + +static inline bool +_ccnxNameSegment_IsEscapable(const char c) +{ + return (c == 0 || strchr("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~", c) == NULL); +} + +static inline bool +_ccnxNameSegmentValue_IsEscaped(const PARCBuffer *value) +{ + bool result = false; + + size_t length = parcBuffer_Remaining(value); + + for (size_t i = 0; i < length; i++) { + if (_ccnxNameSegment_IsEscapable(parcBuffer_GetAtIndex(value, i))) { + result = true; + break; + } + } + + return result; +} + +PARCBufferComposer * +ccnxNameSegment_BuildString(const CCNxNameSegment *segment, PARCBufferComposer *composer) +{ + // Insert the label. However, in the case of an unescaped Name value, the Name lable portion can be left off. + + if (ccnxNameLabel_GetType(segment->label) != CCNxNameLabelType_NAME || _ccnxNameSegmentValue_IsEscaped(segment->value)) { + ccnxNameLabel_BuildString(segment->label, composer); + } + + if (ccnxNameSegment_Length(segment) > 0) { + PARCURISegment *uriSegment = parcURISegment_CreateFromBuffer(ccnxNameSegment_GetValue(segment)); + parcURISegment_BuildString(uriSegment, composer); + + parcURISegment_Release(&uriSegment); + } + + return composer; +} + +PARCHashCode +ccnxNameSegment_HashCode(const CCNxNameSegment *segment) +{ + PARCHash32Bits *hash = parcHash32Bits_Create(); + + parcHash32Bits_Update(hash, &segment->type, sizeof(segment->type)); + if (parcBuffer_Remaining(segment->value) > 0) { + parcHash32Bits_UpdateUint32(hash, parcBuffer_HashCode(segment->value)); + } + + uint32_t result = parcHash32Bits_Hash(hash); + + parcHash32Bits_Release(&hash); + + return result; +} + +char * +ccnxNameSegment_ToString(const CCNxNameSegment *segment) +{ + char *result = NULL; + + PARCBufferComposer *composer = parcBufferComposer_Create(); + if (composer != NULL) { + ccnxNameSegment_BuildString(segment, composer); + PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer(composer); + result = parcBuffer_ToString(tempBuffer); + parcBuffer_Release(&tempBuffer); + parcBufferComposer_Release(&composer); + } + + return result; +} + +void +ccnxNameSegment_Display(const CCNxNameSegment *segment, int indentation) +{ + parcDisplayIndented_PrintLine(indentation, "CCNxNameSegment@%p {", segment); + parcDisplayIndented_PrintLine(indentation + 1, "type=%d", segment->type); + parcBuffer_Display(segment->value, indentation + 1); + parcDisplayIndented_PrintLine(indentation, "}"); +} + +void +ccnxNameSegment_AssertValid(const CCNxNameSegment *segment) +{ + assertTrue(ccnxNameSegment_IsValid(segment), "CCNxNameSegment is invalid."); +} + +bool +ccnxNameSegment_IsValid(const CCNxNameSegment *segment) +{ + bool result = false; + + if (segment != NULL) { + if (parcBuffer_IsValid(segment->value) == true) { + result = true; + } + } + + return result; +} diff --git a/libccnx-common/ccnx/common/ccnx_NameSegment.h b/libccnx-common/ccnx/common/ccnx_NameSegment.h new file mode 100644 index 00000000..44c1ee57 --- /dev/null +++ b/libccnx-common/ccnx/common/ccnx_NameSegment.h @@ -0,0 +1,566 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file ccnx_NameSegment.h + * @ingroup Naming + * @brief A path segment of a CCNx Name + * + * An RFC3986 compliant implementation of URI segments, where each path segment carries a label. + * See {@link CCNxName} for more information. + * + */ +#ifndef libccnx_ccnx_NameSegment_h +#define libccnx_ccnx_NameSegment_h +#include <stdbool.h> +#include <stdint.h> + +#include <parc/algol/parc_BufferComposer.h> +#include <parc/algol/parc_Buffer.h> + +#include <ccnx/common/ccnx_NameLabel.h> + +#include <parc/algol/parc_URI.h> + +struct ccnx_name_Segment; +/** + * @typedef CCNxNameSegment + * @brief A path segment of a `CCNxName` + */ +typedef struct ccnx_name_segment CCNxNameSegment; + +/** + * Create a CCNxNameSegment instance initialised with the given type and value. + * + * @param [in] type A valid CCNxNameLabelType + * @param [in] value A valid PARCBuffer containing the value of the name segment. + * + * @return non-NULL A pointer to a valid CCNxNameSegment instance. + * @return NULL An error occurred. + * + * Example: + * @code + * { + * PARCBuffer *value = parcBuffer_WrapCString("value"); + * + * CCNxNameSegment *expected = ccnxNameSegment_CreateTypeValue(CCNxNameLabelType_NAME, value); + * + * parcBuffer_Release(&value); + * } + * @endcode + */ +CCNxNameSegment *ccnxNameSegment_CreateTypeValue(CCNxNameLabelType type, const PARCBuffer *value); + +/** + * Create a CCNxNameSegment instance initialised with the given type and value taken from the given array of bytes. + * + * @param [in] type A valid CCNxNameLabelType + * @param [in] length The number of bytes in @p array. + * @param [in] array A pointer to a buffer containing the bytes for the value of the name segment. + * + * @return non-NULL A pointer to a valid CCNxNameSegment instance. + * @return NULL An error occurred. + * + * Example: + * @code + * { + * CCNxNameSegment *expected = ccnxNameSegment_CreateTypeValueArray(CCNxNameLabelType_NAME, 5, "value"); + * } + * @endcode + */ +CCNxNameSegment *ccnxNameSegment_CreateTypeValueArray(CCNxNameLabelType type, size_t length, const char *array); + +/** + * Parse a `CCNxNameSegment` from a {@link PARCURISegment} consisting of type specification and value. + * + * The name must be in conformance with `draft-mosko-icnrg-ccnxlabeledcontent-00` + * + * Names that use mnemonic values for labels, must conform to thier respective specifications. + * See `draft-scott-icnrg-ccnxnameregistry-00` for a list of assigned names and type values. + * + * @param [in] uriSegment A pointer to a valid `PARCURISegment` + * @return non-NULL A pointer to an allocated `CCNxNameSegment` which must eventually be released by calling + * {@link ccnxNameSegment_Release}(). + * @return NULL An error occurred. + * @see `ccnxNameSegment_Release()` + * + * Example: + * @code + * { + * char *lciSegment = CCNxNameLabel_Name "=" "abcde"; + * PARC_URISegment *uriSegment = parcURISegment_Parse(lciSegment, NULL); + * + * CCNxNameSegment *actual = ccnxNameSegment_ParseURISegment(uriSegment); + * + * CCNxNameLabelType type = ccnxNameSegment_GetType(actual); + * assertTrue(CCNxNameLabelType_NAME == type, "Expected %04x, actual %04x", 0x20, type); + * + * ccnxNameSegment_Release(&actual); + * parcURISegment_Release(&uriSegment); + * } + * @endcode + */ +CCNxNameSegment *ccnxNameSegment_ParseURISegment(const PARCURISegment *uriSegment); + +/** + * Create a new `CCNxNameSegment` by copying the given `CCNxNameSegment`. This is a deep copy, + * and the created instance must eventually be released by calling {@link ccnxNameSegment_Release}(). + * + * @param [in] segment A `CCNxNameSegment` pointer. + * @return An allocated `CCNxNameSegment` which must eventually be released by calling {@link ccnxNameSegment_Release}(). + * + * Example: + * @code + * { + * PARCBuffer *value = parcBuffer_WrapCString("hello"); + * CCNxNameSegment *segment = ccnxNameSegment_CreateTypeValue(CCNxNameLabelType_NAME, value); + * + * CCNxNameSegment *copy = ccnxNameSegment_Copy(segment); + * + * ccnxNameSegment_Release(&segment); + * ccnxNameSegment_Copy(©); + * parcBuffer_Release(&value); + * } + * @endcode + */ +CCNxNameSegment *ccnxNameSegment_Copy(const CCNxNameSegment *segment); + +/** + * Determine if two `CCNxNameSegment` instances are equal. + * + * The following equivalence relations on non-null `CCNxNameSegment` instances are maintained: + * + * * It is reflexive: for any non-null reference value x, `ccnxNameSegment_Equal(x, x)` must return true. + * + * * It is symmetric: for any non-null reference values x and y, `ccnxNameSegment_Equal(x, y)` must return true if and only if + * `ccnxNameSegment_Equal(y x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `ccnxNameSegment_Equal(x, y)` returns true and + * `ccnxNameSegment_Equal(y, z)` returns true, + * then `ccnxNameSegment_Equal(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple invocations of `ccnxNameSegment_Equal(x, y)` + * consistently return true or consistently return false. + * + * * For any non-null reference value x, `ccnxNameSegment_Equal(x, NULL)` must return false. + * + * @param [in] segmentA A pointer to a `CCNxNameSegment` instance. + * @param [in] segmentB A pointer to a `CCNxNameSegment` instance to be compared to `segmentA`. + * + * @return `true` if the given `CCNxNameSegment` instances are equal. + * + * Example: + * @code + * { + * char *lciSegment = CCNxNameLabelType_Label_Chunk "=" "123"; + * PARC_URISegment *uriSegment = parcURISegment_Parse(lciSegment, NULL); + * CCNxNameSegment *segmentA = ccnxNameSegment_ParseURISegment(uriSegment); + * + * PARCBuffer *buf = parcBuffer_WrapCString("123"); + * CCNxNameSegment *segmentB = ccnxNameSegment_CreateTypeValue(CCNxNameLabelType_CHUNK, buf); + * + * if (ccnxNameSegment_Equals(expected, actual)) { + * // true + * } else { + * // false + * } + * ccnxNameSegment_Release(&segmentA); + * ccnxNameSegment_Release(&segmentB); + * parcURISegment_Release(&uriSegment); + * parcBuffer_Release(&buf); + * } + * @endcode + */ +bool ccnxNameSegment_Equals(const CCNxNameSegment *segmentA, const CCNxNameSegment *segmentB); + +/** + * A signum function comparing two `CCNxNameSegment` instances. + * + * Used to determine the ordering relationship of two `CCNxNameSegment` instances. + * + * @param [in] segmentA A pointer to a `CCNxNameSegment` instance. + * @param [in] segmentB A pointer to a `CCNxNameSegment` instance to be compared to `segmentA`. + * + * @return 0 if `segmentA` and `segmentB` are equivalent + * @return < 0 if `segmentA` < `segmentB` + * @return > 0 if `segmentA` > `segmentB` + * + * Example: + * @code + * { + * PARCBuffer *valueA = parcBuffer_WrapCString("apple"); + * CCNxNameSegment *segmentA = ccnxNameSegment_CreateTypeValue(CCNxNameLabelType_NAME, valueA); + * + * PARCBuffer *valueB = parcBuffer_WrapCString("banana"); + * CCNxNameSegment *segmentB = ccnxNameSegment_CreateTypeValue(CCNxNameLabelType_NAME, valueB); + * + * int signum = ccnxNameSegment_Compare(segmentA, segmentB); + * + * ccnxNameSegment_Release(&segmentA); + * parcBuffer_Release(&bufA); + * ccnxNameSegment_Release(&segmentB); + * parcBuffer_Release(&bufB); + * } + * @endcode + */ +int ccnxNameSegment_Compare(const CCNxNameSegment *segmentA, const CCNxNameSegment *segmentB); + +/** + * Get a pointer to the underlying PARCBuffer storing the value of the given `CCNxNameSegment`. + * + * A new reference to the instance is not created. + * If the caller requires a reference to live beyond the lifetime of the `CCNxNameSegment` + * it must acquire a reference via {@link parcBuffer_Acquire}. + * + * Any modifications to the buffer's position, limit or mark will affect subsequent use of the buffer. + * + * @param [in] segment A pointer to a `CCNxNameSegment` instance. + * @return A pointer to the underlying @link PARCBuffer. + * + * Example: + * @code + * { + * PARC_URISegment *uriSegment = parcURISegment_Parse("label:param=value"); + * + * CCNxNameSegment *actual = ccnxNameSegment_ParseURISegment(uriSegment); + * + * PARCBuffer *value = ccnxNameSegment_GetValue(actual); + * + * ccnxNameSegment_Release(&actual); + * parcURISegment_Release(&uriSegment); + * } + * @endcode + * + * @see {@link ccnxNameSegment_Length}() + */ +PARCBuffer *ccnxNameSegment_GetValue(const CCNxNameSegment *segment); + +/** + * Get a pointer to the underlying PARCBuffer storing the parameter of the given `CCNxNameSegment`. + * + * A new reference to the instance is not created. + * If the caller requires a reference to live beyond the lifetime of the `CCNxNameSegment` + * it must acquire a reference via {@link parcBuffer_Acquire}. + * + * Any modifications to the buffer's position, limit or mark will affect subsequent use of the buffer. + * + * @param [in] segment A pointer to a `CCNxNameSegment` instance. + * @return NULL The given `CCNxNameSegment` does not specify a parameter value. + * @return non-NULL A pointer to the underlying `PARCBuffer`. + * + * Example: + * @code + * { + * CCNxNameSegment *segment = ccnxNameSegment_ParseURISegment("label:param=value"); + * + * PARCBuffer *parameter = ccnxNameSegment_GetParameter(segment); + * + * ccnxNameSegment_Release(&segment); + * } + * @endcode + * + * @see {@link ccnxNameSegment_Length}() + */ +PARCBuffer *ccnxNameSegment_GetParameter(const CCNxNameSegment *segment); + +/** + * Produce a nul-terminated C string representation of the given `CCNxNameSegment`. + * + * A string representation, such as "1=foo". + * + * @param [in] segment A CCNxNameSegment pointer. + * @return An allocated null-terminated byte array that must be deallocated by `{@link parcMemory_Deallocate}()`. + * + * Example: + * @code + * { + * CCNxName *name = ccnxName_CreateFromCString("lci:/parc/csl/sensors/radiation/212"); + * CCNxNameSegment *segment = ccnxName_GetSegment(name, 3); + * + * char *string = ccnxNameSegment_ToString(segment); + * printf("Hello: %s\n", string); + * + * parcMemory_Deallocate(string); + * ccnxName_Release(&name); + * } + * @endcode + * + * @see `parcMemory_Deallocate` + */ +char *ccnxNameSegment_ToString(const CCNxNameSegment *segment); + +/** + * Print a human readable representation of the given `CCNxNameSegment`. + * + * @param [in] segment A pointer to the instance to display. + * @param [in] indentation The level of indentation to use to pretty-print the output. + * + * Example: + * @code + * { + * CCNxName *name = ccnxName_CreateFromCString("lci:/parc/csl/sensors/radiation/212"); + * CCNxNameSegment *segment = ccnxName_GetSegment(name, 3); + * + * ccnxNameSegment_Display(segment, 0); + * + * ccnxName_Release(&name); + * } + * @endcode + * + */ +void ccnxNameSegment_Display(const CCNxNameSegment *segment, int indentation); + +/** + * Append a printable-character representation of the specified instance to the given {@link PARCBufferComposer}. + * + * @param [in] segment A pointer to the `CCNxNameSegment` instance. + * @param [in,out] composer A pointer to the `PARCBufferComposer` instance onto which to append our printable representation. + * + * @return NULL Cannot allocate memory. + * @return non-NULL The @p composer. + * + * Example: + * @code + * { + * PARCBufferComposer *result = parcBufferComposer_Create(); + * + * ccnxNameSegment_BuildString(instance, result); + * + * char *string = parcBuffer_ToString(parcBufferComposer_ProduceBuffer(result)); + * printf("Hello: %s\n", string); + * parcMemory_Deallocate(string); + * + * parcBufferComposer_Release(&result); + * } + * @endcode + * + * @see PARCBufferComposer + */ +PARCBufferComposer *ccnxNameSegment_BuildString(const CCNxNameSegment *segment, PARCBufferComposer *composer); + +/** + * Return the length of the specified `CCNxNameSegment`, in bytes. + * + * @param [in] segment A pointer to a `CCNxNameSegment` instance. + * @return The number of bytes for the value of the given `CCNxNameSegment`. + * + * Example: + * @code + * { + * CCNxName *name = ccnxName_CreateFromCString("lci:/parc/csl/sensors/humidity/12"); + * CCNxNameSegment *segment = ccnxName_GetSegment(name, 3); + * + * int length = ccnxNameSegment_Length(segment); + * + * ccnxName_Release(&name); + * } + * @endcode + */ +size_t ccnxNameSegment_Length(const CCNxNameSegment *segment); + +/** + * Get the label of the given `CCNxNameSegment`. + * + * @param [in] segment A pointer to a `CCNxNameSegment` instance. + * @return non-NULL A pointer to a PARCBuffer instance containing the label. + * @return NULL The CCNxNameSegment instance does not have a label. + * + * Example: + * @code + * { + * PARCBuffer *buf = parcBuffer_WrapCString("apple"); + * CCNxNameSegment *segment = ccnxNameSegment_CreateTypeValue(CCNxNameLabelType_NAME, buf); + * + * CCNxNameLabelType type = ccnxNameSegment_GetType(segment); + * + * ccnxNameSegment_Release(&segment); + * parcBuffer_Release(&buf); + * } + * @endcode + * + * @see `CCNxNameLabelType` + */ +PARCBuffer *ccnxNameSegment_GetLabel(const CCNxNameSegment *segment); + +/** + * Get the {@link CCNxNameLabelType} of the given `CCNxNameSegment`. + * + * @param [in] segment A pointer to a `CCNxNameSegment` instance. + * @return The `CCNxNameLabelType` of the specified `CCNxNameSegment` instance. + * + * Example: + * @code + * { + * PARCBuffer *buf = parcBuffer_WrapCString("apple"); + * CCNxNameSegment *segment = ccnxNameSegment_CreateTypeValue(CCNxNameLabelType_NAME, buf); + * + * CCNxNameLabelType type = ccnxNameSegment_GetType(segment); + * + * ccnxNameSegment_Release(&segment); + * parcBuffer_Release(&buf); + * } + * @endcode + * + * @see `CCNxNameLabelType` + */ +CCNxNameLabelType ccnxNameSegment_GetType(const CCNxNameSegment *segment); + +/** + * Return a hashcode for the given `CCNxNameSegment`. + * + * Whenever `HashCode()` is invoked on the same instance more than once within the same execution environment, + * the HashCode function must consistently return the same value, provided no information used in its corresponding + * `Equals()` comparisons on the same instance is modified. + * + * This value need not remain consistent from one execution of an application to another execution of the same application. + * + * If two instances are equal according to the `Equals()` function, + * then calling the `HashCode` function on each of the two objects must produce the same integer result. + * + * It is not required that if two instances are unequal according to the `Equals()` function, + * then calling the HashCode() method on each of the two instances must produce distinct integer results. + * However, the programmer should be aware that producing distinct results for unequal instances + * may improve the performance of some data structures. + * + * @param segment A pointer to a `CCNxNameSegment` instance. + * @return An unsigned 32-bit integer hash code value. + * + * Example: + * @code + * { + * PARCBuffer *bufA =parcBuffer_WrapCString("Test"); + * + * CCNxNameSegment *segmentA = ccnxNameSegment_CreateTypeValue(CCNxNameLabelType_NAME, bufA); + * + * uint32_t hashCode = ccnxNameSegment_HashCode(segmentA); + * + * ccnxNameSegment_Release(&segmentA); + * parcBuffer_Release(&bufA); + * } + * @endcode + * + * @see `parcHash32Bits_Hash` + */ +PARCHashCode ccnxNameSegment_HashCode(const CCNxNameSegment *segment); + +/** + * Increase the number of references to a `CCNxNameSegment`. + * + * Note that a new `CCNxNameSegment` is not created, + * only that the given `CCNxNameSegment` reference count is incremented. + * Discard the reference by invoking {@link ccnxNameSegment_Release}(). + * + * @param [in] segment A pointer to the original instance. + * @return The value of the input parameter @p instance. + * + * Example: + * @code + * { + * ... + * + * CCNxNameSegment *segment = ccnxNameSegment_Acquire(instance); + * + * ccnxNameSegment_Release(&segment); + * + * } + * @endcode + * + * @see ccnxNameSegment_Release + */ +CCNxNameSegment *ccnxNameSegment_Acquire(const CCNxNameSegment *segment); + +/** + * Release a previously acquired reference to the specified instance, + * decrementing the reference count for the instance. + * + * The pointer to the instance is set to NULL as a side-effect of this function. + * + * If the invocation causes the last reference to the instance to be released, + * the instance is deallocated and the instance's implementation will perform + * additional cleanup and release other privately held references. + * + * @param [in,out] segmentP A pointer to a pointer to the instance to release. + * + * + * Example: + * @code + * { + * ... + * + * CCNxNameSegment *segment = ccnxNameSegment_Acquire(instance); + * + * ccnxNameSegment_Release(&segment); + * + * } + * @endcode + * + * @see ccnxNameSegment_Acquire + */ +void ccnxNameSegment_Release(CCNxNameSegment **segmentP); + +#ifdef Libccnx_DISABLE_VALIDATION +# define ccnxNameSegment_OptionalAssertValid(_instance_) +#else +# define ccnxNameSegment_OptionalAssertValid(_instance_) ccnxNameSegment_AssertValid(_instance_) +#endif + +/** + * Assert that an instance of `CCNxNameSegment` is valid. + * + * If the instance is not valid, terminate via {@link trapIllegalValue} + * + * Valid means the internal state of the type is consistent with its + * required current or future behaviour. + * This may include the validation of internal instances of types. + * + * @param [in] segment A pointer to the instance to check. + * + * Example: + * @code + * { + * ... + * + * ccnxNameSegment_AssertValid(segment); + * + * } + * @endcode + */ +void ccnxNameSegment_AssertValid(const CCNxNameSegment *segment); + +/** + * Determine if an instance of `CCNxNameSegment` is valid. + * + * Valid means the internal state of the type is consistent with its + * required current or future behaviour. + * This may include the validation of internal instances of referenced objects. + * + * @param [in] segment A pointer to the instance to check. + * + * @return true The instance is valid. + * @return false The instance is not valid. + * + * Example: + * @code + * { + * ... + * + * ccnxNameSegment_IsValid(segment); + * + * } + * @endcode + */ +bool ccnxNameSegment_IsValid(const CCNxNameSegment *segment); +#endif // libccnx_ccnx_NameSegment_h diff --git a/libccnx-common/ccnx/common/ccnx_NameSegmentNumber.c b/libccnx-common/ccnx/common/ccnx_NameSegmentNumber.c new file mode 100755 index 00000000..b7187158 --- /dev/null +++ b/libccnx-common/ccnx/common/ccnx_NameSegmentNumber.c @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ +#include <config.h> +#include <LongBow/runtime.h> + +#include <ccnx/common/ccnx_NameSegmentNumber.h> +#include <ccnx/common/ccnx_NameSegment.h> + +#include <parc/algol/parc_Buffer.h> +#include <parc/algol/parc_BufferComposer.h> + +bool +ccnxNameSegmentNumber_IsValid(const CCNxNameSegment *nameSegment) +{ + bool result = false; + + size_t remaining = parcBuffer_Remaining(ccnxNameSegment_GetValue(nameSegment)); + if (remaining > 0 && remaining < 8) { + result = true; + } + + return result; +} + +void +ccnxNameSegmentNumber_AssertValid(const CCNxNameSegment *nameSegment) +{ + assertTrue(ccnxNameSegmentNumber_IsValid(nameSegment), "Encountered an invalid CCNxNameSegment"); +} + +CCNxNameSegment * +ccnxNameSegmentNumber_Create(CCNxNameLabelType type, uint64_t value) +{ + PARCBufferComposer *composer = parcBufferComposer_Create(); + + bool mustContinue = false; + for (int byte = 7; byte >= 0; byte--) { + uint8_t b = (value >> (byte * 8)) & 0xFF; + if (b != 0 || byte == 0 || mustContinue) { + parcBufferComposer_PutUint8(composer, b); + mustContinue = true; + } + } + PARCBuffer *buffer = parcBuffer_Flip(parcBufferComposer_GetBuffer(composer)); + CCNxNameSegment *segment = ccnxNameSegment_CreateTypeValue(type, buffer); + parcBufferComposer_Release(&composer); + + return segment; +} + +uint64_t +ccnxNameSegmentNumber_Value(const CCNxNameSegment *nameSegment) +{ + const PARCBuffer *buffer = ccnxNameSegment_GetValue(nameSegment); + + uint64_t result = 0; + + for (size_t i = 0; i < parcBuffer_Remaining(buffer); i++) { + result = (result << 8) | parcBuffer_GetAtIndex(buffer, i); + } + return result; +} diff --git a/libccnx-common/ccnx/common/ccnx_NameSegmentNumber.h b/libccnx-common/ccnx/common/ccnx_NameSegmentNumber.h new file mode 100755 index 00000000..a685c713 --- /dev/null +++ b/libccnx-common/ccnx/common/ccnx_NameSegmentNumber.h @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file ccnx_NameSegmentNumber.h + * @ingroup Naming + * @brief Utilities for creating and parsing CCNxNameSegments that contain an integer value. + * + * CCNxNameSegments may optionally contain an integer value. This file contains some utilities + * for creating and parsing CCNxNameSegments with integer values. + * See {@link CCNxNameSegment}, {@link CCNxNameLabelType}, and {@link CCNxName} for more information. + * + */ +#ifndef libccnx_ccnx_NameSegmentNumber_h +#define libccnx_ccnx_NameSegmentNumber_h + +#include <stdbool.h> + +#include <ccnx/common/ccnx_NameSegment.h> +#include <ccnx/common/ccnx_NameLabel.h> + +/** + * Create a new {@link CCNxNameSegment} consisting of a type and integer value. + * + * The newly created instance must eventually be released by calling {@link ccnxNameSegment_Release}(). + * + * @param [in] type A valid {@link CCNxNameLabelType}. + * @param [in] value The integer value to assign to the `CCNxNameSegment`. + * @return A pointer to a new `CCNxNameSegment` instance, consisting of a type and integer value. + * + * Example: + * @code + * { + * uint64_t expected = 0x123456789ABCDEF0; + * CCNxNameSegment *segment = ccnxNameSegmentNumber_Create(CCNxNameLabelType_CHUNK, expected); + * + * uint64_t actual = ccnxNameSegmentNumber_Value(segment); + * + * assertTrue(expected == actual, "Expected 0x%" PRIX64 " actual 0x%" PRIX64 "", expected, actual); + * + * ccnxNameSegment_Release(&segment); + * } + * @endcode + * + * @see {@link ccnxNameSegment_Release} + * @see `CCNxNameLabelType` + */ +CCNxNameSegment *ccnxNameSegmentNumber_Create(CCNxNameLabelType type, uint64_t value); + +/** + * Decode an integer value from a {@link CCNxNameSegment}. + * + * Given a `CCNxNameSegment` with a numeric type, return the integer value associated with the `CCNxNameSegment`. + * + * @param [in] nameSegment A pointer a `CCNxNameSegment` instance containing an integer value. + * @return The integer value of the `CCNxNameSegment`. + * + * Example: + * @code + * { + * uint64_t expected = 0x123456789ABCDEF0; + * CCNxNameSegment *segment = ccnxNameSegmentNumber_Create(CCNxNameLabelType_CHUNK, expected); + * + * uint64_t actual = ccnxNameSegmentNumber_Value(segment); + * + * assertTrue(expected == actual, "Expected 0x%" PRIX64 " actual 0x%" PRIX64 "", expected, actual); + * + * ccnxNameSegment_Release(&segment); + * } + * @endcode + */ +uint64_t ccnxNameSegmentNumber_Value(const CCNxNameSegment *nameSegment); + +/** + * Determine if the given CCNxNameSegment value represents a valid encoded number. + * + * A valid encoded number contains at least 1 byte and nor more than 8 bytes. + * + * @param [in] nameSegment A pointer to a CCNxNameSegment instance. + * + * Example: + * @code + * { + * uint64_t expected = 0x123456789ABCDEF0; + * CCNxNameSegment *segment = ccnxNameSegmentNumber_Create(CCNxNameLabelType_CHUNK, expected); + * + * ccnxNameSegmentNumber_AssertValid(segment); + * } + * @endcode + */ +bool ccnxNameSegmentNumber_IsValid(const CCNxNameSegment *nameSegment); + +/** + * Assert that the given CCNxNameSegment value represents a valid encoded number. + * + * @param [in] nameSegment A pointer to a CCNxNameSegment instance. + * + * Example: + * @code + * { + * uint64_t expected = 0x123456789ABCDEF0; + * CCNxNameSegment *segment = ccnxNameSegmentNumber_Create(CCNxNameLabelType_CHUNK, expected); + * + * ccnxNameSegmentNumber_AssertValid(segment); + * } + * @endcode + */ +void ccnxNameSegmentNumber_AssertValid(const CCNxNameSegment *nameSegment); +#endif // libccnx_ccnx_NameSegmentNumber_h diff --git a/libccnx-common/ccnx/common/ccnx_PayloadType.h b/libccnx-common/ccnx/common/ccnx_PayloadType.h new file mode 100755 index 00000000..de75592e --- /dev/null +++ b/libccnx-common/ccnx/common/ccnx_PayloadType.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file ccnx_PayloadType.h + * @brief An enumeration for the supported types of a content object payload. + * + * @see CCNxContentObject + * + */ + +#ifndef libccnx_ccnx_PayloadType_h +#define libccnx_ccnx_PayloadType_h + +/** + * @typedef CCNxPayloadType + * @brief Specifies how the Payload should be interpreted. + */ +typedef enum ccnx_payload_type { + CCNxPayloadType_DATA = 0, + CCNxPayloadType_KEY = 1, + CCNxPayloadType_LINK = 2, + CCNxPayloadType_MANIFEST = 3 +} CCNxPayloadType; +#endif /* defined(libccnx_ccnx_PayloadType_h) */ diff --git a/libccnx-common/ccnx/common/ccnx_TimeStamp.c b/libccnx-common/ccnx/common/ccnx_TimeStamp.c new file mode 100755 index 00000000..75add669 --- /dev/null +++ b/libccnx-common/ccnx/common/ccnx_TimeStamp.c @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ +#include <config.h> +#include <sys/types.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include <LongBow/runtime.h> +#include <parc/algol/parc_Object.h> + +#include <ccnx/common/ccnx_TimeStamp.h> + +#include <parc/algol/parc_Memory.h> + +struct ccnx_timestamp { + struct timespec timespec; +}; + +static void +_Destroy(CCNxTimeStamp **timeStampPtr) +{ +} + +parcObject_ExtendPARCObject(CCNxTimeStamp, _Destroy, ccnxTimeStamp_Copy, + ccnxTimeStamp_ToString, ccnxTimeStamp_Equals, NULL, NULL, NULL); + +parcObject_ImplementAcquire(ccnxTimeStamp, CCNxTimeStamp); + +parcObject_ImplementRelease(ccnxTimeStamp, CCNxTimeStamp); + +void +ccnxTimeStamp_AssertValid(const CCNxTimeStamp *timeStamp) +{ + assertNotNull(timeStamp, "Parameter must be a pointer to a CCNxTimeStamp structure."); +} + +CCNxTimeStamp * +ccnxTimeStamp_CreateFromCurrentUTCTime(void) +{ + struct timeval timeval; + gettimeofday(&timeval, NULL); + + struct timespec timespec; + timespec.tv_sec = timeval.tv_sec; + timespec.tv_nsec = timeval.tv_usec * 1000; + + return ccnxTimeStamp_CreateFromTimespec(×pec); +} + +CCNxTimeStamp * +ccnxTimeStamp_CreateFromTimespec(const struct timespec *timespec) +{ + CCNxTimeStamp *result = parcObject_CreateInstance(CCNxTimeStamp); + result->timespec.tv_sec = timespec->tv_sec; + result->timespec.tv_nsec = timespec->tv_nsec; + + return result; +} + +struct timespec +ccnxTimeStamp_AsTimespec(const CCNxTimeStamp *timeStamp) +{ + ccnxTimeStamp_AssertValid(timeStamp); + return timeStamp->timespec; +} + +CCNxTimeStamp * +ccnxTimeStamp_CreateFromNanosecondsSinceEpoch(uint64_t nanos) +{ + struct timespec timespec = { .tv_sec = nanos / 1000000000, .tv_nsec = (nanos % 1000000000) }; + + return ccnxTimeStamp_CreateFromTimespec(×pec); +} + +CCNxTimeStamp * +ccnxTimeStamp_CreateFromMillisecondsSinceEpoch(uint64_t millis) +{ + struct timespec timespec = { .tv_sec = millis / 1000, .tv_nsec = (millis % 1000) * 1000000 }; + return ccnxTimeStamp_CreateFromTimespec(×pec); +} + +bool +ccnxTimeStamp_Equals(const CCNxTimeStamp *timeStampA, const CCNxTimeStamp *timeStampB) +{ + if (timeStampA == timeStampB) { + return true; + } + if (timeStampA == NULL || timeStampB == NULL) { + return false; + } + if (timeStampA->timespec.tv_sec == timeStampB->timespec.tv_sec) { + if (timeStampA->timespec.tv_nsec == timeStampB->timespec.tv_nsec) { + return true; + } + } + return false; +} + +uint64_t +ccnxTimeStamp_AsNanoSeconds(const CCNxTimeStamp *timeStamp) +{ + ccnxTimeStamp_AssertValid(timeStamp); + uint64_t result = timeStamp->timespec.tv_sec * 1000000000ULL + timeStamp->timespec.tv_nsec; + return result; +} + +char * +ccnxTimeStamp_ToString(const CCNxTimeStamp *timeStamp) +{ + if (timeStamp == NULL) { + return parcMemory_StringDuplicate("NULL", 4); + } + + char *string; + int failure = asprintf(&string, "%ld.%ld", timeStamp->timespec.tv_sec, timeStamp->timespec.tv_nsec); + assertTrue(failure > -1, "Error asprintf"); + + char *result = parcMemory_StringDuplicate(string, strlen(string)); + free(string); + return result; +} + +CCNxTimeStamp * +ccnxTimeStamp_Copy(const CCNxTimeStamp *timeStamp) +{ + ccnxTimeStamp_AssertValid(timeStamp); + return ccnxTimeStamp_CreateFromTimespec(&timeStamp->timespec); +} diff --git a/libccnx-common/ccnx/common/ccnx_TimeStamp.h b/libccnx-common/ccnx/common/ccnx_TimeStamp.h new file mode 100755 index 00000000..e21f5a4e --- /dev/null +++ b/libccnx-common/ccnx/common/ccnx_TimeStamp.h @@ -0,0 +1,336 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file ccnx_TimeStamp.h + * @brief A CCNxTimeStamp represents a point in time. + * + */ +#ifndef libccnx_ccnx_TimeStamp_h +#define libccnx_ccnx_TimeStamp_h + +#include <sys/time.h> +#include <stdbool.h> +#include <stdint.h> + +struct ccnx_timestamp; +/** + * @typedef CCNxTimeStamp + * @brief A CCNxTimeStamp represents a point in time. + */ +typedef struct ccnx_timestamp CCNxTimeStamp; + +/** + * Assert that a pointer to a `CCNxTimeStamp` instance and the instance itself is valid. + * + * @param [in] timeStamp A pointer to a CCNxTimeStamp intance. + * + * Example: + * @code + * { + * CCNxTimeStamp *timestamp = ccnxTimeStamp_CreateFromCurrentUTCTime(); + * ccnxTimeStamp_AssertValid(timestamp); + * ccnxTimeStamp_Release(×tamp); + * } + * @endcode + * + */ +void ccnxTimeStamp_AssertValid(const CCNxTimeStamp *timeStamp); + +/** + * Create a new `CCNxTimeStamp` instance from the current local time. + * + * The newly created instance must eventually be released by calling {@link ccnxTimeStamp_Release}(); + * + * @return An instance of a `CCNxTimeStamp` initialized to the current local time. + * + * Example: + * @code + * { + * CCNxTimeStamp *timestamp = ccnxTimeStamp_CreateFromCurrentUTCTime(); + * ccnxTimeStamp_AssertValid(timestamp); + * ccnxTimeStamp_Release(×tamp); + * } + * @endcode + * + * @see ccnxTimeStamp_Release + */ +CCNxTimeStamp *ccnxTimeStamp_CreateFromCurrentUTCTime(void); + +/** + * Create a new `CCNxTimeStamp` instance from the given `struct timespec`. + * + * Given a `struct timespec` create a `CCNxTimeStamp` representing the same time. + * The newly created instance must eventually be released by calling {@link ccnxTimeStamp_Release}(); + * + * @param [in] timespec The `struct timespec` of the time to represent. + * @return An instance of a `CCNxTimeStamp` initialized from the specified `struct timespec`. + * + * Example: + * @code + * { + * struct timespec time = {.tv_sec = 1, .tv_nsec = 1 }; + * + * CCNxTimeStamp *timestamp = ccnxTimeStamp_CreateFromTimespec(&time); + * + * ccnxTimeStamp_Release(×tamp); + * } + * @endcode + * + * @see ccnxTimeStamp_Release + */ +CCNxTimeStamp *ccnxTimeStamp_CreateFromTimespec(const struct timespec *timespec); + +/** + * Return a `struct timespec` representation of the given `CCNxTimeStamp`. + * + * The internal form of the `CCNxTimeStamp` is returned as a `struct timespec`. + * + * @param [in] timeStamp A pointer to a `CCNxTimeStamp` instance. + * @return A `struct timespec` representation of the given `CCNxTimeStamp`. + * + * Example: + * @code + * { + * struct timespec time = {.tv_sec = 1, .tv_nsec = 1 }; + * + * CCNxTimeStamp *timeStamp = ccnxTimeStamp_CreateFromTimespec(&time); + * + * struct timespec actualTime = ccnxTimeStamp_AsTimespec(timeStamp); + * assertTrue(time.tv_sec == actualTime.tv_sec && time.tv_nsec == actualTime.tv_nsec, "Expected timespec to be equal."); + * + * ccnxTimeStamp_Release(&timeStamp); + * } + * @endcode + */ +struct timespec ccnxTimestamp_AsTimespec(const CCNxTimeStamp *timeStamp); + +/** + * Create a new `CCNxTimeStamp` instance initialized to the given number of milliseconds from the epoch. + * + * The epoch is defined as 00:00:00, 01/01/1970. + * The newly created instance must eventually be released by calling {@link ccnxTimeStamp_Release}(); + * + * @param [in] milliseconds A `uint64_t` specifying the number of milliseconds since the epoch. + * @return A pointer to a `CCNxTimeStamp` instance. + * + * Example: + * @code + * { + * CCNxTimeStamp *timestamp = ccnxTimeStamp_CreateFromMillisecondsSinceEpoch(100); + * ccnxTimeStamp_AssertValid(timestamp); + * ccnxTimeStamp_Release(×tamp); + * } + * @endcode + * + * @see ccnxTimeStamp_Release + */ +extern CCNxTimeStamp *ccnxTimeStamp_CreateFromMillisecondsSinceEpoch(uint64_t milliseconds); + +/** + * Create a new `CCNxTimeStamp` instance initialized to the given number of nanoseconds from the epoch. + * + * The epoch is defined as 00:00:00, 01/01/1970. + * The newly created instance must eventually be released by calling {@link ccnxTimeStamp_Release}(); + * + * @param [in] nanoseconds A uint64_t specifying the number of nanoseconds since the epoch. + * @return A pointer to a `CCNxTimeStamp` instance. + * + * Example: + * @code + * { + * CCNxTimeStamp *timestamp = ccnxTimeStamp_CreateFromMillisecondsSinceEpoch(100); + * ccnxTimeStamp_AssertValid(timestamp); + * ccnxTimeStamp_Release(×tamp); + * } + * @endcode + * + * @see ccnxTimeStamp_Release + */ +CCNxTimeStamp *ccnxTimeStamp_CreateFromNanosecondsSinceEpoch(uint64_t nanoseconds); + +/** + * Return the value of the given `CCNxTimeStamp` as an unsigned 64-bit integer representing the number of nanoseconds since the epoch. + * + * The resolution of a `CCNxTimeStamp` is nanoseconds, although the resolution of the host environment may not. + * + * @param [in] timeStamp A pointer to a `CCNxTimeStamp` instance. + * @return The value of the given `CCNxTimeStamp` as an unsigned 64-bit integer + * + * Example: + * @code + * { + * uint64_t expected = 1099511627776ULL; + * + * CCNxTimeStamp *timeStamp = ccnxTimeStamp_CreateFromNanosecondsSinceEpoch(expected); + * + * uint64_t actual = ccnxTimeStamp_AsNanoSeconds(timeStamp); + * + * assertTrue(expected == actual, "Expected " PRIu64 " actual " PRIu64, expected, actual); + * + * ccnxTimeStamp_Release(&timeStamp); + * } + * @endcode + */ +uint64_t ccnxTimeStamp_AsNanoSeconds(const CCNxTimeStamp *timeStamp); + +/** + * Create a deep copy of the given `CCNxTimeStamp`, using dynamically allocated memory. + * + * The newly created instance must eventually be released by calling {@link ccnxTimeStamp_Release}(); + * + * @param [in] timeStamp A pointer to a `CCNxTimeStamp` instance. + * @return A new copy of the given `CCNxTimeStamp`. + * + * Example: + * @code + * { + * CCNxTimeStamp *timeStamp = ccnxTimeStamp_CreateFromMillisecondsSinceEpoch(100); + * CCNxTimeStamp *copy = ccnxTimeStamp_Copy(timeStamp); + * + * ccnxTimeStamp_AssertValid(timeStamp); + * ccnxTimeStamp_AssertValid(copy); + * + * ccnxTimeStamp_Release(&timeStamp); + * ccnxTimeStamp_Release(©); + * } + * @endcode + * + * @see ccnxTimeStamp_Release + */ +extern CCNxTimeStamp *ccnxTimeStamp_Copy(const CCNxTimeStamp *timeStamp); + +/** + * Determine if two `CCNxTimeStamp` instances are equal. + * + * + * The following equivalence relations on non-null `CCNxTimeStamp` instances are maintained: * + * It is reflexive: for any non-null reference value `ccnxTimeStamp_Equals(x, x)` must return true. + * + * * It is symmetric: for any non-null reference values x and y, `ccnxTimeStamp_Equals(x, y)` must return true if and only if + * `ccnxTimeStamp_Equals(y, x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `ccnxTimeStamp_Equals(x, y)` returns true and + * `ccnxTimeStamp_Equals(y, z)` returns true, + * then `ccnxTimeStamp_Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple invocations of `ccnxTimeStamp_Equals(x, y)` + * consistently return true or consistently return false. + * + * * For any non-null reference value x, `ccnxTimeStamp_Equals(x, NULL)` must return false. + * + * + * @param [in] timeStampA A pointer to a `CCNxTimeStamp` instance. + * @param [in] timeStampB A pointer to a `CCNxTimeStamp` instance. + * @return `true` if the referenced CCNxTimeStamp instances are equal. + * + * Example: + * @code + * { + * CCNxTimeStamp *timeStampA = ccnxTimeStamp_CreateFromMillisecondsSinceEpoch(100); + * CCNxTimeStamp *timeStampB = ccnxTimeStamp_CreateFromMillisecondsSinceEpoch(100); + * + * if (ccnxTimeStamp_Equals(timeStampA, timeStampB)) { + * // true + * } else { + * // false + * } + * + * ccnxTimeStamp_Release(&timeStampA); + * ccnxTimeStamp_Release(&timeStampB); + * } + * @endcode + */ +bool ccnxTimeStamp_Equals(const CCNxTimeStamp *timeStampA, const CCNxTimeStamp *timeStampB); + +/** + * Increase the number of references to a `CCNxTimeStamp`. + * + * Note that new `CCNxTimeStamp` is not created, + * only that the given `CCNxTimeStamp` reference count is incremented. + * Discard the reference by invoking {@link ccnxTimeStamp_Release}. + * + * @param [in] instance A pointer to the original instance. + * @return The value of the input parameter @p instance. + * + * Example: + * @code + * { + * CCNxTimeStamp *timeStamp = ccnxTimeStamp_CreateFromCurrentUTCTime(); + * + * CCNxTimeStamp *reference = ccnxTimeStamp_Acquire(timeStamp); + * + * ccnxTimeStamp_Release(&timeStamp); + * ccnxTimeStamp_Release(&reference); + * } + * @endcode + * + * @see ccnxTimeStamp_Release + */ +CCNxTimeStamp *ccnxTimeStamp_Acquire(const CCNxTimeStamp *instance); + +/** + * Release a previously acquired reference to the specified instance, + * decrementing the reference count for the instance. + * + * The pointer to the instance is set to NULL as a side-effect of this function. + * + * If the invocation causes the last reference to the instance to be released, + * the instance is deallocated and the instance's implementation will perform + * additional cleanup and release other privately held references. + * + * @param [in,out] timeStampPtr A pointer to a pointer to the instance to release. + * + * Example: + * @code + * { + * CCNxTimeStamp *timeStamp = ccnxTimeStamp_CreateFromCurrentUTCTime(); + * + * ccnxTimeStamp_Release(&timeStamp); + * } + * @endcode + * + * @see {@link ccnxTimeStamp_Acquire} + */ +void ccnxTimeStamp_Release(CCNxTimeStamp **timeStampPtr); + +/** + * Produce a null-terminated C-string representation of the specified instance. + * + * The non-null result must be freed by the caller via {@link parcMemory_Deallocate}. + * + * @param [in] timeStamp A pointer to a 'CCNxTimeStamp' instance. + * @return NULL Memory could not be allocated. + * @return non-NULL A nul-terminated string that must be deallocated via {@link parcMemory_Deallocate}(). + * + * Example: + * @code + * { + * CCNxTimeStamp *timeStamp = ccnxTimeStamp_CreateFromCurrentUTCTime(); + * + * char *string = ccnxTimeStamp_ToString(timeStamp); + * + * printf("Hello: %s\n", string); + * + * parcMemory_Deallocate(string); + * ccnxTimeStamp_Release(&timeStamp); + * } + * @endcode + * + * @see `parcMemory_Deallocate` + */ +char *ccnxTimeStamp_ToString(const CCNxTimeStamp *timeStamp); +#endif // libccnx_ccnx_TimeStamp_h diff --git a/libccnx-common/ccnx/common/ccnx_WireFormatMessage.c b/libccnx-common/ccnx/common/ccnx_WireFormatMessage.c new file mode 100644 index 00000000..9fd82482 --- /dev/null +++ b/libccnx-common/ccnx/common/ccnx_WireFormatMessage.c @@ -0,0 +1,326 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +#include "config.h" + +#include <LongBow/runtime.h> + +#include <ccnx/common/ccnx_WireFormatMessage.h> + +void +ccnxWireFormatMessage_AssertValid(const CCNxWireFormatMessage *message) +{ + assertNotNull(message, "Must be a non-null pointer to a CCNxWireFormatMessage."); + + // Check for required fields in the underlying dictionary. Case 1036 + CCNxWireFormatMessageInterface *impl = ccnxWireFormatMessageInterface_GetInterface(message); + assertNotNull(impl, "WireFormatMessage must have an valid implementation pointer."); + if (impl->assertValid) { + impl->assertValid(message); + } +} + +static CCNxWireFormatMessage * +_ccnxWireFormatMessage_CreateWithImpl(const CCNxWireFormatMessageInterface *impl, const PARCBuffer *wireFormatBuffer) +{ + CCNxWireFormatMessage *result = impl->create(wireFormatBuffer); + return result; +} + +CCNxWireFormatMessage * +ccnxWireFormatMessage_Create(const PARCBuffer *wireFormat) +{ + CCNxWireFormatMessage *result = NULL; + + uint8_t version = parcBuffer_GetAtIndex(wireFormat, 0); + switch (version) { + case CCNxTlvDictionary_SchemaVersion_V1: + result = _ccnxWireFormatMessage_CreateWithImpl(&CCNxWireFormatFacadeV1_Implementation, wireFormat); + break; + default: + // no action, will return NULL + break; + } + + return result; +} + +static CCNxWireFormatMessageInterface * +_getImplForSchema(const CCNxTlvDictionary_SchemaVersion schemaVersion) +{ + CCNxWireFormatMessageInterface *result = NULL; + switch (schemaVersion) { + case CCNxTlvDictionary_SchemaVersion_V1: + result = &CCNxWireFormatFacadeV1_Implementation; + break; + default: + trapIllegalValue(data->fwd_state->nextMessage.version, "Unknown schema version: %d", schemaVersion); + } + return result; +} + +CCNxWireFormatMessage * +ccnxWireFormatMessage_FromInterestPacketType(const CCNxTlvDictionary_SchemaVersion schemaVersion, const PARCBuffer *wireFormat) +{ + CCNxWireFormatMessageInterface *impl = _getImplForSchema(schemaVersion); + CCNxWireFormatMessage *result = NULL; + + if (impl->fromInterestPacketType != NULL) { + result = impl->fromInterestPacketType(wireFormat); + } + + return result; +} + +CCNxWireFormatMessage * +ccnxWireFormatMessage_FromInterestPacketTypeIoVec(const CCNxTlvDictionary_SchemaVersion schemaVersion, const CCNxCodecNetworkBufferIoVec *vec) +{ + CCNxWireFormatMessageInterface *impl = _getImplForSchema(schemaVersion); + CCNxWireFormatMessage *result = NULL; + + if (impl->fromInterestPacketTypeIoVec != NULL) { + result = impl->fromInterestPacketTypeIoVec(vec); + } + + return result; +} + +CCNxWireFormatMessage * +ccnxWireFormatMessage_FromContentObjectPacketType(const CCNxTlvDictionary_SchemaVersion schemaVersion, const PARCBuffer *wireFormat) +{ + CCNxWireFormatMessageInterface *impl = _getImplForSchema(schemaVersion); + CCNxWireFormatMessage *result = NULL; + + if (impl->fromContentObjectPacketType != NULL) { + result = impl->fromContentObjectPacketType(wireFormat); + } + + return result; +} + +CCNxWireFormatMessage * +ccnxWireFormatMessage_FromControlPacketType(const CCNxTlvDictionary_SchemaVersion schemaVersion, const PARCBuffer *wireFormat) +{ + CCNxWireFormatMessageInterface *impl = _getImplForSchema(schemaVersion); + CCNxWireFormatMessage *result = NULL; + + if (impl->fromControlPacketType != NULL) { + result = impl->fromControlPacketType(wireFormat); + } + + return result; +} + +CCNxTlvDictionary * +ccnxWireFormatMessage_GetDictionary(const CCNxWireFormatMessage *message) +{ + return (CCNxTlvDictionary *) message; +} + + +CCNxCodecNetworkBufferIoVec * +ccnxWireFormatMessage_GetIoVec(const CCNxWireFormatMessage *message) +{ + ccnxWireFormatMessage_OptionalAssertValid(message); + CCNxWireFormatMessageInterface *impl = ccnxWireFormatMessageInterface_GetInterface(message); + + CCNxCodecNetworkBufferIoVec *result = NULL; + + if (impl->getIoVec != NULL) { + result = impl->getIoVec(message); + } else { + trapNotImplemented("ccnxWireFormatMessage_GetIoVec"); + } + + return result; +} + +bool +ccnxWireFormatMessage_PutIoVec(CCNxWireFormatMessage *message, CCNxCodecNetworkBufferIoVec *vec) +{ + ccnxWireFormatMessage_OptionalAssertValid(message); + CCNxWireFormatMessageInterface *impl = ccnxWireFormatMessageInterface_GetInterface(message); + + bool result = false; + + if (impl->putIoVec != NULL) { + result = impl->putIoVec(message, vec); + } else { + trapNotImplemented("ccnxWireFormatMessage_PutIoVec"); + } + + return result; +} + +bool +ccnxWireFormatMessage_PutWireFormatBuffer(CCNxWireFormatMessage *message, PARCBuffer *buffer) +{ + ccnxWireFormatMessage_OptionalAssertValid(message); + CCNxWireFormatMessageInterface *impl = ccnxWireFormatMessageInterface_GetInterface(message); + + bool result = false; + + if (impl->putWireFormatBuffer != NULL) { + result = impl->putWireFormatBuffer(message, buffer); + } else { + trapNotImplemented("ccnxWireFormatMessage_PutWireFormatBuffer"); + } + + return result; +} + +PARCBuffer * +ccnxWireFormatMessage_GetWireFormatBuffer(const CCNxWireFormatMessage *message) +{ + ccnxWireFormatMessage_OptionalAssertValid(message); + CCNxWireFormatMessageInterface *impl = ccnxWireFormatMessageInterface_GetInterface(message); + + PARCBuffer *result = NULL; + + if (impl->getWireFormatBuffer != NULL) { + result = impl->getWireFormatBuffer(message); + } else { + trapNotImplemented("ccnxWireFormatMessage_GetWireFormatBuffer"); + } + + return result; +} + +void +ccnxWireFormatMessage_WriteToFile(const CCNxWireFormatMessage *message, const char *filename) +{ + ccnxWireFormatMessage_OptionalAssertValid(message); + CCNxWireFormatMessageInterface *impl = ccnxWireFormatMessageInterface_GetInterface(message); + + if (impl->writeToFile != NULL) { + impl->writeToFile(message, filename); + } else { + trapNotImplemented("ccnxWireFormatMessage_WriteToFile"); + } +} + +bool +ccnxWireFormatMessage_SetProtectedRegionStart(CCNxWireFormatMessage *message, size_t startPosition) +{ + ccnxWireFormatMessage_OptionalAssertValid(message); + CCNxWireFormatMessageInterface *impl = ccnxWireFormatMessageInterface_GetInterface(message); + + bool result = false; + + if (impl->setProtectedRegionStart != NULL) { + result = impl->setProtectedRegionStart(message, startPosition); + } else { + trapNotImplemented("ccnxWireFormatMessage_SetProtectedRegionStart"); + } + + return result; +} + +bool +ccnxWireFormatMessage_SetProtectedRegionLength(CCNxWireFormatMessage *message, size_t length) +{ + ccnxWireFormatMessage_OptionalAssertValid(message); + CCNxWireFormatMessageInterface *impl = ccnxWireFormatMessageInterface_GetInterface(message); + + bool result = false; + + if (impl->setProtectedRegionLength != NULL) { + result = impl->setProtectedRegionLength(message, length); + } else { + trapNotImplemented("ccnxWireFormatMessage_SetProtectedRegionLength"); + } + + return result; +} + + +PARCCryptoHash * +ccnxWireFormatMessage_HashProtectedRegion(const CCNxWireFormatMessage *message, PARCCryptoHasher *hasher) +{ + ccnxWireFormatMessage_OptionalAssertValid(message); + CCNxWireFormatMessageInterface *impl = ccnxWireFormatMessageInterface_GetInterface(message); + + PARCCryptoHash *result = NULL; + + if (impl->hashProtectedRegion != NULL) { + result = impl->hashProtectedRegion(message, hasher); + } else { + trapNotImplemented("ccnxWireFormatMessage_HashProtectedRegion"); + } + + return result; +} + +PARCCryptoHash * +ccnxWireFormatMessage_CreateContentObjectHash(CCNxWireFormatMessage *message) +{ + ccnxWireFormatMessage_AssertValid(message); + CCNxWireFormatMessageInterface *impl = ccnxWireFormatMessageInterface_GetInterface(message); + + PARCCryptoHash *result = NULL; + + if (impl->computeContentObjectHash != NULL) { + result = impl->computeContentObjectHash(message); + } else { + trapNotImplemented("ccnxWireFormatMessage_ComputeContentObjectHash"); + } + + return result; +} + +CCNxWireFormatMessage * +ccnxWireFormatMessage_Acquire(const CCNxWireFormatMessage *message) +{ + return ccnxTlvDictionary_Acquire(message); +} + +void +ccnxWireFormatMessage_Release(CCNxWireFormatMessage **message) +{ + ccnxTlvDictionary_Release(message); +} + +bool +ccnxWireFormatMessage_SetHopLimit(CCNxWireFormatMessage *message, uint32_t hoplimit) +{ + bool result = false; + + ccnxWireFormatMessage_OptionalAssertValid(message); + CCNxWireFormatMessageInterface *impl = ccnxWireFormatMessageInterface_GetInterface(message); + + if (impl != NULL) { + result = impl->setHopLimit(message, hoplimit); + } + + return result; +} + +bool +ccnxWireFormatMessage_ConvertInterestToInterestReturn(CCNxWireFormatMessage *message, uint8_t returnCode) +{ + bool result = false; + + ccnxWireFormatMessage_OptionalAssertValid(message); + CCNxWireFormatMessageInterface *impl = ccnxWireFormatMessageInterface_GetInterface(message); + + if (impl != NULL) { + result = impl->convertInterestToInterestReturn(message, returnCode); + } + return result; +} + diff --git a/libccnx-common/ccnx/common/ccnx_WireFormatMessage.h b/libccnx-common/ccnx/common/ccnx_WireFormatMessage.h new file mode 100755 index 00000000..7a61e23b --- /dev/null +++ b/libccnx-common/ccnx/common/ccnx_WireFormatMessage.h @@ -0,0 +1,422 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @brief A structure of functions to enable accessing a CCNxTlvDictionary as a + * wire format object. + * + */ + +#ifndef __CCNx_Common__ccnx_WireFormatMessage__ +#define __CCNx_Common__ccnx_WireFormatMessage__ + +#include <ccnx/common/internal/ccnx_WireFormatMessageInterface.h> +#include <ccnx/common/internal/ccnx_TlvDictionary.h> + +#include <parc/security/parc_CryptoHash.h> + +typedef CCNxTlvDictionary CCNxWireFormatMessage; + +/** + * Creates a new CCNxWireFormatMessage instance from the `wireFormat` buffer passed in. + * The schema version and the message type are determined from `wireFormat`. + * + * @param wireFormat - a buffer, in wire format, representing the message to created. + * @return A new instance of CCNxWireFormatMessage that must eventually be released by calling + * {@link ccnxWireFormatMessage_Release}. + * + * Example: + * @code + * <#example#> + * @endcode + * @see ccnxWireFormatMessage_Release + */ +CCNxWireFormatMessage *ccnxWireFormatMessage_Create(const PARCBuffer *wireFormat); + +/** + * Creates dictionary of Interest type from the wire format + * + * <#Discussion#> + * + * @param <#param1#> + * @return <#return#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +CCNxWireFormatMessage *ccnxWireFormatMessage_FromInterestPacketType(const CCNxTlvDictionary_SchemaVersion schemaVersion, const PARCBuffer *wireFormat); + +/** + * Creates dictionary of Interest type from the wire format + * + * <#Discussion#> + * + * @param <#param1#> + * @return <#return#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +CCNxWireFormatMessage *ccnxWireFormatMessage_FromInterestPacketTypeIoVec(const CCNxTlvDictionary_SchemaVersion schemaVersion, const CCNxCodecNetworkBufferIoVec *vec); + +/** + * Creates a dictionary of ContentObject type from the wire format + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +CCNxWireFormatMessage *ccnxWireFormatMessage_FromContentObjectPacketType(const CCNxTlvDictionary_SchemaVersion schemaVersion, const PARCBuffer *wireFormat); + +/** + * Creates a dictionary of Control type from the wire format + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +CCNxWireFormatMessage *ccnxWireFormatMessage_FromControlPacketType(const CCNxTlvDictionary_SchemaVersion schemaVersion, const PARCBuffer *wireFormat); + +#ifdef Libccnx_DISABLE_VALIDATION +# define ccnxWireFormatMessage_OptionalAssertValid(_instance_) +#else +# define ccnxWireFormatMessage_OptionalAssertValid(_instance_) ccnxWireFormatMessage_AssertValid(_instance_) +#endif + +/** + * Assert that the given `CCNxWireFormatMessage` instance is valid. + * + * @param [in] instance A pointer to a valid CCNxWireFormatMessage instance. + * + * Example: + * @code + * { + * CCNxWireFormatMessage *a = ccnxWireFormatMessage_Create(); + * + * ccnxWireFormatMessage_AssertValid(a); + * + * printf("Instance is valid.\n"); + * + * ccnxWireFormatMessage_Release(&b); + * } + * @endcode + */ +void ccnxWireFormatMessage_AssertValid(const CCNxWireFormatMessage *message); + +/** + * Returns the PARCBuffer that wraps the entire wireformat representation + * + * May be NULL if there is no wire format yet (e.g. you're going down the stack before the codec) + * + * @param [<#in#> | <#out#> | <#in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +PARCBuffer *ccnxWireFormatMessage_GetWireFormatBuffer(const CCNxWireFormatMessage *dictionary); + +/** + * Returns the CCNxCodecNetworkBufferIoVec that wraps the entire wireformat representation + * + * May be NULL if there is no wire format yet (e.g. you're going down the stack before the codec), or it + * may be NULL because the wireformat is wrapped in a PARCBuffer instead. + * + * @param [<#in#> | <#out#> | <#in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +CCNxCodecNetworkBufferIoVec *ccnxWireFormatMessage_GetIoVec(const CCNxWireFormatMessage *dictionary); + + +/** + * Sets the wire format buffer in a Dictionary + * + * You can only put the wire format once. Will store its down reference to the wire format, so + * caller should release their copy when done with it. + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return true Value was put in the dictionary + * @return false Value not put, likely already existed + * + * Example: + * @code + * { + * + * } + * @endcode + */ +bool ccnxWireFormatMessage_PutWireFormatBuffer(CCNxWireFormatMessage *dictionary, PARCBuffer *buffer); + +/** + * Sets the wire format buffer in a Dictionary + * + * You can only put the wire format once. Will store its down reference to the wire format, so + * caller should release their copy when done with it. + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return true Value was put in the dictionary + * @return false Value not put, likely already existed + * + * Example: + * @code + * { + * + * } + * @endcode + */ +bool ccnxWireFormatMessage_PutIoVec(CCNxWireFormatMessage *dictionary, CCNxCodecNetworkBufferIoVec *vec); + +/** + * Writes the wire format to the specified file + * + * The file will be truncated to 0. If there is no wire format, the file will remain at 0 bytes. + * + * You can view the packet with the Wireshark plugin. First convert it to a TCP enapsulation + * (the program text2pcap is included as part of WireShark). The part "- filename.pcap" means that + * text2pcap will read from stdin (the "-") and write to "filename.pcap". Text2pcap requires a + * text format derived from "od" or from "hexdump". + * + * od -Ax -tx1 -v filename | text2pcap -T 9695,9695 - filename.pcap + * tshark -r filename.pcap -V + * + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +void ccnxWireFormatMessage_WriteToFile(const CCNxWireFormatMessage *dictionary, const char *filename); + +/** + * Write to the dictionary the start of the protection region + * + * The protected region is the byte range that is protected by the signature (validation payload). + * This sets the validation region in the dictionary. + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return false A validation region is already set, cannot overwrite + * @return true The validation region was set in the dictionary + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool ccnxWireFormatMessage_SetProtectedRegionStart(CCNxWireFormatMessage *dictionary, size_t startPosition); + +/** + * Write to the dictionary the length of the protection region + * + * The protected region is the byte range that is protected by the signature (validation payload). + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return false A validation region is already set, cannot overwrite + * @return true The validation region was set in the dictionary + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool ccnxWireFormatMessage_SetProtectedRegionLength(CCNxWireFormatMessage *dictionary, size_t length); + +/** + * Runs a hasher over the protected part of the wire format message + * + * The protected part of the wire format message is from the start of the CCNx Message + * through the end of the Validation Algorithm. + * + * If no validation algorithm block exists, then there is no protection region and + * we cannot compute the hash. + * + * The protection region is computed by the CCNxCodecTlvDecoder and stored in the dictionary. + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return null There is no protection region + * @return non-null The computed hash + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCCryptoHash *ccnxWireFormatMessage_HashProtectedRegion(const CCNxWireFormatMessage *dictionary, PARCCryptoHasher *hasher); + +/** + * Calculates the ContentObject Hash, which is the SHA256 hash of the protected part of the wire format message. + * + * The protected part of the wire format message is from the start of the CCNx Message + * through the end of the Validation Algorithm. + * + * The protection region is computed by the CCNxCodecTlvDecoder and stored in the dictionary. + * + * This function must only be called on a dictionary that contains a ContentObject and a WireFormat buffer. + * + * @param [in] dictionary The ContentObject message on which to calculate its hash. + * + * @return null if we could not calculate the hash. This could mean the protected region wasn't set, or it wasn't a ContentObject. + * @return non-null The computed hash + * + * Example: + * @code + * <#example#> + * @endcode + */ + +PARCCryptoHash *ccnxWireFormatMessage_CreateContentObjectHash(CCNxWireFormatMessage *dictionary); + +/** + * Returns a pointer to the CCNxTlvDictionary underlying the specified CCNxWireFormatMessage. + * + * @param message - The CCNxWireFormatMessage instance from which to retrieve the CCNxTlvDictionary. + * @return A pointer to the underlying CCNxTlvDictionary + * + * Example: + * @code + * <#example#> + * @endcode + */ +CCNxTlvDictionary *ccnxWireFormatMessage_GetDictionary(const CCNxWireFormatMessage *message); + +/** + * Increase the number of references to a `CCNxWireFormatMessage`. + * + * Note that a new `CCNxWireFormatMessage` is not created, + * only that the given `CCNxWireFormatMessage` reference count is incremented. + * Discard the reference by invoking {@link ccnxWireFormatMessage_Release}. + * + * @param [in] message A pointer to the original instance. + * @return The value of the input parameter @p instance. + * + * Example: + * @code + * { + * + * CCNxWireFormat *reference = ccnxWireFormatMessage_Acquire(message); + * + * ... + * + * ccnxWireFormatMessage_Release(&reference); + * + * } + * @endcode + * + * @see {@link ccnxWireFormatMessage_Release} + */ +CCNxWireFormatMessage *ccnxWireFormatMessage_Acquire(const CCNxWireFormatMessage *message); + +/** + * Release a previously acquired reference to the specified instance, + * decrementing the reference count for the instance. + * + * The pointer to the instance is set to NULL as a side-effect of this function. + * + * If the invocation causes the last reference to the instance to be released, + * the instance is deallocated and the instance's implementation will perform + * additional cleanup and release other privately held references. + * + * @param [in,out] messageP A pointer to a pointer to the instance to release. + * + * Example: + * @code + * { + * CCNxWireFormatMessage *reference = ccnxWireFormatMessage_Acquire(message); + * + * ... + * + * ccnxWireFormatMessage_Release(&reference); + * + * } + * @endcode + * + * @see {@link ccnxWireFormatMessage_Acquire} + */ +void ccnxWireFormatMessage_Release(CCNxWireFormatMessage **messageP); + +/** + * Write a hoplimit to a messages attached wire format io vectors or buffers + * + * @param [in] message + * @param [in] hoplimit + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool ccnxWireFormatMessage_SetHopLimit(CCNxWireFormatMessage *message, uint32_t hoplimit); + +/** + * Given an Interest (as a CCNxWireFormatMessage), convert it to an InterestReturn and + * set the return code of the InterestReturn. This does not create a new instance, but + * simply modifies the supplied Interest in place. + * + * @param [in] message - the Interest (as a CCNxWireFormatMessage) to convert. + * @param [in] returnCode - the InterestReturn code to embed in the message. + * + * Example: + * @code + * { + * CCNxWireFormatMessage *interest = <...>; + * + * CCNxWireFormatMessage *interestReturn = + * ccnxWireFormatMessage_ConvertInterestToInterestReturn(interest, + * CCNxInterestReturn_ReturnCode_HopLimitExceeded); + * + * } + * @endcode + */ +bool ccnxWireFormatMessage_ConvertInterestToInterestReturn(CCNxWireFormatMessage *message, uint8_t returnCode); +#endif /* defined(__CCNx_Common__ccnx_WireFormatMessage__) */ diff --git a/libccnx-common/ccnx/common/codec/ccnxCodec_EncodingBuffer.c b/libccnx-common/ccnx/common/codec/ccnxCodec_EncodingBuffer.c new file mode 100644 index 00000000..2fdab85f --- /dev/null +++ b/libccnx-common/ccnx/common/codec/ccnxCodec_EncodingBuffer.c @@ -0,0 +1,559 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +#include <config.h> +#include <stdio.h> +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Object.h> + +#include <LongBow/runtime.h> + +#include <ccnx/common/codec/ccnxCodec_EncodingBuffer.h> + +struct ccnx_tlv_encoding_buffer_linked_array; +typedef struct ccnx_tlv_encoding_buffer_linked_array _CCNxCodecEncodingBufferLinkedArray; + +typedef struct array_entry { + struct iovec vec; + PARCBuffer *buffer; +} _ArrayEntry; + +/** + * @typedef CCNxCodecEncodingBufferLinkedArray + * @abstract A chain of arrays of PARCBuffers + * @constant next The next EncodingBuffer in the chain + * @constant prev The previous EncodingBuffer in the chain + * @constant cacapcity The array capacity of bufferArray + * @constant count The number of items in this EncodingBuffer + * @constant bytes The bytes in this EncodingBuffer + * @constant bufferArray An array of (PARCBuffer *) + * @discussion If 'next' is NULL, then new elements are appendig to bufferArray. If 'next' is + * not null, then calls to Append will go to the bufferArray at the tail of the linked list. + */ +struct ccnx_tlv_encoding_buffer_linked_array { + // we can chain these encoding buffers together to make + // one long linear list + _CCNxCodecEncodingBufferLinkedArray *next; + _CCNxCodecEncodingBufferLinkedArray *prev; + + // the number of elements allocated the array + uint32_t capacity; + + // The total number of elements in this bufferArray + uint32_t count; + + // The total bytes of this bufferArray + size_t bytes; + + // each encoding buffer is an array of _ArrayEntry structures containing + // a PARCBuffer reference and a vector referencing the PARCBuffer contents. + _ArrayEntry *array; +}; + +/** + * @typedef CCNxCodecEncodingBuffer + * @abstract A chain of arrays of PARCBuffers + * @constant next The next EncodingBuffer in the chain + * @constant prev The previous EncodingBuffer in the chain + * @constant cacapcity The array capacity of bufferArray + * @constant totalCount The total number of items in this EncodingBuffer and all subsequent + * @constant totalBytes The total number of bytes in this EncodingBuffer and all subsequent + * @constant bufferArray An array of (PARCBuffer *) + * @discussion If 'next' is NULL, then new elements are appendig to bufferArray. If 'next' is + * not null, then calls to Append will go to the bufferArray at the tail of the linked list. + */ +struct ccnx_codec_encoding_buffer { + _CCNxCodecEncodingBufferLinkedArray *head; + _CCNxCodecEncodingBufferLinkedArray *tail; + + // The total number of elements in all LinkedArrays + uint32_t totalCount; + + // The total bytes in all LinkedArrays + size_t totalBytes; +}; + +static void _ccnxCodecEncodingBufferEntry_SetIOVec(PARCBuffer *buffer, struct iovec *iov); + +static const uint32_t DEFAULT_CAPACITY = 32; + +// ====================================================================================== +// CCNxCodecEncodingBufferLinkedArray releated + +static void +_ccnxCodecEncodingBufferLinkedArray_Display(const _CCNxCodecEncodingBufferLinkedArray *array, + int indentation) +{ + printf("Entry %p prev %p next %p capacity %u count %u bytes %zu\n", + (void *) array, (void *) array->prev, (void *) array->next, array->capacity, array->count, array->bytes); + + size_t totalBytes = 0; + + for (int i = 0; i < array->count; i++) { + size_t bytes = array->array[i].vec.iov_len; + totalBytes += bytes; + printf(" %3d iovec_base=%p bytes=%4zu total bytes=%4zu\n", + i, array->array[i].vec.iov_base, bytes, totalBytes); + } +} + +static _CCNxCodecEncodingBufferLinkedArray * +_ccnxCodecEncodingBufferLinkedArray_Create(uint32_t capacity) +{ + // allocation for the object plus the array of buffers + _CCNxCodecEncodingBufferLinkedArray *array = parcMemory_Allocate(sizeof(_CCNxCodecEncodingBufferLinkedArray)); + assertNotNull(array, "parcMemory_Allocate(%zu) returned NULL", sizeof(_CCNxCodecEncodingBufferLinkedArray)); + array->capacity = capacity; + array->bytes = 0; + array->count = 0; + array->next = NULL; + array->prev = NULL; + array->array = parcMemory_AllocateAndClear(sizeof(_ArrayEntry) * capacity); + return array; +} + +/** + * A LinkedArray can only be released if it has been removed from the EncodingBuffer + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +static void +_ccnxCodecEncodingBufferLinkedArray_Release(_CCNxCodecEncodingBufferLinkedArray **arrayPtr) +{ + assertNotNull(arrayPtr, "Parameter must be non-null"); + _CCNxCodecEncodingBufferLinkedArray *array = *arrayPtr; + + assertNull(array->prev, "array->prev must be null") + { + _ccnxCodecEncodingBufferLinkedArray_Display(array, 0); + } + + assertNull(array->next, "array->next must be null") + { + _ccnxCodecEncodingBufferLinkedArray_Display(array, 0); + } + + for (int i = 0; i < array->count; i++) { + if (array->array[i].buffer) { + parcBuffer_Release(&(array->array[i].buffer)); + } + } + + parcMemory_Deallocate(&(array->array)); + parcMemory_Deallocate((void **) &array); +} + +static void +_ccnxCodecEncodingBufferLinkedArray_Validate(const _CCNxCodecEncodingBufferLinkedArray *array) +{ + assertNotNull(array, "Parameter list must be non-null"); + + if (array->next) { + assertTrue(array->next->prev == array, + "next->prev does not point to this entry: entry %p next %p next->prev %p", + (void *) array, (void *) array->next, (void *) array->next->prev) + { + _ccnxCodecEncodingBufferLinkedArray_Display(array, 0); + } + } + + if (array->prev) { + assertTrue(array->prev->next == array, + "prev->next does not point to this entry: entry %p prev %p prev->next %p", + (void *) array, (void *) array->prev, (void *) array->prev->next) + { + _ccnxCodecEncodingBufferLinkedArray_Display(array, 0); + } + } + + assertTrue(array->count <= array->capacity, + "Array count greater than capacity: count %u capacity %u", + array->count, array->capacity) + { + _ccnxCodecEncodingBufferLinkedArray_Display(array, 0); + } + + size_t totalBytes = 0; + + for (int i = 0; i < array->count; i++) { + totalBytes += array->array[i].vec.iov_len; + } + + assertTrue(totalBytes == array->bytes, + "Array bytes wrong, got %zu expected %zu", + totalBytes, array->bytes); +} + +__attribute__((unused)) +static void +_ccnxCodecEncodingBuffer_Validate(CCNxCodecEncodingBuffer *list) +{ + assertNotNull(list, "List is null"); + + // either we have both a head and a tail or neither + assertTrue((list->head && list->tail) || !(list->head || list->tail), + "List has a mixture of null head or tail: list %p head %p tail %p", + (void *) list, (void *) list->head, (void *) list->tail) + { + ccnxCodecEncodingBuffer_Display(list, 0); + } + + assertTrue(list->head == NULL || list->head->prev == NULL, + "List head has head->prev: list %p head %p head->prev %p", + (void *) list, (void *) list->head, (void *) list->tail) + { + ccnxCodecEncodingBuffer_Display(list, 0); + } + + assertTrue(list->tail == NULL || list->tail->next == NULL, + "List tail has tail->next: list %p tail %p tail->next %p", + (void *) list, (void *) list->head, (void *) list->tail) + { + ccnxCodecEncodingBuffer_Display(list, 0); + } + + + // walk the linked list and make sure the count is equal to what we expect + size_t itemCount = 0; + size_t totalBytes = 0; + _CCNxCodecEncodingBufferLinkedArray *next = list->head; + while (next != NULL) { + _ccnxCodecEncodingBufferLinkedArray_Validate(next); + + itemCount += next->count; + totalBytes += next->bytes; + + if (next->next == NULL) { + assertTrue(next == list->tail, + "Found list link with null next, but it is not tail: list %p list->tail %p entry %p", + (void *) list, (void *) list->tail, (void *) next) + { + ccnxCodecEncodingBuffer_Display(list, 0); + _ccnxCodecEncodingBufferLinkedArray_Display(next, 0); + } + } + next = next->next; + } + + assertTrue(itemCount == list->totalCount, "Wrong itemCount, got %zu expected %u", itemCount, list->totalCount); + assertTrue(totalBytes == list->totalBytes, "Wrong totalBytes, got %zu expected %zu", totalBytes, list->totalBytes); +} + + +// ====================================================================================== + +static void +_ccnxCodecEncodingBuffer_Remove(CCNxCodecEncodingBuffer *list, _CCNxCodecEncodingBufferLinkedArray *array) +{ + if (array->prev) { + array->prev->next = array->next; + } + + if (array->next) { + array->next->prev = array->prev; + } + + if (list->head == array) { + list->head = array->next; + } else if (list->tail == array) { + list->tail = array->prev; + } + + array->next = NULL; + array->prev = NULL; + + assertTrue(list->totalBytes >= array->bytes, + "list bytes smaller than array: list %zu array %zu", list->totalBytes, array->bytes); + assertTrue(list->totalCount >= array->count, + "list count smaller than array: list %u array %u", list->totalCount, array->count); + + list->totalCount -= array->count; + list->totalBytes -= array->bytes; +} + +static void +_ccnxCodecEncodingBuffer_FinalRelease(CCNxCodecEncodingBuffer **listBufferPtr) +{ + CCNxCodecEncodingBuffer *list = *listBufferPtr; + + _CCNxCodecEncodingBufferLinkedArray *next = list->head; + while (next != NULL) { + _CCNxCodecEncodingBufferLinkedArray *nextnext = next->next; + _ccnxCodecEncodingBuffer_Remove(list, next); + _ccnxCodecEncodingBufferLinkedArray_Release(&next); + next = nextnext; + } +} + +parcObject_ExtendPARCObject(CCNxCodecEncodingBuffer, _ccnxCodecEncodingBuffer_FinalRelease, NULL, NULL, NULL, NULL, NULL, NULL); + +parcObject_ImplementAcquire(ccnxCodecEncodingBuffer, CCNxCodecEncodingBuffer); + +parcObject_ImplementRelease(ccnxCodecEncodingBuffer, CCNxCodecEncodingBuffer); + +CCNxCodecEncodingBuffer * +ccnxCodecEncodingBuffer_Create(void) +{ + CCNxCodecEncodingBuffer *listBuffer = parcObject_CreateInstance(CCNxCodecEncodingBuffer); + listBuffer->head = NULL; + listBuffer->tail = NULL; + listBuffer->totalCount = 0; + listBuffer->totalBytes = 0; + return listBuffer; +} + +void +ccnxCodecEncodingBuffer_Display(const CCNxCodecEncodingBuffer *list, int indentation) +{ + printf("List %p head %p tail %p itemCount %u totalBytes %zu\n", + (void *) list, (void *) list->head, (void *) list->tail, list->totalCount, list->totalBytes); + + size_t totalCount = 0; + size_t totalBytes = 0; + size_t position = 0; + _CCNxCodecEncodingBufferLinkedArray *next = list->head; + while (next != NULL) { + printf(" %3zu: entry %p prev %p next %p\n", + position, (void *) next, (void *) next->prev, (void *) next->next); + _ccnxCodecEncodingBufferLinkedArray_Display(next, indentation); + + totalCount += next->count; + totalBytes += next->bytes; + position++; + next = next->next; + } +} + +static void +_ccnxCodecEncodingBuffer_AppendLinkedArray(CCNxCodecEncodingBuffer *list, _CCNxCodecEncodingBufferLinkedArray *array) +{ + if (list->tail) { + list->tail->next = array; + } else { + // if list tail is null, it means list head is null too + list->head = array; + } + + array->prev = list->tail; + list->tail = array; +} + +static void +_ccnxCodecEncodingBuffer_PrependLinkedArray(CCNxCodecEncodingBuffer *list, _CCNxCodecEncodingBufferLinkedArray *array) +{ + array->next = list->head; + array->prev = list->head->prev; + list->head->prev = array; + list->head = array; +} + +size_t +ccnxCodecEncodingBuffer_PrependBuffer(CCNxCodecEncodingBuffer *list, PARCBuffer *buffer) +{ + assertNotNull(list, "Parameter list must be non-null"); + assertNotNull(buffer, "Parameter buffer must be non-null"); + + _CCNxCodecEncodingBufferLinkedArray *head = list->head; + if ((head == NULL) || (list->head->count == list->head->capacity)) { + head = _ccnxCodecEncodingBufferLinkedArray_Create(DEFAULT_CAPACITY); + _ccnxCodecEncodingBuffer_PrependLinkedArray(list, head); + } + + assertTrue(head->count < head->capacity, "head does not have any room left") + { + _ccnxCodecEncodingBufferLinkedArray_Display(head, 0); + } + + size_t position = list->head->count; + for (int i = 0; i < list->head->count; i++) { + head->array[i + 1] = head->array[i]; + } + head->array[0].buffer = parcBuffer_Acquire(buffer); + _ccnxCodecEncodingBufferEntry_SetIOVec(buffer, &head->array[0].vec); + + size_t bytes = head->array[0].vec.iov_len; + head->bytes += bytes; + list->totalBytes += bytes; + + head->count++; + list->totalCount++; + + return position; +} + +size_t +ccnxCodecEncodingBuffer_AppendBuffer(CCNxCodecEncodingBuffer *list, PARCBuffer *buffer) +{ + assertNotNull(list, "Parameter list must be non-null"); + assertNotNull(buffer, "Parameter buffer must be non-null"); + + _CCNxCodecEncodingBufferLinkedArray *tail = list->tail; + if (tail == NULL || list->tail->count == list->tail->capacity) { + tail = _ccnxCodecEncodingBufferLinkedArray_Create(DEFAULT_CAPACITY); + _ccnxCodecEncodingBuffer_AppendLinkedArray(list, tail); + } + + assertTrue(tail->count < tail->capacity, "tail does not have any room left") + { + _ccnxCodecEncodingBufferLinkedArray_Display(tail, 0); + } + + size_t position = list->totalCount; + tail->array[tail->count].buffer = parcBuffer_Acquire(buffer); + _ccnxCodecEncodingBufferEntry_SetIOVec(buffer, &tail->array[tail->count].vec); + + size_t bytes = tail->array[tail->count].vec.iov_len; + tail->bytes += bytes; + list->totalBytes += bytes; + + tail->count++; + list->totalCount++; + + return position; +} + +// Returns the number of elements in the list +size_t +ccnxCodecEncodingBuffer_Size(const CCNxCodecEncodingBuffer *buffer) +{ + assertNotNull(buffer, "Parameter buffer must be non-null"); + return buffer->totalCount; +} + +// Returns the total number of bytes in the list +size_t +ccnxCodecEncodingBuffer_Length(const CCNxCodecEncodingBuffer *buffer) +{ + assertNotNull(buffer, "Parameter buffer must be non-null"); + return buffer->totalBytes; +} + +// ====================================================================================== + +// ====================================================================================== + +static void +_ccnxCodecEncodingBufferEntry_SetIOVec(PARCBuffer *buffer, struct iovec *iov) +{ + // this can return NULL for a 0 capacity entry + PARCByteArray *byteArray = parcBuffer_Array(buffer); + + if (byteArray) { + uint8_t *array = parcByteArray_Array(byteArray); + + // we need to advance the array so it is at the buffer's offset. + size_t offset = parcBuffer_ArrayOffset(buffer) + parcBuffer_Position(buffer); + + iov->iov_base = (array + offset); + iov->iov_len = parcBuffer_Remaining(buffer); + } else { + iov->iov_base = NULL; + iov->iov_len = 0; + } +} + +// Creates an iovec array pointing to the PARCBuffer contents at offset for length +CCNxCodecEncodingBuffer * +ccnxCodecEncodingBuffer_Slice(CCNxCodecEncodingBuffer *encodingBuffer, size_t offset, size_t length) +{ + CCNxCodecEncodingBuffer *listBuffer = parcObject_CreateInstance(CCNxCodecEncodingBuffer); + listBuffer->head = NULL; + listBuffer->tail = NULL; + listBuffer->totalCount = 0; + listBuffer->totalBytes = 0; + + _CCNxCodecEncodingBufferLinkedArray *head; + head = _ccnxCodecEncodingBufferLinkedArray_Create(encodingBuffer->totalCount); // pessimistic + _ccnxCodecEncodingBuffer_AppendLinkedArray(listBuffer, head); + + _CCNxCodecEncodingBufferLinkedArray *next = encodingBuffer->head; + int position = 0; + while (next && length) { + for (int i = 0; (i < next->count) && length; i++) { + if ((offset >= position) && (offset < (position + next->array[i].vec.iov_len))) { + int remainder = 0; + head->array[head->count].buffer = parcBuffer_Acquire(next->array[i].buffer); + head->array[head->count].vec.iov_base = next->array[i].vec.iov_base + (offset - position); + remainder = next->array[i].vec.iov_len - (offset - position); + if (remainder > length) { + remainder = length; + } + head->array[head->count].vec.iov_len = remainder; + offset += remainder; + length -= remainder; + + head->count++; + head->bytes += remainder; + listBuffer->totalCount++; + listBuffer->totalBytes += remainder; + } + position += next->array[i].vec.iov_len; + } + next = next->next; + } + if (listBuffer->totalCount == 0) { + ccnxCodecEncodingBuffer_Release(&listBuffer); + } + + return listBuffer; +} + +// Creates an iovec array pointing to the PARCBuffer contents +CCNxCodecEncodingBufferIOVec * +ccnxCodecEncodingBuffer_CreateIOVec(CCNxCodecEncodingBuffer *encodingBuffer) +{ + // one allocation for the object and the array + size_t totalAllocation = sizeof(CCNxCodecEncodingBufferIOVec) + sizeof(struct iovec) * encodingBuffer->totalCount; + + CCNxCodecEncodingBufferIOVec *vec = parcMemory_Allocate(totalAllocation); + assertNotNull(vec, "parcMemory_Allocate(%zu) returned NULL", totalAllocation); + vec->encodingBuffer = ccnxCodecEncodingBuffer_Acquire(encodingBuffer); + vec->iovcnt = (int) encodingBuffer->totalCount; + + _CCNxCodecEncodingBufferLinkedArray *next = encodingBuffer->head; + int position = 0; + while (next) { + for (int i = 0; i < next->count; i++) { + vec->iov[position] = next->array[i].vec; + position++; + } + next = next->next; + } + + return vec; +} + +// releases the iovec structure, but does not release the contents. you must +// keep at least one reference to the parent CCNxCodecEncodingBuffer alive. +void +ccnxCodecEncodingBufferIOVec_Release(CCNxCodecEncodingBufferIOVec **iovecPtr) +{ + CCNxCodecEncodingBufferIOVec *iov = *iovecPtr; + ccnxCodecEncodingBuffer_Release(&iov->encodingBuffer); + parcMemory_Deallocate((void **) &iov); + *iovecPtr = NULL; +} diff --git a/libccnx-common/ccnx/common/codec/ccnxCodec_EncodingBuffer.h b/libccnx-common/ccnx/common/codec/ccnxCodec_EncodingBuffer.h new file mode 100644 index 00000000..05563762 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/ccnxCodec_EncodingBuffer.h @@ -0,0 +1,300 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file ccnxCodec_EncodingBuffer + * @brief An encoding buffer is a zero-copy vectored I/O for PARCBuffers + * + * An Encoding Buffer is an ordered list of PARCBuffers that can be written by functions like + * writev(). You can append and append to the list and the buffers are stored only by reference. + * + * You can also append one encoding buffer to another. In this case, the buffers are moved from + * the previous list to the end of the new list. + * + * @code + * { + * PARCBuffer *name = parcBuffer_Wrap("marc", 4, 0, 4); + * PARCBuffer *space= parcBuffer_Wrap(" ", 1, 0 ,1); + * PARCBuffer *email= parcBuffer_Wrap("<marc@example.com>", 18, 0, 18); + * + * CCNxCodecEncodingBuffer *encodingBuffer = ccnxCodecEncodingBuffer_Create(); + * ccnxCodecEncodingBuffer_BufferInsertTail(encodingBuffer, name); + * ccnxCodecEncodingBuffer_BufferInsertTail(encodingBuffer, space); + * parcBuffer_Release(&space); + * parcBuffer_Release(&name); + * + * CCNxCodecEncodingBuffer *emailBuffer = ccnxCodecEncodingBuffer_Create(); + * ccnxCodecEncodingBuffer_BufferInsertTail(emailBuffer, email); + * parcBuffer_Release(&email); + * + * ccnxCodecEncodingBuffer_MoveToTail(encodingBuffer, emailBuffer); + * ccnxCodecEncodingBuffer_Release(&emailBuffer); + * + * CCNxCodecEncodingBufferIOVec *iov = ccnxCodecEncodingBuffer_CreateIOVec(encodingBuffer); + * writev(STDOUT_FILENO, iov->iov, iov->iovcnt); + * ccnxCodecEncodingBufferIOVec_Release(&iov); + * + * ccnxCodecEncodingBuffer_Release(&encodingBuffer); + * } + * @endcode + * + */ + +#ifndef libccnx_ccnxCodec_EncodingBuffer_h +#define libccnx_ccnxCodec_EncodingBuffer_h + +#include <sys/uio.h> +#include <parc/algol/parc_Buffer.h> + +struct ccnx_codec_encoding_buffer; +typedef struct ccnx_codec_encoding_buffer CCNxCodecEncodingBuffer; + +/** + * @typedef CCNxCodecEncodingBufferIOVec + * @abstract Used for writev() or similar functions + * @constant encodingBuffer A reference counted copy of the encoding Buffer + * @constant iov An allocated array of iovec entries + * @constant iovcnt The number of array entries + * @discussion <#Discussion#> + */ +typedef struct ccnx_tlv_encoding_buffer_iovec { + CCNxCodecEncodingBuffer *encodingBuffer; + int iovcnt; + struct iovec iov[]; +} CCNxCodecEncodingBufferIOVec; + +// ====================================================================================== + +/** + * Creates an empty encoding buffer + * + * <#Paragraphs Of Explanation#> + * + * @return non-null An allocated encoding buffer + * @return null An error + * + * Example: + * @code + * <#example#> + * @endcode + */ +CCNxCodecEncodingBuffer *ccnxCodecEncodingBuffer_Create(void); + +/** + * Returns a reference counted copy + * + * Caller must call ccnxCodecEncodingBuffer_Release() on all copies. + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return non-null A reference counted copy + * @return null An error + * + * Example: + * @code + * <#example#> + * @endcode + */ +CCNxCodecEncodingBuffer *ccnxCodecEncodingBuffer_Acquire(const CCNxCodecEncodingBuffer *encodingBuffer); + + +/** + * Release the encoding buffer and all internal references + * + * will release the list and release our reference to all enclosed PARCBuffers + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +void ccnxCodecEncodingBuffer_Release(CCNxCodecEncodingBuffer **listBufferPtr); + +/** + * Displays the structure of the encoding buffer to STDOUT + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +void ccnxCodecEncodingBuffer_Display(const CCNxCodecEncodingBuffer *encodingBuffer, int indentation); + +/** + * Appends a PARCBuffer to the encoding buffer + * + * Appends to the encoding buffer a reference count to the given buffer. + * The return value is the storage node used in the internal data structure. + * + * The buffer will be used from its position at the time of use (i.e. when + * ccnxCodecEncodingBuffer_CreateIOVec() is called). It is important that no other + * use of the PARCBuffer move the Position. + * + * @param [in] encodingBuffer The buffer to append to + * @param [in] bufferToInsert The PARCBuffer to insert at the tail of the encoding buffer. + * + * @return number The position in the encoding buffer list + * + * Example: + * @code + * <#example#> + * @endcode + */ +size_t ccnxCodecEncodingBuffer_AppendBuffer(CCNxCodecEncodingBuffer *encodingBuffer, PARCBuffer *bufferToInsert); + +/** + * Prepends a PARCBuffer to the encoding buffer + * + * Prepends to the encoding buffer a reference count to the given buffer. + * The return value is the storage node used in the internal data structure. + * + * The buffer will be used from its position at the time of use (i.e. when + * ccnxCodecEncodingBuffer_CreateIOVec() is called). It is important that no other + * use of the PARCBuffer move the Position. + * + * @param [in] encodingBuffer The buffer to prepend to + * @param [in] bufferToInsert The PARCBuffer to insert at the head of the encoding buffer. + * + * @return number The position in the encoding buffer list + * + * Example: + * @code + * <#example#> + * @endcode + */ +size_t ccnxCodecEncodingBuffer_PrependBuffer(CCNxCodecEncodingBuffer *encodingBuffer, PARCBuffer *bufferToPrepend); + +/** + * Puts the value in scratch memory + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +void ccnxTlvEncodingbuffer_AppendUint16(CCNxCodecEncodingBuffer *encodingBuffer, uint16_t value); + +/** + * The number of elements in the list + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +size_t ccnxCodecEncodingBuffer_Size(const CCNxCodecEncodingBuffer *encodingBuffer); + +/** + * The total number of bytes in the list + * + * This is calculated as the sum of all PARCBuffer Remaining lengths in + * the encoding buffer. + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +size_t ccnxCodecEncodingBuffer_Length(const CCNxCodecEncodingBuffer *encodingBuffer); + +// ====================================================================================== + +/** + * Constructs an iovec array based on the buffers in the list + * + * The elements of the iovec array will be in the list order. + * Each iovec entry will point to the backing array of each PARCBuffer + * based on that buffers current position. + * + * This object contains a reference counted copy to the encoding buffer, so + * the caller can release the encoding buffer and hold on to only this object + * until the writev (or similar function) is done. + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return non-null The allocated IOVec structure + * @return null An error + * + * Example: + * @code + * <#example#> + * @endcode + */ +CCNxCodecEncodingBufferIOVec *ccnxCodecEncodingBuffer_CreateIOVec(CCNxCodecEncodingBuffer *encodingBuffer); + +/** + * Constructs an iovec array based on the buffers in the list that cooresponds to offset and length + * + * The elements of the iovec array will be in the list order. + * Each iovec entry will point to the backing array of each PARCBuffer + * based on that buffers current position. + * + * This object contains a reference counted copy to the encoding buffer, so + * the caller can release the encoding buffer and hold on to only this object + * until the writev (or similar function) is done. + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return non-null The allocated IOVec structure + * @return null An error, or the specified offset/length is not contained in the extent + * + * Example: + * @code + * <#example#> + * @endcode + */ +CCNxCodecEncodingBuffer *ccnxCodecEncodingBuffer_Slice(CCNxCodecEncodingBuffer *encodingBuffer, size_t offset, size_t length); + +/** + * Release the iovec object. + * + * This will release the IOVec object and release its reference to the encoding + * buffer. If this was the last reference to the encoding buffer, all references to + * the underlying PARCBuffers will be released too. + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +void ccnxCodecEncodingBufferIOVec_Release(CCNxCodecEncodingBufferIOVec **iovecPtr); + +#endif // libccnx_ccnxCodec_EncodingBuffer_h diff --git a/libccnx-common/ccnx/common/codec/ccnxCodec_Error.c b/libccnx-common/ccnx/common/codec/ccnxCodec_Error.c new file mode 100644 index 00000000..282ec210 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/ccnxCodec_Error.c @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <limits.h> + +#include <parc/algol/parc_Memory.h> +#include <LongBow/runtime.h> + +#include <ccnx/common/codec/ccnxCodec_Error.h> + +struct error_messages { + CCNxCodecErrorCodes code; + const char *message; +} TlvErrorMessages[] = { + { .code = TLV_ERR_NO_ERROR, .message = "No error" }, + { .code = TLV_ERR_VERSION, .message = "Unsupported version" }, + { .code = TLV_ERR_PACKETTYPE, .message = "Unsupported packet type" }, + { .code = TLV_ERR_BEYOND_PACKET_END, .message = "Field goes beyond end of packet" }, + { .code = TLV_ERR_TOO_LONG, .message = "Length too long for parent container" }, + { .code = TLV_ERR_NOT_FIXED_SIZE, .message = "Fixed size Type wrong Length" }, + { .code = TLV_ERR_DUPLICATE_FIELD, .message = "Duplicate field" }, + { .code = TLV_ERR_EMPTY_SPACE, .message = "The sum of child TLVs did not add up to parent container length" }, + + // missing mandatory field errors + { .code = TLV_MISSING_MANDATORY, .message = "Missing mandatory field" }, + + { .code = TLV_ERR_DECODE, .message = "Decoding error" }, + + { .code = TLV_ERR_PACKETLENGTH_TOO_SHORT, .message = "Packet length less than 8" }, + { .code = TLV_ERR_HEADERLENGTH_TOO_SHORT, .message = "Header length less than 8" }, + { .code = TLV_ERR_PACKETLENGTHSHORTER, .message = "Packet length less than header length" }, + + + // end of list sentinel, the NULL determines the end of list + { .code = UINT16_MAX, .message = NULL } +}; + + +const char * +ccnxCodecErrors_ErrorMessage(CCNxCodecErrorCodes code) +{ + for (int i = 0; TlvErrorMessages[i].message != NULL; i++) { + if (TlvErrorMessages[i].code == code) { + return TlvErrorMessages[i].message; + } + } + return "No error message found"; +} + +// ========================================================================== + +struct ccnx_codec_error { + CCNxCodecErrorCodes code; + const char *functionName; + int line; + size_t byteOffset; + unsigned refcount; + char *toString; +}; + +CCNxCodecError * +ccnxCodecError_Create(CCNxCodecErrorCodes code, const char *func, int line, size_t byteOffset) +{ + CCNxCodecError *error = parcMemory_AllocateAndClear(sizeof(CCNxCodecError)); + assertNotNull(error, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(CCNxCodecError)); + error->code = code; + error->functionName = func; + error->line = line; + error->byteOffset = byteOffset; + error->toString = NULL; // computed on the fly + error->refcount = 1; + return error; +} + +CCNxCodecError * +ccnxCodecError_Acquire(CCNxCodecError *error) +{ + assertNotNull(error, "Parameter error must be non-null"); + assertTrue(error->refcount > 0, "Parameter has 0 refcount, not valid"); + + error->refcount++; + return error; +} + +void +ccnxCodecError_Release(CCNxCodecError **errorPtr) +{ + assertNotNull(errorPtr, "Parameter must be non-null double pointer"); + assertNotNull(*errorPtr, "Parameter must derefernece to non-null pointer"); + CCNxCodecError *error = *errorPtr; + + assertTrue(error->refcount > 0, "Parameter has 0 refcount, not valid"); + error->refcount--; + if (error->refcount == 0) { + if (error->toString) { + // this is asprintf generated + free(error->toString); + } + parcMemory_Deallocate((void **) &error); + } + *errorPtr = NULL; +} + +size_t +ccnxCodecError_GetByteOffset(const CCNxCodecError *error) +{ + assertNotNull(error, "Parameter must be non-null"); + return error->byteOffset; +} + + +CCNxCodecErrorCodes +ccnxCodecError_GetErrorCode(const CCNxCodecError *error) +{ + assertNotNull(error, "Parameter must be non-null"); + return error->code; +} + +const char * +ccnxCodecError_GetFunction(const CCNxCodecError *error) +{ + assertNotNull(error, "Parameter must be non-null"); + return error->functionName; +} + +int +ccnxCodecError_GetLine(const CCNxCodecError *error) +{ + assertNotNull(error, "Parameter must be non-null"); + return error->line; +} + +const char * +ccnxCodecError_GetErrorMessage(const CCNxCodecError *error) +{ + assertNotNull(error, "Parameter must be non-null"); + return ccnxCodecErrors_ErrorMessage(error->code); +} + +const char * +ccnxCodecError_ToString(CCNxCodecError *error) +{ + assertNotNull(error, "Parameter must be non-null"); + if (error->toString) { + return error->toString; + } + + int failure = asprintf(&error->toString, "TLV error: %s:%d offset %zu: %s", + error->functionName, + error->line, + error->byteOffset, + ccnxCodecError_GetErrorMessage(error)); + assertTrue(failure > -1, "Error asprintf"); + + return error->toString; +} diff --git a/libccnx-common/ccnx/common/codec/ccnxCodec_Error.h b/libccnx-common/ccnx/common/codec/ccnxCodec_Error.h new file mode 100755 index 00000000..7204cedb --- /dev/null +++ b/libccnx-common/ccnx/common/codec/ccnxCodec_Error.h @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file ccnxCodec_Error.h + * @brief Wraps an error condition in the Tlv codec + * + * <#Detailed Description#> + * + */ +#ifndef libccnx_ccnxCodec_Error_h +#define libccnx_ccnxCodec_Error_h + +#include <stdlib.h> +#include <ccnx/common/codec/ccnxCodec_ErrorCodes.h> + +struct ccnx_codec_error; +typedef struct ccnx_codec_error CCNxCodecError; + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +const char *ccnxCodecError_ErrorMessage(CCNxCodecErrorCodes code); + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +CCNxCodecError *ccnxCodecError_Create(CCNxCodecErrorCodes code, const char *func, int line, size_t byteOffset); + +/** + * Returns a reference counted copy of the error + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return non-null A reference counted copy + * @return null An error + * + * Example: + * @code + * <#example#> + * @endcode + */ +CCNxCodecError *ccnxCodecError_Acquire(CCNxCodecError *error); + +/** + * Releases a reference count + * + * When the reference count reaches 0, the object is destroyed. + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +void ccnxCodecError_Release(CCNxCodecError **errorPtr); + +/** + * The byte offset of the error + * + * Primarily for decoding errors. It will contain the byte offset of the first byte + * of the field causing the error. For encoding, it will be the byte offer of the + * partially-encoded buffer, but the error is usually in the native format, not the + * partially encoded buffer. + * + * @param <#param1#> + * @return The byte offset of the error + * + * Example: + * @code + * <#example#> + * @endcode + */ +size_t ccnxCodecError_GetByteOffset(const CCNxCodecError *error); + + +/** + * If there was a decode error, return the error code + * + * A text message is available from <code>tlvErrors_ErrorMessage()</code>. + * + * @param <#param1#> + * @return Returns the error code, or TVL_ERR_NO_ERROR for successful decode + * + * Example: + * @code + * <#example#> + * @endcode + */ +CCNxCodecErrorCodes ccnxCodecError_GetErrorCode(const CCNxCodecError *error); + +/** + * The function where the error occured + * + * <#Discussion#> + * + * @param <#param1#> + * @return <#return#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +const char *ccnxCodecError_GetFunction(const CCNxCodecError *error); + +/** + * The line where the error occured + * + * <#Discussion#> + * + * @param <#param1#> + * @return <#return#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +int ccnxCodecError_GetLine(const CCNxCodecError *error); + +/** + * Descriptive error message + * + * <#Discussion#> + * + * @param <#param1#> + * @return A static text string + * + * Example: + * @code + * <#example#> + * @endcode + */ +const char *ccnxCodecError_GetErrorMessage(const CCNxCodecError *error); + +/** + * A string representation of the entire error + * + * <#Discussion#> + * + * @param <#param1#> + * @return An internally allocated string, do not destroy it + * + * Example: + * @code + * <#example#> + * @endcode + */ +const char *ccnxCodecError_ToString(CCNxCodecError *error); +#endif // libccnx_ccnx_TlvError_h diff --git a/libccnx-common/ccnx/common/codec/ccnxCodec_ErrorCodes.h b/libccnx-common/ccnx/common/codec/ccnxCodec_ErrorCodes.h new file mode 100755 index 00000000..938f6b20 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/ccnxCodec_ErrorCodes.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file ccnxCodec_ErrorCodes.h + * @brief The error codes used by CCNxCodecError. + * + * The codecs in schema_v0 and schema_v1 use these error codes to report problems inside + * CCNxCodec_TlvEncoder and CCNxCodec_TlvDecoder. + * + */ + +#ifndef CCNx_Common_ccnxCodec_ErrorCodes_h +#define CCNx_Common_ccnxCodec_ErrorCodes_h + +/** + * @typedef <#CCNBHeaderType#> + * @abstract <#Abstract#> + * @constant <#name#> <#description#> + * @discussion <#Discussion#> + */ +typedef enum ccnx_codec_error_codes { + TLV_ERR_NO_ERROR, + TLV_ERR_VERSION, + TLV_ERR_PACKETTYPE, + TLV_ERR_BEYOND_PACKET_END, + TLV_ERR_TOO_LONG, + TLV_ERR_NOT_FIXED_SIZE, + TLV_ERR_DUPLICATE_FIELD, + TLV_ERR_EMPTY_SPACE, + + // generic error for decoding error + TLV_ERR_DECODE, + + TLV_ERR_PACKETLENGTH_TOO_SHORT, + TLV_ERR_HEADERLENGTH_TOO_SHORT, + TLV_ERR_PACKETLENGTHSHORTER, + + + // errors for missing mandatory fields + TLV_MISSING_MANDATORY, +} CCNxCodecErrorCodes; +#endif diff --git a/libccnx-common/ccnx/common/codec/ccnxCodec_NetworkBuffer.c b/libccnx-common/ccnx/common/codec/ccnxCodec_NetworkBuffer.c new file mode 100755 index 00000000..ea1868b6 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/ccnxCodec_NetworkBuffer.c @@ -0,0 +1,809 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * We maintain a linked list of memory blocks. We fill each memory block to capacity, then + * allocate another memory block, putting it at the tail of the list. + * + * We maintain a "current" pointer to the memory block that holds "position". Insertions always go + * current block. + * + * Each memory block has a capacity and a limit. The capacity is the maximum number of bytes available. + * The limit is the furthest byte written. It will not exceed the capacity.. + * + * Once a memory block has a "next" block, the limit is fixed. One cannot shrink or expand the limit. + * When the "next" pointer is set, the capacity is shrunk to the limit and the buffer is called "frozen". + * + * (always in ABSOLUTE bytes) + * position = 4036 + * begin = 0 begin = 1536 begin = 3536 | + * | | | | + * +--------------------------+--------------------------+--------------------------+ + * | block 0 | block 1 | block 2 | + * +--------------------------+--------------------------+--------------------------+ + * | | | | + * capacity = 1536 capacity = 2000 | capacity = 2046 + * limit = 1536 limit = 2000 limit = 500 + * (always in RELATIVE bytes) + * + * Block 0 was allocated at 1536 bytes and filled to capacity before it was frozen. + * + * Block 1 was allocated at 2046 but only filled to 2000 bytes when it was frozen. The last 46 bytes + * of the block are permanently lost. + * + * Block 2 is still in use. 500 bytes have been written out of the 2046 capacity. + * + * The "begin" of a memory block is equal to the previous's memory block's "begin" plus + * the previous blocks "limit" when it is frozen. The "begin" value is absolute byte position + * and it will never change because all prior blocks must be frozen. + * + * The total "limit" of the entire chain is the tail's "begin" plus tail's "limit". + * + * + */ + +#include <config.h> +#include <stdio.h> +#include <parc/algol/parc_Memory.h> +#include <LongBow/runtime.h> + +#include <ccnx/common/codec/ccnxCodec_NetworkBuffer.h> + +struct ccnx_codec_network_buffer_memory; +typedef struct ccnx_codec_network_buffer_memory CCNxCodecNetworkBufferMemory; + +struct ccnx_codec_network_buffer_memory { + CCNxCodecNetworkBufferMemory *next; + + size_t begin; /**< Absolute position of begining */ + size_t limit; /**< Bytes used */ + size_t capacity; /**< maximum bytes available (end - begin) */ + + uint8_t *memory; +}; + +struct ccnx_codec_network_buffer_iovec { + CCNxCodecNetworkBuffer *networkBuffer; + unsigned refcount; + size_t totalBytes; + int iovcnt; + struct iovec array[]; +}; + +struct ccnx_codec_network_buffer { + size_t position; + size_t capacity; /**< Bytes allocated */ + + CCNxCodecNetworkBufferMemory *current; + CCNxCodecNetworkBufferMemory *head; + CCNxCodecNetworkBufferMemory *tail; + + void *userarg; + CCNxCodecNetworkBufferMemoryBlockFunctions memoryFunctions; + unsigned refcount; +}; + +// ================================================================================ + +#define INLINE_POSITION(block) ((uint8_t *) block + sizeof(CCNxCodecNetworkBufferMemory)) + +static CCNxCodecNetworkBufferMemory * +_ccnxCodecNetworkBufferMemory_Allocate(CCNxCodecNetworkBuffer *buffer, size_t bytes) +{ + assertNotNull(buffer->memoryFunctions.allocator, "Allocator must be non-null to allocate memory!"); + + CCNxCodecNetworkBufferMemory *block; + size_t totalAllocation = bytes + sizeof(CCNxCodecNetworkBufferMemory); + size_t actual = buffer->memoryFunctions.allocator(buffer->userarg, totalAllocation, (void **) &block); + + if (actual > sizeof(CCNxCodecNetworkBufferMemory)) { + block->next = NULL; + block->begin = 0; + block->capacity = actual - sizeof(CCNxCodecNetworkBufferMemory); + block->limit = 0; + + block->memory = INLINE_POSITION(block); + return block; + } + + // Need a de-allocator, see case 1006 + trapOutOfMemory("Wanted %zu got %zu, minimum required %zu", totalAllocation, actual, sizeof(CCNxCodecNetworkBufferMemory)); + return NULL; +} + +/** + * Wrap a user-provided buffer. It will be de-allocated with the buffer memory functions. + * + * The capacity = limit = length of the user provided memory. + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +static CCNxCodecNetworkBufferMemory * +_ccnxCodecNetworkBufferMemory_Wrap(CCNxCodecNetworkBuffer *buffer, size_t length, uint8_t memory[length]) +{ + CCNxCodecNetworkBufferMemory *block = parcMemory_AllocateAndClear(sizeof(CCNxCodecNetworkBufferMemory)); + if (block) { + block->next = NULL; + block->begin = 0; + block->capacity = length; + block->limit = length; + block->memory = memory; + + return block; + } + trapOutOfMemory("Could not allocate a CCNxCodecNetworkBufferMemory"); +} + +/** + * Releases a memory block + * + * The memory block must not be in a linked list (i.e memory->next must be NULL) + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +static void +_ccnxCodecNetworkBufferMemory_Release(CCNxCodecNetworkBuffer *buffer, CCNxCodecNetworkBufferMemory **memoryPtr) +{ + assertNotNull(memoryPtr, "Parameter must be non-null"); + assertNotNull(*memoryPtr, "Parameter must dereference to non-null"); + + CCNxCodecNetworkBufferMemory *memory = *memoryPtr; + + assertNull(memory->next, "memory->next is not null"); + + // If the memory is not in-line, free it with the deallocator + if (memory->memory == INLINE_POSITION(memory)) { + if (buffer->memoryFunctions.deallocator) { + buffer->memoryFunctions.deallocator(buffer->userarg, (void **) memoryPtr); + } + } else { + if (buffer->memoryFunctions.deallocator) { + buffer->memoryFunctions.deallocator(buffer->userarg, (void **) &memory->memory); + } + parcMemory_Deallocate((void **) &memory); + } + + + *memoryPtr = NULL; +} + +static void +_ccnxCodecNetworkBufferMemory_Display(const CCNxCodecNetworkBufferMemory *block, unsigned indent) +{ + assertNotNull(block, "Parameter block must be non-null"); + + printf("Memory block %p next %p offset %zu limit %zu capacity %zu\n", + (void *) block, (void *) block->next, block->begin, block->limit, block->capacity); + + longBowDebug_MemoryDump((const char *) block->memory, block->capacity); +} + +static bool +_ccnxCodecNetworkBufferMemory_ContainsPosition(CCNxCodecNetworkBufferMemory *memory, size_t position) +{ + return (memory->begin <= position && position < memory->begin + memory->limit); +} + +// ================================================================================ + +static void +_ccnxCodecNetworkBuffer_Expand(CCNxCodecNetworkBuffer *buffer) +{ + size_t allocationSize = 2048; + CCNxCodecNetworkBufferMemory *memory = _ccnxCodecNetworkBufferMemory_Allocate(buffer, allocationSize); + + buffer->capacity += memory->capacity; + + memory->begin = buffer->tail->begin + buffer->tail->limit; + + // this will free the tail buffer. We will drop its capacity to its limit. + buffer->tail->next = memory; + buffer->tail->capacity = buffer->tail->limit; + + buffer->tail = memory; +} + +static size_t +_ccnxCodecNetworkBuffer_RemainingCurrentBlock(CCNxCodecNetworkBuffer *buffer) +{ + size_t remaining = buffer->current->begin + buffer->current->capacity - buffer->position; + return remaining; +} + +static size_t +_ccnxCodecNetworkBuffer_BlockCount(CCNxCodecNetworkBuffer *buffer) +{ + // we should store this count for faster access + size_t count = 0; + CCNxCodecNetworkBufferMemory *block = buffer->head; + while (block) { + count++; + block = block->next; + } + return count; +} + +static void +_ccnxCodecNetworkBuffer_AllocateIfNeeded(CCNxCodecNetworkBuffer *buffer) +{ + if (buffer->position == buffer->current->begin + buffer->current->capacity) { + if (buffer->current->next) { + buffer->current = buffer->current->next; + } else { + // we are at the end of the current buffer and there's nothing beyond, + // so allocate another memory block + _ccnxCodecNetworkBuffer_Expand(buffer); + buffer->current = buffer->tail; + } + } +} + +/** + * Check if we can fit 'length' bytes in contiguous memory. + * + * If we cannot, and the remaining buffer space in the current buffer is small, freeze it out + * and allocate a new buffer. Otherwise if the difference is large, do not freeze it and the + * write will span memory blocks. + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +static void +_ccnxCodecNetworkBuffer_EnsureRemaining(CCNxCodecNetworkBuffer *buffer, size_t length) +{ + // If the current block as a next pointer, then the remaining is from + // the position to the limit. Otherwise it is from the position to + // the end. + + size_t remaining; + remaining = buffer->current->begin + buffer->current->capacity - buffer->position; + + + if (remaining < length) { + // If its a small amount of memory to waste, we'll freeze the curent buffer and + // make a new one. + if (length < 32 && buffer->current->next == NULL) { + _ccnxCodecNetworkBuffer_Expand(buffer); + buffer->current = buffer->tail; + return; + } + + // otherwise, thre is still space in the current buffer, even though it is not + // long enough. The writer will just need to span the two memory blocks. + _ccnxCodecNetworkBuffer_AllocateIfNeeded(buffer); + } +} + + +// ================================================================================ + +static size_t +_ccnxCodecNetworkBuffer_ParcMemoryAllocator(void *userarg, size_t bytes, void **output) +{ + *output = parcMemory_Allocate(bytes); + if (*output) { + return bytes; + } + return 0; +} + +static void +_ccnxCodecNetworkBuffer_ParcMemoryDeallocator(void *userarg, void **memoryPtr) +{ + void *memory = *memoryPtr; + parcMemory_Deallocate((void **) &memory); + *memoryPtr = NULL; +} + +const CCNxCodecNetworkBufferMemoryBlockFunctions ParcMemoryMemoryBlock = { + .allocator = &_ccnxCodecNetworkBuffer_ParcMemoryAllocator, + .deallocator = &_ccnxCodecNetworkBuffer_ParcMemoryDeallocator +}; + +CCNxCodecNetworkBuffer * +ccnxCodecNetworkBuffer_Allocate(const CCNxCodecNetworkBufferMemoryBlockFunctions *memoryFunctions, void *userarg) +{ + CCNxCodecNetworkBuffer *buffer = parcMemory_Allocate(sizeof(CCNxCodecNetworkBuffer)); + assertNotNull(buffer, "parcMemory_Allocate(%zu) returned NULL", sizeof(CCNxCodecNetworkBuffer)); + buffer->refcount = 1; + buffer->position = 0; + memcpy(&buffer->memoryFunctions, memoryFunctions, sizeof(CCNxCodecNetworkBufferMemoryBlockFunctions)); + buffer->userarg = userarg; + return buffer; +} + +CCNxCodecNetworkBuffer * +ccnxCodecNetworkBuffer_Create(const CCNxCodecNetworkBufferMemoryBlockFunctions *memoryFunctions, void *userarg) +{ + CCNxCodecNetworkBuffer *buffer = ccnxCodecNetworkBuffer_Allocate(memoryFunctions, userarg); + + buffer->head = _ccnxCodecNetworkBufferMemory_Allocate(buffer, 1536); + buffer->tail = buffer->head; + buffer->current = buffer->head; + buffer->capacity = buffer->head->capacity; + + return buffer; +} + +CCNxCodecNetworkBuffer * +ccnxCodecNetworkBuffer_CreateFromArray(const CCNxCodecNetworkBufferMemoryBlockFunctions *memoryFunctions, void *userarg, size_t length, uint8_t memory[length]) +{ + CCNxCodecNetworkBuffer *buffer = ccnxCodecNetworkBuffer_Allocate(memoryFunctions, userarg); + + buffer->head = _ccnxCodecNetworkBufferMemory_Wrap(buffer, length, memory); + buffer->tail = buffer->head; + buffer->current = buffer->head; + buffer->capacity = buffer->head->capacity; + + return buffer; +} + + + +CCNxCodecNetworkBuffer * +ccnxCodecNetworkBuffer_Acquire(CCNxCodecNetworkBuffer *original) +{ + assertNotNull(original, "Parameter must be non-null"); + assertTrue(original->refcount > 0, "Refcount must be positive, got 0"); + + original->refcount++; + return original; +} + +void +ccnxCodecNetworkBuffer_Release(CCNxCodecNetworkBuffer **bufferPtr) +{ + assertNotNull(bufferPtr, "Parameter must be non-null"); + assertNotNull(*bufferPtr, "Parameter must dereference to non-null"); + + CCNxCodecNetworkBuffer *buffer = *bufferPtr; + assertTrue(buffer->refcount > 0, "refcount must be positive"); + + buffer->refcount--; + if (buffer->refcount == 0) { + while (buffer->head) { + CCNxCodecNetworkBufferMemory *next = buffer->head->next; + buffer->head->next = NULL; + _ccnxCodecNetworkBufferMemory_Release(buffer, &buffer->head); + buffer->head = next; + } + parcMemory_Deallocate((void **) &buffer); + } + *bufferPtr = NULL; +} + +// ================================================================================ + +static inline size_t +_ccnxCodecNetworkBuffer_Limit(const CCNxCodecNetworkBuffer *buffer) +{ + return buffer->tail->begin + buffer->tail->limit; +} + +size_t +ccnxCodecNetworkBuffer_Position(const CCNxCodecNetworkBuffer *buffer) +{ + assertNotNull(buffer, "Parameter must be non-null"); + return buffer->position; +} + +size_t +ccnxCodecNetworkBuffer_Limit(const CCNxCodecNetworkBuffer *buffer) +{ + assertNotNull(buffer, "Parameter must be non-null"); + return _ccnxCodecNetworkBuffer_Limit(buffer); +} + +void +ccnxCodecNetworkBuffer_SetPosition(CCNxCodecNetworkBuffer *buffer, size_t position) +{ + assertNotNull(buffer, "Parameter buffer must be non-null"); + assertTrue(position <= _ccnxCodecNetworkBuffer_Limit(buffer), "Position must not exceed limit, got %zu limit %zu", + position, _ccnxCodecNetworkBuffer_Limit(buffer)); + + // We allow the position to be set to the end (just past the last written byte) of the buffer. + // This is the "next" position to be written + if (position == _ccnxCodecNetworkBuffer_Limit(buffer)) { + buffer->current = buffer->tail; + } else { + // Is the new position within the current memory block? + if (_ccnxCodecNetworkBufferMemory_ContainsPosition(buffer->current, position)) { + // we're ok, new position is in this buffer, we're done :) + } else { + // we need to find the right buffer + CCNxCodecNetworkBufferMemory *memory = buffer->head; + while (!_ccnxCodecNetworkBufferMemory_ContainsPosition(memory, position)) { + memory = memory->next; + assertNotNull(memory, "Illegal state: position < buffer->limit, but we ran off end of linked list"); + } + + buffer->current = memory; + } + } + + buffer->position = position; +} + +void +ccnxCodecNetworkBuffer_Finalize(CCNxCodecNetworkBuffer *buffer) +{ + assertNotNull(buffer, "Parameter buffer must be non-null"); + + // if we're at the limit, we're done + if (buffer->position < _ccnxCodecNetworkBuffer_Limit(buffer)) { + // begin at the tail and free memory blocks until we've found the current position + size_t position = buffer->position; + + // Is the new position within the current memory block? + if (_ccnxCodecNetworkBufferMemory_ContainsPosition(buffer->current, position)) { + // we're ok, new position is in this buffer, we're done :) + } else { + // we need to find the right buffer + CCNxCodecNetworkBufferMemory *memory = buffer->head; + while (!_ccnxCodecNetworkBufferMemory_ContainsPosition(memory, position)) { + memory = memory->next; + assertNotNull(memory, "Illegal state: position < buffer->limit, but we ran off end of linked list"); + } + + buffer->current = memory; + } + + // discard any memory blocks after this + + CCNxCodecNetworkBufferMemory *current = buffer->current->next; + while (current) { + CCNxCodecNetworkBufferMemory *next = current->next; + + // this is a requirement for _Release to not throw an assertion + current->next = NULL; + _ccnxCodecNetworkBufferMemory_Release(buffer, ¤t); + current = next; + } + + // Set the limit of the current block so buffer->position is the end + buffer->current->next = NULL; + size_t relativePosition = buffer->position - buffer->current->begin; + buffer->current->limit = relativePosition; + buffer->tail = buffer->current; + } +} + +static inline void +_ccnxCodecNetworkBuffer_PutUint8(CCNxCodecNetworkBuffer *buffer, uint8_t value) +{ + _ccnxCodecNetworkBuffer_AllocateIfNeeded(buffer); + + size_t relativePosition = buffer->position - buffer->current->begin; + buffer->current->memory[relativePosition++] = value; + if (relativePosition > buffer->current->limit) { + buffer->current->limit = relativePosition; + } + + buffer->position++; +} + +void +ccnxCodecNetworkBuffer_PutUint8(CCNxCodecNetworkBuffer *buffer, uint8_t value) +{ + assertNotNull(buffer, "Parameter buffer must be non-null"); + _ccnxCodecNetworkBuffer_PutUint8(buffer, value); +} + +void +ccnxCodecNetworkBuffer_PutUint16(CCNxCodecNetworkBuffer *buffer, uint16_t value) +{ + _ccnxCodecNetworkBuffer_EnsureRemaining(buffer, 2); + + _ccnxCodecNetworkBuffer_PutUint8(buffer, value >> 8); + _ccnxCodecNetworkBuffer_PutUint8(buffer, value & 0xFF); +} + +void +ccnxCodecNetworkBuffer_PutUint32(CCNxCodecNetworkBuffer *buffer, uint32_t value) +{ + _ccnxCodecNetworkBuffer_EnsureRemaining(buffer, 4); + + for (int i = sizeof(uint32_t) - 1; i > 0; i--) { + uint8_t b = value >> (i * 8) & 0xFF; + _ccnxCodecNetworkBuffer_PutUint8(buffer, b); + } + + _ccnxCodecNetworkBuffer_PutUint8(buffer, value & 0xFF); +} + +void +ccnxCodecNetworkBuffer_PutUint64(CCNxCodecNetworkBuffer *buffer, uint64_t value) +{ + _ccnxCodecNetworkBuffer_EnsureRemaining(buffer, 8); + + for (int i = sizeof(uint64_t) - 1; i > 0; i--) { + uint8_t b = value >> (i * 8) & 0xFF; + _ccnxCodecNetworkBuffer_PutUint8(buffer, b); + } + + _ccnxCodecNetworkBuffer_PutUint8(buffer, value & 0xFF); +} + +void +ccnxCodecNetworkBuffer_PutArray(CCNxCodecNetworkBuffer *buffer, size_t length, const uint8_t array[length]) +{ + _ccnxCodecNetworkBuffer_AllocateIfNeeded(buffer); + + size_t offset = 0; + while (offset < length) { + size_t available = _ccnxCodecNetworkBuffer_RemainingCurrentBlock(buffer); + if (available == 0) { + _ccnxCodecNetworkBuffer_AllocateIfNeeded(buffer); + } else { + if (available > (length - offset)) { + available = length - offset; + } + + size_t relativePosition = buffer->position - buffer->current->begin; + void *dest = &buffer->current->memory[relativePosition]; + const void *src = &array[offset]; + memcpy(dest, src, available); + + relativePosition += available; + if (relativePosition > buffer->current->limit) { + buffer->current->limit = relativePosition; + } + + buffer->position += available; + offset += available; + } + } +} + +void +ccnxCodecNetworkBuffer_PutBuffer(CCNxCodecNetworkBuffer *buffer, PARCBuffer *value) +{ + size_t length = parcBuffer_Remaining(value); + if (length > 0) { + void *ptr = parcBuffer_Overlay(value, 0); + ccnxCodecNetworkBuffer_PutArray(buffer, length, ptr); + } +} + +PARCBuffer * +ccnxCodecNetworkBuffer_CreateParcBuffer(CCNxCodecNetworkBuffer *buffer) +{ + // We don't have the idea of Flip here yet, so we go from 0 .. position + + size_t length = _ccnxCodecNetworkBuffer_Limit(buffer); + PARCBuffer *output = parcBuffer_Allocate(length); + CCNxCodecNetworkBufferMemory *block = buffer->head; + while (block) { + size_t available = (length > block->limit) ? block->limit : length; + if (available > 0) { + parcBuffer_PutArray(output, available, block->memory); + } + length -= available; + block = block->next; + } + parcBuffer_Flip(output); + return output; +} + +PARCSignature * +ccnxCodecNetworkBuffer_ComputeSignature(CCNxCodecNetworkBuffer *buffer, size_t start, size_t end, PARCSigner *signer) +{ + // Most positions (start, end, position, roof) below are in **absolute** coordinates + // The position relativePosition is relative to the memory block start + assertNotNull(buffer, "Parameter buffer must be non-null"); + assertTrue(end >= start, "End is less than start: start %zu end %zu", start, end); + + PARCSignature *signature = NULL; + if (signer) { + // compute the signature over the specified area + + PARCCryptoHasher *hasher = parcSigner_GetCryptoHasher(signer); + parcCryptoHasher_Init(hasher); + + size_t position = start; + CCNxCodecNetworkBufferMemory *block = buffer->head; + while (block && position < block->begin + block->limit) { + if (_ccnxCodecNetworkBufferMemory_ContainsPosition(block, position)) { + // determine if we're going all the way to the block's end or are we + // stopping early because that's the end of the designated area + size_t roof = (end > block->begin + block->limit) ? block->limit : end; + size_t length = roof - position; + + // now calculate the relative offset in the block so we can update the hash + size_t relativePosition = position - block->begin; + + parcCryptoHasher_UpdateBytes(hasher, &block->memory[relativePosition], length); + + position += length; + } + + block = block->next; + } + + PARCCryptoHash *hash = parcCryptoHasher_Finalize(hasher); + + signature = parcSigner_SignDigest(signer, hash); + parcCryptoHash_Release(&hash); + } + + return signature; +} + +uint8_t +ccnxCodecNetworkBuffer_GetUint8(const CCNxCodecNetworkBuffer *netbuff, size_t position) +{ + assertNotNull(netbuff, "Parameter buffer must be non-null"); + assertTrue(position < _ccnxCodecNetworkBuffer_Limit(netbuff), "Position %zu beyond limit %zu", position, _ccnxCodecNetworkBuffer_Limit(netbuff)); + + CCNxCodecNetworkBufferMemory *block = netbuff->head; + while (block && !_ccnxCodecNetworkBufferMemory_ContainsPosition(block, position)) { + block = block->next; + } + + trapUnexpectedStateIf(block == NULL, + "Could not find position %zu that is less than limit %zu", + position, _ccnxCodecNetworkBuffer_Limit(netbuff)); + + size_t relativeOffset = position - block->begin; + return block->memory[relativeOffset]; +} + +void +ccnxCodecNetworkBuffer_Display(const CCNxCodecNetworkBuffer *netbuff, unsigned indent) +{ + printf("CCNxCodecNetworkBuffer %p head %p current %p tail %p\n", + (void *) netbuff, (void *) netbuff->head, (void *) netbuff->current, (void *) netbuff->tail); + printf(" position %zu limit %zu capacity %zu refcount %u userarg %p\n", + netbuff->position, _ccnxCodecNetworkBuffer_Limit(netbuff), netbuff->capacity, netbuff->refcount, netbuff->userarg); + + CCNxCodecNetworkBufferMemory *block = netbuff->head; + while (block) { + _ccnxCodecNetworkBufferMemory_Display(block, 6); + block = block->next; + } +} + +// ====================================================================================== + + +CCNxCodecNetworkBufferIoVec * +ccnxCodecNetworkBuffer_CreateIoVec(CCNxCodecNetworkBuffer *buffer) +{ + size_t blockCount = _ccnxCodecNetworkBuffer_BlockCount(buffer); + size_t allocationSize = sizeof(CCNxCodecNetworkBufferIoVec) + sizeof(struct iovec) * blockCount; + + CCNxCodecNetworkBufferIoVec *vec = parcMemory_Allocate(allocationSize); + assertNotNull(vec, "parcMemory_Allocate(%zu) returned NULL", allocationSize); + vec->refcount = 1; + vec->networkBuffer = ccnxCodecNetworkBuffer_Acquire(buffer); + vec->iovcnt = (int) blockCount; + vec->totalBytes = 0; + + CCNxCodecNetworkBufferMemory *block = buffer->head; + for (int i = 0; i < vec->iovcnt; i++) { + vec->array[i].iov_base = block->memory; + vec->array[i].iov_len = block->limit; + vec->totalBytes += block->limit; + block = block->next; + } + + return vec; +} + +CCNxCodecNetworkBufferIoVec * +ccnxCodecNetworkBufferIoVec_Acquire(CCNxCodecNetworkBufferIoVec *vec) +{ + assertNotNull(vec, "Parameter vec must be non-null"); + assertTrue(vec->refcount > 0, "Existing reference count is 0"); + vec->refcount++; + return vec; +} + +void +ccnxCodecNetworkBufferIoVec_Release(CCNxCodecNetworkBufferIoVec **vecPtr) +{ + assertNotNull(vecPtr, "Parameter must be non-null"); + assertNotNull(*vecPtr, "Parameter must dereference to non-null"); + CCNxCodecNetworkBufferIoVec *vec = *vecPtr; + assertTrue(vec->refcount > 0, "object has 0 refcount!"); + + vec->refcount--; + if (vec->refcount == 0) { + ccnxCodecNetworkBuffer_Release(&vec->networkBuffer); + parcMemory_Deallocate((void **) &vec); + } + *vecPtr = NULL; +} + +int +ccnxCodecNetworkBufferIoVec_GetCount(CCNxCodecNetworkBufferIoVec *vec) +{ + assertNotNull(vec, "Parameter vec must be non-null"); + return vec->iovcnt; +} + +const struct iovec * +ccnxCodecNetworkBufferIoVec_GetArray(CCNxCodecNetworkBufferIoVec *vec) +{ + assertNotNull(vec, "Parameter vec must be non-null"); + return vec->array; +} + +void +ccnxCodecNetworkBufferIoVec_Display(const CCNxCodecNetworkBufferIoVec *vec, int indent) +{ + printf("\nCCNxCodecNetworkBufferIoVec %p refcount %u totalBytes %zu iovcnt %d NetworkBuffer %p\n", + (void *) vec, vec->refcount, vec->totalBytes, vec->iovcnt, (void *) vec->networkBuffer); + + size_t total = 0; + for (int i = 0; i < vec->iovcnt; i++) { + total += vec->array[i].iov_len; + int nwritten = printf(" vec %3d base %p length %5zu total %5zu\n", i, (void *) vec->array[i].iov_base, vec->array[i].iov_len, total); + assertTrue(nwritten >= 0, "Error calling printf"); + longBowDebug_MemoryDump(vec->array[i].iov_base, vec->array[i].iov_len); + } +} + +size_t +ccnxCodecNetworkBufferIoVec_Length(const CCNxCodecNetworkBufferIoVec *vec) +{ + assertNotNull(vec, "Parameter vec must be non-null"); + return vec->totalBytes; +} + +bool +ccnxCodecNetworkBufferIoVec_Equals(const CCNxCodecNetworkBufferIoVec *a, const CCNxCodecNetworkBufferIoVec *b) +{ + if (a == NULL && b == NULL) { + return true; + } + + if (a == NULL || b == NULL) { + return false; + } + + // both are non-null + bool equals = false; + if (a->totalBytes == b->totalBytes) { + PARCBuffer *abuffer = ccnxCodecNetworkBuffer_CreateParcBuffer(a->networkBuffer); + PARCBuffer *bbuffer = ccnxCodecNetworkBuffer_CreateParcBuffer(b->networkBuffer); + + equals = parcBuffer_Equals(abuffer, bbuffer); + + parcBuffer_Release(&abuffer); + parcBuffer_Release(&bbuffer); + } + return equals; +} diff --git a/libccnx-common/ccnx/common/codec/ccnxCodec_NetworkBuffer.h b/libccnx-common/ccnx/common/codec/ccnxCodec_NetworkBuffer.h new file mode 100644 index 00000000..3ac1fffd --- /dev/null +++ b/libccnx-common/ccnx/common/codec/ccnxCodec_NetworkBuffer.h @@ -0,0 +1,833 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file codec/ccnxCodec_NetworkBuffer.h + * @brief A network buffer represents memory used for network I/O + * + * A network buffer represents memory used for network I/O and may be scatter/gather non-contiguous memory or + * may be made up for special memory regions, such as DMA memory directly from the kernel. + * + * The general usage pattern of this is to create the network buffer, fill it in with the encoded packet, then + * create a CCNxCodecNetworkBufferIoVec from it. The IoVec is then used in a gathering write. Calling ccnxCodecNetworkBuffer_CreateIoVec() + * will create a CCNxCodecNetworkBufferIoVec object that has a reference to the original network buffer and will release it when the + * IoVec is released. A user can get a normal system "struct iovec" from the CCNxCodecNetworkBufferIoVec. + * + * The CCNxCodecNetworkBufferIoVec is a read-only object. + * + * A network buffer uses a CCNxCodecNetworkBufferMemoryBlockFunctions structure for an allocator and de-allocator. The allocator is called + * to add more memory to the scatter/gather list of memory buffers and the de-allocator is used to return those + * buffers to the owner. A user could point to "ParcMemoryMemoryBlock" to use the normal parcMemory_allocate() and + * parcMemory_deallocate() functions. Or, they can use their own or wrap event buffers or wrap kernel memory blocks. + * + * The user can address the memory using a linearized position with ccnxCodecNetworkBuffer_Position() and ccnxCodecNetworkBuffer_SetPosition(). + * If a write would span two (or more) memory blocks, the write function will correctly split the write. + * + * When doing a write that would span two memory blocks, the network buffer may choose to truncate the current block and do an + * unsplit write to the second block. It will only do this if it would result in a small amount of wasted memory. This can only + * be done on the first pass through a memory block (if you set the position backwards and do a write that splits over memory blocks, + * the write must be split). + * + * Add a control to turn off the "optimized" write splitting (i.e. the behavior to truncate the current block and do an unsplit + * write to the next block). + * + * ccnxCodecNetworkBuffer_ComputeSignature should be factored out of here, like the verifier is factored out + * + */ +#ifndef Libccnx_codec_ccnxCodecNetworkBuffer_h +#define Libccnx_codec_ccnxCodecNetworkBuffer_h + +#include <stdint.h> +#include <sys/uio.h> +#include <parc/algol/parc_Buffer.h> +#include <parc/security/parc_Signer.h> + +struct ccnx_codec_network_buffer; +/** + * @typedef CCNxCodecNetworkBuffer + * @brief A network buffer represents memory used for network I/O. + */ +typedef struct ccnx_codec_network_buffer CCNxCodecNetworkBuffer; + +struct ccnx_codec_network_buffer_iovec; +/** + * @typedef CCNxCodecNetworkBufferIoVec + * @brief Contains a sequence of buffers to read in which the data to be read is stored. + */ +typedef struct ccnx_codec_network_buffer_iovec CCNxCodecNetworkBufferIoVec; + +/** + * @typedef CCNxCodecNetworkBufferMemoryBlockFunctions + * @brief Structure and functions for MemoryBlocks + */ + +typedef struct ccnx_codec_network_buffer_memory_block_struct { + /** + * Allocate a block of memory at least 'bytes' long. + * + * @param [in] userarg Closure, may be null. + * @param [in] bytes The requested number of bytes. + * @param [out] output The buffer to set. + * + * @return The number of bytes granted in output. + */ + size_t (*allocator)(void *userarg, size_t bytes, void **output); + + /** + * Returns (frees) a memory block. + * + * @param [in] userarg Closure, may be null. + * @param [out] memoryPtr The memory pointer to dellocate and NULLify. + */ + void (*deallocator)(void *userarg, void **memoryPtr); +} CCNxCodecNetworkBufferMemoryBlockFunctions; + +extern const CCNxCodecNetworkBufferMemoryBlockFunctions ParcMemoryMemoryBlock; + +/** + * Creates a `CCNxCodecNetworkBuffer`. + * + * The first memory block is allocated using the default settings. The parameter "userarg" will be passed + * to the CCNxCodecNetworkBufferMemoryBlockFunctions for allocations and de-allocations. + * + * @param [in] blockFunctions The allocator/de-allocator to use. + * @param [in] userarg Passed to all calls to the blockFunctions, may be NULL. + * + * @return non-null An allocated memory block using memory from blockFunctions. + * @return null An error + * + * Example: + * @code + * { + * CCNxCodecNetworkBuffer * netbuffer = ccnxCodecNetworkBuffer_Create(&ParcMemoryMemoryBlock, NULL); + * } + * @endcode + */ +CCNxCodecNetworkBuffer *ccnxCodecNetworkBuffer_Create(const CCNxCodecNetworkBufferMemoryBlockFunctions *blockFunctions, void *userarg); + +/** + * Create a `CCNxCodecNetworkBuffer` from a buffer block. + * + * The first memory block of the Network Buffer will wrap the user provided memory. + * + * If the allocator is non-null then the user could append more memory blocks. + * + * The deallocator in the blockFunctions will be called on the memory when done. The userarg will + * be passed to the CCNxCodecNetworkBufferMemoryBlockFunctions. + * + * @param [in] blockFunctions The allocator/de-allocator to use. + * @param [in] userarg Passed to all calls to the blockFunctions, may be NULL. + * @param [in] length The length of the user-provided memory. + * @param [in] memory The user-provided memory. + * + * @return non-null An allocated memory block that wraps the user-provided memory. + * @return null An error occurred. + * + * Example: + * @code + * { + * uint8_t *packet_buffer; + * packet_buffer = parcMemory_Allocate(msg_length); + * read(fd, packet_buffer, msg_length); + * CCNxCodecNetworkBuffer *netbuff = ccnxCodecNetworkBuffer_CreateFromArray(&ParcMemoryMemoryBlock, NULL, msg_length, packet_buffer); + * } + * @endcode + */ +CCNxCodecNetworkBuffer *ccnxCodecNetworkBuffer_CreateFromArray(const CCNxCodecNetworkBufferMemoryBlockFunctions *blockFunctions, void *userarg, size_t length, uint8_t *memory); + +/** + * Increase the number of references to a `CCNxCodecNetworkBuffer`. + * + * Note that new `CCNxCodecNetworkBuffer` is not created, + * only that the given `CCNxCodecNetworkBuffer` reference count is incremented. + * Discard the reference by invoking `ccnxCodecNetworkBuffer_Release`. + * + * @param [in] original A pointer to a `CCNxCodecNetworkBuffer` instance. + * + * @return The input `CCNxCodecNetworkBuffer` pointer. + * + * Example: + * @code + * { + * uint8_t *packet_buffer; + * packet_buffer = parcMemory_Allocate(msg_length); + * read(fd, packet_buffer, msg_length); + * CCNxCodecNetworkBuffer *netbuff = ccnxCodecNetworkBuffer_CreateFromArray(&ParcMemoryMemoryBlock, NULL, msg_length, packet_buffer); + * + * CCNxCodecNetworkBuffer *handle = ccnxCodecNetworkBuffer_Acquire(netbuff); + * + * ... + * + * ccnxCodecNetworkBuffer_Release(&handle); + * ccnxCodecNetworkBuffer_Release(&netbuff); + * } + * @endcode + */ +CCNxCodecNetworkBuffer *ccnxCodecNetworkBuffer_Acquire(CCNxCodecNetworkBuffer *original); + +/** + * Release a previously acquired reference to the specified instance, + * decrementing the reference count for the instance. + * + * The pointer to the instance is set to NULL as a side-effect of this function. + * + * If the invocation causes the last reference to the instance to be released, + * the instance is deallocated and the instance's implementation will perform + * additional cleanup and release other privately held references. + * + * @param [in,out] bufferPtr A pointer to a pointer to the instance to release. + * + * + * Example: + * @code + * { + * uint8_t *packet_buffer; + * packet_buffer = parcMemory_Allocate(msg_length); + * read(fd, packet_buffer, msg_length); + * CCNxCodecNetworkBuffer *netbuff = ccnxCodecNetworkBuffer_CreateFromArray(&ParcMemoryMemoryBlock, NULL, msg_length, packet_buffer); + * + * CCNxCodecNetworkBuffer *handle = ccnxCodecNetworkBuffer_Acquire(netbuff); + * + * ... + * + * ccnxCodecNetworkBuffer_Release(&handle); + * ccnxCodecNetworkBuffer_Release(&netbuff); + * } + * @endcode + */ +void ccnxCodecNetworkBuffer_Release(CCNxCodecNetworkBuffer **bufferPtr); + +/** + * Returns the linearlized cursor position in the buffer. + * + * Returns the current cursor position in linearized memory location (this does not + * actually linearize the memory). + * + * @param [in] buffer An allocated network buffer. + * + * @return The linearized memory position (bytes). + * + * Example: + * @code + * { + * uint8_t *packet_buffer; + * packet_buffer = parcMemory_Allocate(msg_length); + * read(fd, packet_buffer, msg_length); + * CCNxCodecNetworkBuffer *netbuff = ccnxCodecNetworkBuffer_CreateFromArray(&ParcMemoryMemoryBlock, NULL, msg_length, packet_buffer); + * + * size_t position = ccnxCodecNetworkBuffer_Position(netbuff); + * // position is 0 since nothing has been written yet + * } + * @endcode + */ +size_t ccnxCodecNetworkBuffer_Position(const CCNxCodecNetworkBuffer *buffer); + +/** + * Returns the maximum position to which is written. + * + * The maximum position of the currently written memory, as if it were linear memory. The limit + * will be "0" if no data has been written. + * + * @param [in] buffer An allocated network buffer. + * + * @return The lineaized capacity (bytes). + * + * Example: + * @code + * { + * uint8_t *packet_buffer; + * packet_buffer = parcMemory_Allocate(msg_length); + * read(fd, packet_buffer, msg_length); + * CCNxCodecNetworkBuffer *netbuff = ccnxCodecNetworkBuffer_CreateFromArray(&ParcMemoryMemoryBlock, NULL, msg_length, packet_buffer); + * + * size_t limit = ccnxCodecNetworkBuffer_Limit(netbuff); + * // limit is msg_length since the buffer was created as a wrapper for the packet_buffer + * } + * @endcode + */ +size_t ccnxCodecNetworkBuffer_Limit(const CCNxCodecNetworkBuffer *buffer); + +/** + * Sets the cursor position to the linearized memory location. + * + * Sets the cursor to the linearized memory location. It must not exceed {@link ccnxCodecNetworkBuffer_Limit}(). + * + * @param [in,out] buffer An allocated `CCNxCodecNetworkBuffer`. + * @param [in] position The linearized buffer position. + * + * Example: + * @code + * { + * uint8_t *packet_buffer; + * packet_buffer = parcMemory_Allocate(msg_length); + * read(fd, packet_buffer, msg_length); + * CCNxCodecNetworkBuffer *netbuff = ccnxCodecNetworkBuffer_CreateFromArray(&ParcMemoryMemoryBlock, NULL, msg_length, packet_buffer); + * + * ccnxCodecNetworkBuffer_SetPosition(netbuff, 1); + * // the position is now 1, instead of 0 + * } + * @endcode + */ +void ccnxCodecNetworkBuffer_SetPosition(CCNxCodecNetworkBuffer *buffer, size_t position); + +/** + * Sets the buffer limit to the current position. Throws away anything after. + * + * The Limit will be set to the current position. Any bytes left after the new Limit are discarded + * and un-recoverable. This should be done after finishing writing to the buffer, especially if + * the buffer was backed up to discard or overwrite previous data. + * + * @param [in,out] buffer An allocated `CCNxCodecNetworkBuffer`. + * + * Example: + * @code + * { + * uint8_t *packet_buffer; + * packet_buffer = parcMemory_Allocate(msg_length); + * read(fd, packet_buffer, msg_length); + * CCNxCodecNetworkBuffer *netbuff = ccnxCodecNetworkBuffer_CreateFromArray(&ParcMemoryMemoryBlock, NULL, msg_length, packet_buffer); + * + * size_t newPosition = ccnxCodecNetworkBuffer_Position(netbuff) + sizeof(uint16_t); + * ccnxCodecNetworkBuffer_PutUint16(netbuff, 0x1234); + * ccnxCodecNetworkBuffer_SetPosition(netbuff, 1); + * ccnxCodecNetworkBuffer_Finalize(netbuff); + * // the buffer is now only '0x12' and the limit is reduced to 1. + * } + * @endcode + */ +void ccnxCodecNetworkBuffer_Finalize(CCNxCodecNetworkBuffer *buffer); + +/** + * Writes a `uint8_t` to the current cursor position, allocating as necessary + * + * Writes to the current cursor position. If the cursor is at the end of a memory block, + * a new memory block will be allocated. Will assert if cannot allocate more memory (or if the allocator is null). + * + * @param [in,out] buffer An allocated `CCNxCodecNetworkBuffer`. + * @param [in] value The value to write. + * + * Example: + * @code + * { + * uint8_t *packet_buffer; + * packet_buffer = parcMemory_Allocate(msg_length); + * read(fd, packet_buffer, msg_length); + * CCNxCodecNetworkBuffer *netbuff = ccnxCodecNetworkBuffer_CreateFromArray(&ParcMemoryMemoryBlock, NULL, msg_length, packet_buffer); + * + * size_t newPosition = ccnxCodecNetworkBuffer_Position(netbuff) + sizeof(uint8_t); + * ccnxCodecNetworkBuffer_PutUint8(netbuff, 0x12); + * + * size_t position = ccnxCodecNetworkBuffer_Position(netbuff); + * // position will equal newPosition + * } + * @endcode + */ +void ccnxCodecNetworkBuffer_PutUint8(CCNxCodecNetworkBuffer *buffer, uint8_t value); + +/** + * Writes a `uint16_t` to the current cursor position, allocating as necessary. + * + * Writes to the current cursor position. If the cursor is at the end of a memory block, + * a new memory block will be allocated. Will assert if cannot allocate more memory (or if the allocator is null). + * The value is written in network byte order. + * + * @param [in,out] buffer An allocated `CCNxCodecNetworkBuffer`. + * @param [in] value The value to write. + * + * Example: + * @code + * { + * uint8_t *packet_buffer; + * packet_buffer = parcMemory_Allocate(msg_length); + * read(fd, packet_buffer, msg_length); + * CCNxCodecNetworkBuffer *netbuff = ccnxCodecNetworkBuffer_CreateFromArray(&ParcMemoryMemoryBlock, NULL, msg_length, packet_buffer); + * + * size_t newPosition = ccnxCodecNetworkBuffer_Position(netbuff) + sizeof(uint16_t); + * ccnxCodecNetworkBuffer_PutUint16(netbuff, 0x1234); + * + * size_t position = ccnxCodecNetworkBuffer_Position(netbuff); + * // position will equal newPosition + * } + * @endcode + */ +void ccnxCodecNetworkBuffer_PutUint16(CCNxCodecNetworkBuffer *buffer, uint16_t value); + +/** + * Writes a `uint32_t` to the current cursor position, allocating as necessary. + * + * Writes to the current cursor position. If the cursor is at the end of a memory block, + * a new memory block will be allocated. Will assert if cannot allocate more memory (or if the allocator is null). + * The value is written in network byte order. + * + * @param [in,out] buffer An allocated `CCNxCodecNetworkBuffer`. + * @param [in] value The value to write. + * + * Example: + * @code + * { + * uint8_t *packet_buffer; + * packet_buffer = parcMemory_Allocate(msg_length); + * read(fd, packet_buffer, msg_length); + * CCNxCodecNetworkBuffer *netbuff = ccnxCodecNetworkBuffer_CreateFromArray(&ParcMemoryMemoryBlock, NULL, msg_length, packet_buffer); + * + * size_t newPosition = ccnxCodecNetworkBuffer_Position(netbuff) + sizeof(uint32_t); + * ccnxCodecNetworkBuffer_PutUint32(netbuff, 0x12345678); + * + * size_t position = ccnxCodecNetworkBuffer_Position(netbuff); + * // position will equal newPosition + * } + * @endcode + */ +void ccnxCodecNetworkBuffer_PutUint32(CCNxCodecNetworkBuffer *buffer, uint32_t value); + +/** + * Writes a `uint64_t` to the current cursor position, allocating as necessary + * + * Writes to the current cursor position. If the cursor is at the end of a memory block, + * a new memory block will be allocated. Will assert if cannot allocate more memory (or if the allocator is null). + * The value is written in network byte order. + * + * @param [in,out] buffer An allocated `CCNxCodecNetworkBuffer`. + * @param [in] value The value to write. + * + * Example: + * @code + * { + * uint8_t *packet_buffer; + * packet_buffer = parcMemory_Allocate(msg_length); + * read(fd, packet_buffer, msg_length); + * CCNxCodecNetworkBuffer *netbuff = ccnxCodecNetworkBuffer_CreateFromArray(&ParcMemoryMemoryBlock, NULL, msg_length, packet_buffer); + * + * size_t newPosition = ccnxCodecNetworkBuffer_Position(netbuff) + sizeof(uint64_t); + * ccnxCodecNetworkBuffer_PutUint64(netbuff, 0x1234567812345678); + * + * size_t position = ccnxCodecNetworkBuffer_Position(netbuff); + * // position will equal newPosition + * } + * @endcode + */ +void ccnxCodecNetworkBuffer_PutUint64(CCNxCodecNetworkBuffer *buffer, uint64_t value); + +/** + * Writes an array to the current cursor position, allocating as necessary + * + * Writes to the current cursor position. If the cursor is at the end of a memory block, + * a new memory block will be allocated. Will assert if cannot allocate more memory (or if the allocator is null). + * The value is written in array order. + * + * @param [in,out] buffer An allocated `CCNxCodecNetworkBuffer`. + * @param [in] length AThe length of the array to write. + * @param [in] array The value to write. + * + * Example: + * @code + * { + * uint8_t *packet_buffer; + * packet_buffer = parcMemory_Allocate(msg_length); + * read(fd, packet_buffer, msg_length); + * CCNxCodecNetworkBuffer *netbuff = ccnxCodecNetworkBuffer_CreateFromArray(&ParcMemoryMemoryBlock, NULL, msg_length, packet_buffer); + * + * uint8_t array[5] = {1,2,3,4,5}; + * size_t newPosition = ccnxCodecNetworkBuffer_Position(netbuff) + 5; + * ccnxCodecNetworkBuffer_PutArray(netbuff, array, 5); + * + * size_t position = ccnxCodecNetworkBuffer_Position(netbuff); + * // position will equal newPosition + * } + * @endcode + */ +void ccnxCodecNetworkBuffer_PutArray(CCNxCodecNetworkBuffer *buffer, size_t length, const uint8_t *array); + +/** + * Writes to the current cursor position, allocating as necessary + * + * Writes to the current cursor position. If the cursor is at the end of a memory block, + * a new memory block will be allocated. Will assert if cannot allocate more memory (or if the allocator is null). + * The value is written in buffer order. + * + * @param [in,out] buffer An allocated `CCNxCodecNetworkBuffer`. + * @param [in] value The value to write. + * + * Example: + * @code + * { + * uint8_t *packet_buffer; + * packet_buffer = parcMemory_Allocate(msg_length); + * read(fd, packet_buffer, msg_length); + * CCNxCodecNetworkBuffer *netbuff = ccnxCodecNetworkBuffer_CreateFromArray(&ParcMemoryMemoryBlock, NULL, msg_length, packet_buffer); + * + * uint8_t array[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + * PARCBuffer *buffer = parcBuffer_Wrap(array, 10, 0, 10); + * size_t newPosition = ccnxCodecNetworkBuffer_Position(netbuff) + 10; + * ccnxCodecNetworkBuffer_PutBuffer(netbuff, buffer); + * + * size_t position = ccnxCodecNetworkBuffer_Position(netbuff); + * // position will equal newPosition + * } + * @endcode + */ +void ccnxCodecNetworkBuffer_PutBuffer(CCNxCodecNetworkBuffer *buffer, PARCBuffer *value); + +/** + * Creates a linearized memory buffer. + * + * Allocates a single buffer and copies the `CCNxCodecNetworkBuffer` to it. + * + * @param [in] buffer An allocated `CCNxCodecNetworkBuffer`. + * + * @return non-null A copy of the network buffer's written contents. + * @return null An error. + * + * Example: + * @code + * { + * uint8_t *packet_buffer; + * packet_buffer = parcMemory_Allocate(msg_length); + * read(fd, packet_buffer, msg_length); + * CCNxCodecNetworkBuffer *netbuff = ccnxCodecNetworkBuffer_CreateFromArray(&ParcMemoryMemoryBlock, NULL, msg_length, packet_buffer); + * + * size_t newPosition = ccnxCodecNetworkBuffer_Position(netbuff) + sizeof(uint8_t); + * ccnxCodecNetworkBuffer_PutUint8(netbuff, 0x12); + * + * PARCBuffer *buffer = ccnxCodecNetworkBuffer_CreateParcBuffer(netbuff); + * } + * @endcode + */ +PARCBuffer *ccnxCodecNetworkBuffer_CreateParcBuffer(CCNxCodecNetworkBuffer *buffer); + +/** + * Runs a signer over the network buffer + * + * Runs a {@link PARCSigner} over a specified range of the network buffer + * + * @param [in] buffer An allocated `CCNxCodecNetworkBuffer`. + * @param [in] start The start position (must be 0 <= start < Limit) + * @param [in] end The last posiiton (start < end <= Limit) + * @param [in] signer The `PARCSigner` + * + * @return non-null The {@link PARCSignature} computed by the signer + * @return null An error + * + * Example: + * @code + * { + * parcSecurity_Init(); + * PARCSigner *signer = parcSigner_Create(parcPublicKeySignerPkcs12Store_Open("test_rsa.p12", "blueberry", PARCCryptoHashType_SHA256)); + * + * uint8_t *packet_buffer; + * packet_buffer = parcMemory_Allocate(msg_length); + * read(fd, packet_buffer, msg_length); + * CCNxCodecNetworkBuffer *netbuff = ccnxCodecNetworkBuffer_CreateFromArray(&ParcMemoryMemoryBlock, NULL, msg_length, packet_buffer); + * + * size_t newPosition = ccnxCodecNetworkBuffer_Position(netbuff) + sizeof(uint8_t); + * ccnxCodecNetworkBuffer_PutUint8(netbuff, 0x12); + * + * PARCSignature *sig = ccnxCodecNetworkBuffer_ComputeSignature(netbuff, 0, ccnxCodecNetworkBuffer_Limit(netbuff), signer); + * } + * @endcode + */ +PARCSignature *ccnxCodecNetworkBuffer_ComputeSignature(CCNxCodecNetworkBuffer *buffer, size_t start, size_t end, PARCSigner *signer); + +/** + * Get a `uint8_t` byte from the buffer, does not change position. + * + * Reads the byte at the given position. The position must be less than the buffer's limit. + * + * @param [in] netbuff An allocated memory buffer. + * @param [in] position Must be 0 <= position < Limit. + * + * @return number The specified byte + * + * Example: + * @code + * { + * uint8_t *packet_buffer; + * packet_buffer = parcMemory_Allocate(msg_length); + * read(fd, packet_buffer, msg_length); + * CCNxCodecNetworkBuffer *netbuff = ccnxCodecNetworkBuffer_CreateFromArray(&ParcMemoryMemoryBlock, NULL, msg_length, packet_buffer); + * + * size_t newPosition = ccnxCodecNetworkBuffer_Position(netbuff) + sizeof(uint8_t); + * ccnxCodecNetworkBuffer_PutUint8(netbuff, 0x12); + * + * uint8_t byte = ccnxCodecNetworkBuffer_GetUint8(netbuff, 0); + * } + * @endcode + */ +uint8_t ccnxCodecNetworkBuffer_GetUint8(const CCNxCodecNetworkBuffer *netbuff, size_t position); + +/** + * Prints the buffer to the console. + * + * @param [in] netbuff A `CCNxCodecNetworkBuffer` instance. + * @param [in] indent The number of spaces by which to indent the output. + * + * Example: + * @code + * { + * uint8_t *packet_buffer; + * packet_buffer = parcMemory_Allocate(msg_length); + * read(fd, packet_buffer, msg_length); + * CCNxCodecNetworkBuffer *netbuff = ccnxCodecNetworkBuffer_CreateFromArray(&ParcMemoryMemoryBlock, NULL, msg_length, packet_buffer); + * + * size_t newPosition = ccnxCodecNetworkBuffer_Position(netbuff) + sizeof(uint8_t); + * ccnxCodecNetworkBuffer_PutUint8(netbuff, 0x12); + * + * ccnxCodecNetworkBuffer_Display(netbuff, 0); + * } + * @endcode + */ +void ccnxCodecNetworkBuffer_Display(const CCNxCodecNetworkBuffer *netbuff, unsigned indent); + +// ====================================================================== +// IoVec related + +/** + * Creates a read-only `CCNxCodecNetworkBufferIoVec` representation of the `CCNxCodecNetworkBuffer`. + * + * Creates a reference to the `CCNxCodecNetworkBuffer`, so the buffer will not go away until the IoVec is released. + * It is a zero-copy operation. The IoVec is a read-only representation. It is used to return a "struct iovec" + * for doing a gathering write. + * + * @param [in] buffer An allocated `CCNxCodecNetworkBuffer` (will acquire a reference to it). + * + * @return non-null An allocated {@link CCNxCodecNetworkBufferIoVec}, you must call {@link ccnxCodecNetworkBufferIoVec_Release} + * @return null An error + * + * Example: + * @code + * { + * CCNxCodecNetworkBuffer *netbuff = ccnxCodecNetworkBuffer_Create(&ParcMemoryMemoryBlock, NULL); + * ccnxCodecNetworkBuffer_PutArray(netbuff, sizeof(interest_nameA), interest_nameA); + * CCNxCodecNetworkBufferIoVec *vec = ccnxCodecNetworkBuffer_CreateIoVec(netbuff); + * ccnxCodecNetworkBuffer_Release(&netbuff); + * + * // the memory represented by iov will be "interest_nameA" + * const struct iovec * iov = ccnxCodecNetworkBufferIoVec_GetArray(vec); + * + * // final release of "netbuff" too + * ccnxCodecNetworkBufferIoVec_Release(&vec); + * } + * @endcode + */ +CCNxCodecNetworkBufferIoVec *ccnxCodecNetworkBuffer_CreateIoVec(CCNxCodecNetworkBuffer *buffer); + +/** + * Increase the number of references to a `CCNxCodecNetworkBufferIoVec`. + * + * Note that new `CCNxCodecNetworkBufferIoVec` is not created, + * only that the given `CCNxCodecNetworkBufferIoVec` reference count is incremented. + * Discard the reference by invoking `ccnxCodecNetworkBufferIoVec_Release`. + * + * @param [in] vec A pointer to a `CCNxCodecNetworkBufferIoVec` instance to acquire. + * + * @return The @p vec. + * + * Example: + * @code + * { + * CCNxCodecNetworkBuffer *netbuff = ccnxCodecNetworkBuffer_Create(&ParcMemoryMemoryBlock, NULL); + * ccnxCodecNetworkBuffer_PutArray(netbuff, sizeof(interest_nameA), interest_nameA); + * CCNxCodecNetworkBufferIoVec *vec = ccnxCodecNetworkBuffer_CreateIoVec(netbuff); + * ccnxCodecNetworkBuffer_Release(&netbuff); + * + * CCNxCodecNetworkBufferIoVec *handle = ccnxCodecNetworkBufferIoVec_Acquire(vec); + * const struct iovec *iov = ccnxCodecNetworkBufferIoVec_GetArray(handle); + * + * ccnxCodecNetworkBufferIoVec_Release(&handle); + * ccnxCodecNetworkBufferIoVec_Release(&vec); + * } + * @endcode + */ +CCNxCodecNetworkBufferIoVec *ccnxCodecNetworkBufferIoVec_Acquire(CCNxCodecNetworkBufferIoVec *vec); + +/** + * Release a previously acquired reference to the specified instance, + * decrementing the reference count for the instance. + * + * The pointer to the instance is set to NULL as a side-effect of this function. + * + * If the invocation causes the last reference to the instance to be released, + * the instance is deallocated and the instance's implementation will perform + * additional cleanup and release other privately held references. + * + * @param [in,out] vecPtr A pointer to a pointer to the instance to release. + * + * Example: + * @code + * { + * CCNxCodecNetworkBuffer *netbuff = ccnxCodecNetworkBuffer_Create(&ParcMemoryMemoryBlock, NULL); + * ccnxCodecNetworkBuffer_PutArray(netbuff, sizeof(interest_nameA), interest_nameA); + * CCNxCodecNetworkBufferIoVec *vec = ccnxCodecNetworkBuffer_CreateIoVec(netbuff); + * ccnxCodecNetworkBuffer_Release(&netbuff); + * + * const struct iovec *iov = ccnxCodecNetworkBufferIoVec_GetArray(vec); + * + * ccnxCodecNetworkBufferIoVec_Release(&vec); + * } + * @endcode + */ +void ccnxCodecNetworkBufferIoVec_Release(CCNxCodecNetworkBufferIoVec **vecPtr); + +/** + * Returns the number of extents in the iovec. + * + * The number of memory buffers gathered in the iovec. + * + * @param [in] vec An allocated `CCNxCodecNetworkBufferIoVec`. + * + * @return The number of buffers gathered in the iovec. + * + * Example: + * @code + * { + * CCNxCodecNetworkBuffer *netbuff = ccnxCodecNetworkBuffer_Create(&ParcMemoryMemoryBlock, NULL); + * ccnxCodecNetworkBuffer_PutArray(netbuff, sizeof(interest_nameA), interest_nameA); + * CCNxCodecNetworkBufferIoVec *vec = ccnxCodecNetworkBuffer_CreateIoVec(netbuff); + * ccnxCodecNetworkBuffer_Release(&netbuff); + * + * // the memory represented by iov will be "interest_nameA" + * writev(fh, ccnxCodecNetworkBufferIoVec_GetArray(vec), ccnxCodecNetworkBufferIoVec_GetCount(vec)); + * + * // final release of "netbuff" too + * ccnxCodecNetworkBufferIoVec_Release(&vec); + * } + * @endcode + */ +int ccnxCodecNetworkBufferIoVec_GetCount(CCNxCodecNetworkBufferIoVec *vec); + +/** + * Returns an iovec representation of the memory. + * + * Returns the internal iovec representation of the memory. You do NOT need to free this. + * + * @param [in] vec An allocated `CCNxCodecNetworkBufferIoVec`. + * + * @return non-null The internal iovec representation of the memory, do not free + * @return null An error + * + * Example: + * @code + * { + * CCNxCodecNetworkBuffer *netbuff = ccnxCodecNetworkBuffer_Create(&ParcMemoryMemoryBlock, NULL); + * ccnxCodecNetworkBuffer_PutArray(netbuff, sizeof(interest_nameA), interest_nameA); + * CCNxCodecNetworkBufferIoVec *vec = ccnxCodecNetworkBuffer_CreateIoVec(netbuff); + * ccnxCodecNetworkBuffer_Release(&netbuff); + * + * // the memory represented by iov will be "interest_nameA" + * writev(fh, ccnxCodecNetworkBufferIoVec_GetArray(vec), ccnxCodecNetworkBufferIoVec_GetCount(vec)); + * + * // final release of "netbuff" too + * ccnxCodecNetworkBufferIoVec_Release(&vec); + * } + * @endcode + */ +const struct iovec *ccnxCodecNetworkBufferIoVec_GetArray(CCNxCodecNetworkBufferIoVec *vec); + +/** + * Dispalys the CCNxCodecNetworkBufferIoVec to the console. + * + * @param [in] vec A `CCNxCodecNetworkBufferIoVec` instance. + * @param [in] indent The number of spaces by which to indent the output. + * + * Example: + * @code + * { + * CCNxCodecNetworkBuffer *netbuff = ccnxCodecNetworkBuffer_Create(&ParcMemoryMemoryBlock, NULL); + * ccnxCodecNetworkBuffer_PutArray(netbuff, sizeof(interest_nameA), interest_nameA); + * CCNxCodecNetworkBufferIoVec *vec = ccnxCodecNetworkBuffer_CreateIoVec(netbuff); + * ccnxCodecNetworkBuffer_Release(&netbuff); + * + * ccnxCodecNetworkBufferIoVec_Display(vec, 0); + * + * ccnxCodecNetworkBufferIoVec_Release(&vec); + * } + * @endcode + */ +void ccnxCodecNetworkBufferIoVec_Display(const CCNxCodecNetworkBufferIoVec *vec, int indent); + +/** + * The total bytes of all iovecs. + * + * The total number of bytes as if linearized memory. + * + * @param [in] vec An allocated `CCNxCodecNetworkBufferIoVec`. + * + * @return number The total bytes represented by all iovecs + * + * Example: + * @code + * { + * CCNxCodecNetworkBuffer *netbuff = ccnxCodecNetworkBuffer_Create(&ParcMemoryMemoryBlock, NULL); + * ccnxCodecNetworkBuffer_PutArray(netbuff, sizeof(interest_nameA), interest_nameA); + * CCNxCodecNetworkBufferIoVec *vec = ccnxCodecNetworkBuffer_CreateIoVec(netbuff); + * ccnxCodecNetworkBuffer_Release(&netbuff); + * + * size_t interestSize = ccnxCodecNetworkBufferIoVec_Length(vec); + * // interestSize will be the size of the linearized interest put into the network buffer + * + * ccnxCodecNetworkBufferIoVec_Release(&vec); + * } + * @endcode + */ +size_t ccnxCodecNetworkBufferIoVec_Length(const CCNxCodecNetworkBufferIoVec *vec); + +/** + * Determine if two `CCNxCodecNetworkBufferIoVec` instances are equal. + * + * The following equivalence relations on non-null `CCNxCodecNetworkBufferIoVec` instances are maintained: + * + * * It is reflexive: for any non-null reference value x, `ccnxCodecNetworkBufferIoVec_Equals(x, x)` must return true. + * + * * It is symmetric: for any non-null reference values x and y, `ccnxCodecNetworkBufferIoVec_Equals(x, y)` must return true if and only if + * `ccnxCodecNetworkBufferIoVec_Equals(y x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `ccnxCodecNetworkBufferIoVec_Equals(x, y)` returns true and + * `ccnxCodecNetworkBufferIoVec_Equals(y, z)` returns true, + * then `ccnxCodecNetworkBufferIoVec_Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple invocations of `ccnxCodecNetworkBufferIoVec_Equals(x, y)` + * consistently return true or consistently return false. + * + * * For any non-null reference value x, `ccnxCodecNetworkBufferIoVec_Equals(x, NULL)` must return false. + * + * + * @param [in] a A pointer to a `CCNxCodecNetworkBufferIoVec` instance. + * @param [in] b A pointer to a `CCNxCodecNetworkBufferIoVec` instance. + * + * @return true if `CCNxCodecNetworkBufferIoVec` x and y are equal. + * @return false if `CCNxCodecNetworkBufferIoVec` x and y are not equal. + * + * Example: + * @code + * { + * CCNxCodecNetworkBuffer *netbuff = ccnxCodecNetworkBuffer_Create(&ParcMemoryMemoryBlock, NULL); + * ccnxCodecNetworkBuffer_PutArray(netbuff, sizeof(interest_nameA), interest_nameA); + * CCNxCodecNetworkBufferIoVec *vec1 = ccnxCodecNetworkBuffer_CreateIoVec(netbuff); + * CCNxCodecNetworkBufferIoVec *vec2 = ccnxCodecNetworkBuffer_CreateIoVec(netbuff); + * ccnxCodecNetworkBuffer_Release(&netbuff); + * + * if (ccnxCodecNetworkBufferIoVec_Equals(vec1, vec2)) { + * printf("IOVectors are equal.\n"); + * } else { + * printf("IOVectors are NOT equal.\n"); + * } + * + * ccnxCodecNetworkBufferIoVec_Release(&vec); + * } + * @endcode + */ +bool ccnxCodecNetworkBufferIoVec_Equals(const CCNxCodecNetworkBufferIoVec *a, const CCNxCodecNetworkBufferIoVec *b); +#endif // Libccnx_codec_ccnxCodecNetworkBuffer_h diff --git a/libccnx-common/ccnx/common/codec/ccnxCodec_TlvDecoder.c b/libccnx-common/ccnx/common/codec/ccnxCodec_TlvDecoder.c new file mode 100755 index 00000000..8e547743 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/ccnxCodec_TlvDecoder.c @@ -0,0 +1,320 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * We use a 2-byte T and a 2-byte L + * + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <LongBow/runtime.h> +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Buffer.h> +#include <ccnx/common/codec/ccnxCodec_NetworkBuffer.h> + +#include <ccnx/common/codec/ccnxCodec_TlvDecoder.h> + +struct ccnx_codec_tlv_decoder { + // we use a read only buffer because we want independent + // position and limit from whatever the user gives us. + PARCBuffer *buffer; + + CCNxCodecError *error; +}; + +CCNxCodecTlvDecoder * +ccnxCodecTlvDecoder_Create(PARCBuffer *buffer) +{ + assertNotNull(buffer, "Parameter buffer must be non-null"); + + CCNxCodecTlvDecoder *decoder = parcMemory_AllocateAndClear(sizeof(CCNxCodecTlvDecoder)); + assertNotNull(decoder, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(CCNxCodecTlvDecoder)); + + // create a reference but with independent position + limit from what the user gives us + decoder->buffer = parcBuffer_Slice(buffer); + + return decoder; +} + +void +ccnxCodecTlvDecoder_Destroy(CCNxCodecTlvDecoder **decoderPtr) +{ + assertNotNull(decoderPtr, "Parameter must be non-null double pointer"); + assertNotNull(*decoderPtr, "Parameter must dereferecne to non-null pointer"); + CCNxCodecTlvDecoder *decoder = *decoderPtr; + parcBuffer_Release(&decoder->buffer); + + + if (decoder->error) { + ccnxCodecError_Release(&decoder->error); + } + + parcMemory_Deallocate((void **) &decoder); + *decoderPtr = NULL; +} + +bool +ccnxCodecTlvDecoder_IsEmpty(CCNxCodecTlvDecoder *decoder) +{ + assertNotNull(decoder, "Parameter decoder must be non-null"); + return (!parcBuffer_HasRemaining(decoder->buffer)); +} + +bool +ccnxCodecTlvDecoder_EnsureRemaining(CCNxCodecTlvDecoder *decoder, size_t bytes) +{ + assertNotNull(decoder, "Parameter decoder must be non-null"); + return parcBuffer_Remaining(decoder->buffer) >= bytes; +} + +size_t +ccnxCodecTlvDecoder_Remaining(const CCNxCodecTlvDecoder *decoder) +{ + assertNotNull(decoder, "Parameter decoder must be non-null"); + return parcBuffer_Remaining(decoder->buffer); +} + +uint16_t +ccnxCodecTlvDecoder_PeekType(CCNxCodecTlvDecoder *decoder) +{ + assertNotNull(decoder, "Parameter decoder must be non-null"); + size_t position = parcBuffer_Position(decoder->buffer); + uint16_t type = parcBuffer_GetUint16(decoder->buffer); + parcBuffer_SetPosition(decoder->buffer, position); + return type; +} + +uint16_t +ccnxCodecTlvDecoder_GetType(CCNxCodecTlvDecoder *decoder) +{ + assertNotNull(decoder, "Parameter decoder must be non-null"); + return parcBuffer_GetUint16(decoder->buffer); +} + +uint16_t +ccnxCodecTlvDecoder_GetLength(CCNxCodecTlvDecoder *decoder) +{ + assertNotNull(decoder, "Parameter decoder must be non-null"); + return parcBuffer_GetUint16(decoder->buffer); +} + +PARCBuffer * +ccnxCodecTlvDecoder_GetValue(CCNxCodecTlvDecoder *decoder, uint16_t length) +{ + assertNotNull(decoder, "Parameter decoder must be non-null"); + PARCBuffer *value = NULL; + + if (ccnxCodecTlvDecoder_EnsureRemaining(decoder, length)) { + value = parcBuffer_Slice(decoder->buffer); + parcBuffer_SetLimit(value, length); + + size_t position = parcBuffer_Position(decoder->buffer); + position += length; + parcBuffer_SetPosition(decoder->buffer, position); + } + + return value; +} + +PARCBuffer * +ccnxCodecTlvDecoder_GetBuffer(CCNxCodecTlvDecoder *decoder, uint16_t type) +{ + assertNotNull(decoder, "Parameter decoder must be non-null"); + + PARCBuffer *output = NULL; + if (ccnxCodecTlvDecoder_EnsureRemaining(decoder, 4)) { + if (ccnxCodecTlvDecoder_PeekType(decoder) == type) { + (void) ccnxCodecTlvDecoder_GetType(decoder); + uint16_t length = ccnxCodecTlvDecoder_GetLength(decoder); + output = ccnxCodecTlvDecoder_GetValue(decoder, length); + } + } + return output; +} + +CCNxCodecTlvDecoder * +ccnxCodecTlvDecoder_GetContainer(CCNxCodecTlvDecoder *decoder, uint16_t length) +{ + CCNxCodecTlvDecoder *innerDecoder = NULL; + if (ccnxCodecTlvDecoder_EnsureRemaining(decoder, length)) { + PARCBuffer *value = ccnxCodecTlvDecoder_GetValue(decoder, length); + innerDecoder = ccnxCodecTlvDecoder_Create(value); + parcBuffer_Release(&value); + } + return innerDecoder; +} + +bool +ccnxCodecTlvDecoder_GetUint8(CCNxCodecTlvDecoder *decoder, uint16_t type, uint8_t *output) +{ + const size_t valueLength = 1; + bool success = false; + if (parcBuffer_Remaining(decoder->buffer) >= 4 + valueLength) { + if (ccnxCodecTlvDecoder_PeekType(decoder) == type) { + // advance the buffer + (void) ccnxCodecTlvDecoder_GetType(decoder); + if (ccnxCodecTlvDecoder_GetLength(decoder) == valueLength) { + *output = parcBuffer_GetUint8(decoder->buffer); + success = true; + } + } + } + return success; +} + +bool +ccnxCodecTlvDecoder_GetUint16(CCNxCodecTlvDecoder *decoder, uint16_t type, uint16_t *output) +{ + const size_t valueLength = 2; + bool success = false; + if (parcBuffer_Remaining(decoder->buffer) >= 4 + valueLength) { + if (ccnxCodecTlvDecoder_PeekType(decoder) == type) { + // advance the buffer + (void) ccnxCodecTlvDecoder_GetType(decoder); + if (ccnxCodecTlvDecoder_GetLength(decoder) == valueLength) { + *output = parcBuffer_GetUint16(decoder->buffer); + success = true; + } + } + } + return success; +} + +bool +ccnxCodecTlvDecoder_GetUint32(CCNxCodecTlvDecoder *decoder, uint16_t type, uint32_t *output) +{ + const size_t valueLength = 4; + bool success = false; + if (parcBuffer_Remaining(decoder->buffer) >= 4 + valueLength) { + if (ccnxCodecTlvDecoder_PeekType(decoder) == type) { + // advance the buffer + (void) ccnxCodecTlvDecoder_GetType(decoder); + if (ccnxCodecTlvDecoder_GetLength(decoder) == valueLength) { + *output = parcBuffer_GetUint32(decoder->buffer); + success = true; + } + } + } + return success; +} + +bool +ccnxCodecTlvDecoder_GetUint64(CCNxCodecTlvDecoder *decoder, uint16_t type, uint64_t *output) +{ + const size_t valueLength = 8; + bool success = false; + if (parcBuffer_Remaining(decoder->buffer) >= 4 + valueLength) { + if (ccnxCodecTlvDecoder_PeekType(decoder) == type) { + // advance the buffer + (void) ccnxCodecTlvDecoder_GetType(decoder); + if (ccnxCodecTlvDecoder_GetLength(decoder) == valueLength) { + *output = parcBuffer_GetUint64(decoder->buffer); + success = true; + } + } + } + return success; +} + + +size_t +ccnxCodecTlvDecoder_Position(CCNxCodecTlvDecoder *decoder) +{ + assertNotNull(decoder, "Parameter decoder must be non-null"); + return parcBuffer_Position(decoder->buffer); +} + +bool +ccnxCodecTlvDecoder_Advance(CCNxCodecTlvDecoder *decoder, uint16_t length) +{ + assertNotNull(decoder, "Parameter decoder must be non-null"); + bool success = false; + if (parcBuffer_Remaining(decoder->buffer) >= length) { + size_t position = parcBuffer_Position(decoder->buffer); + position += length; + parcBuffer_SetPosition(decoder->buffer, position); + success = true; + } + return success; +} + +bool +ccnxCodecTlvDecoder_BufferToVarInt(PARCBuffer *buffer, uint16_t length, uint64_t *output) +{ + assertNotNull(buffer, "Parameter buffer must be non-null"); + assertNotNull(output, "Parameter output must be non-null"); + + bool success = false; + if (length >= 1 && length <= 8 && parcBuffer_Remaining(buffer) >= length) { + uint64_t value = 0; + for (int i = 0; i < length; i++) { + value = value << 8 | parcBuffer_GetUint8(buffer); + } + *output = value; + success = true; + } + return success; +} + +bool +ccnxCodecTlvDecoder_GetVarInt(CCNxCodecTlvDecoder *decoder, uint16_t length, uint64_t *output) +{ + assertNotNull(decoder, "Parameter decoder must be non-null"); + return ccnxCodecTlvDecoder_BufferToVarInt(decoder->buffer, length, output); +} + +bool +ccnxCodecTlvDecoder_HasError(const CCNxCodecTlvDecoder *decoder) +{ + assertNotNull(decoder, "Parameter decoder must be non-null"); + if (decoder->error) { + return true; + } + + return false; +} + + +bool +ccnxCodecTlvDecoder_SetError(CCNxCodecTlvDecoder *decoder, CCNxCodecError *error) +{ + assertNotNull(decoder, "Parameter decoder must be non-null"); + if (ccnxCodecTlvDecoder_HasError(decoder)) { + return false; + } + + decoder->error = ccnxCodecError_Acquire(error); + return true; +} + + +void +ccnxCodecTlvDecoder_ClearError(CCNxCodecTlvDecoder *decoder) +{ + assertNotNull(decoder, "Parameter decoder must be non-null"); + if (ccnxCodecTlvDecoder_HasError(decoder)) { + ccnxCodecError_Release(&decoder->error); + } +} + + +CCNxCodecError * +ccnxCodecTlvDecoder_GetError(const CCNxCodecTlvDecoder *decoder) +{ + assertNotNull(decoder, "Parameter decoder must be non-null"); + return decoder->error; +} diff --git a/libccnx-common/ccnx/common/codec/ccnxCodec_TlvDecoder.h b/libccnx-common/ccnx/common/codec/ccnxCodec_TlvDecoder.h new file mode 100755 index 00000000..39cdcb94 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/ccnxCodec_TlvDecoder.h @@ -0,0 +1,677 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file ccnxCodec_TlvDecoder.h + * @ingroup networking + * @brief TLV codec for messages + * + * TLV decoder + * + * Terminology: + * type = a field that labels a value + * length = the byte lenth of the value + * value = the data + * header = type + length + * container= a value that contains TLVs + * + * For example, in this structure, the "type 1" TLV is a container that holds a second TLV + * The second TLV is a terminal, and holds an opaque value. + * + * { .type = 1, .length = 20, .value = { .type = 2, .length = 16, .value ="It was a dark a " } } + * + * To decode the above example, we would use the decoder like this: + * + * @code + * { + * CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(container); + * unsigned type = ccnxCodecTlvDecoder_GetType(decoder); + * unsigned length = ccnxCodecTlvDecoder_GetLength(decoder); + * if (type == 3) { + * size_t end = ccnxCodecTlvDecoder_GetPosition(decoder) + length; + * while ( ccnxCodecTlvDecoder_GetPosition(decoder) < end ) { + * type = ccnxCodecTlvDecoder_GetType(decoder); + * length = ccnxCodecTlvDecoder_GetLength(decoder); + * if (type == 1) { + * PARCBuffer *name = ccnxCodecTlvDecoder_GetValue(decoder, length); + * // use name, then release it + * } else if (type == 2) { + * PARCBuffer *address = ccnxCodecTlvDecoder_GetValue(decoder, length); + * // use address, then release it + * } + * } + * } + * PARCReadOnlyBuffer * value = ccnxCodecTlvDecoder_GetValue(decoder, length); + * } + * @endcode + * + * Another way to do the same parsing without having to use `ccnxCodecTlvDecoder_GetPosition' is to + * recursively parse each value: + * + * @code + * { + * CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(container); + * unsigned type = ccnxCodecTlvDecoder_GetType(decoder); + * unsigned length = ccnxCodecTlvDecoder_GetLength(decoder); + * PARCBuffer * value = ccnxCodecTlvDecoder_GetValue(decoder, length); + * if (type == 3) { + * CCNxCodecTlvDecoder *innerDecoder = ccnxCodecTlvDecoder_Create(value); + * while ( ! ccnxCodecTlvDecoder_IsEmpty(innerDecoder) ) { + * type = ccnxCodecTlvDecoder_GetType(decoder); + * length = ccnxCodecTlvDecoder_GetLength(decoder); + * if (type == 1) { + * PARCBuffer *name = ccnxCodecTlvDecoder_GetValue(decoder, length); + * // use name, then release it + * } else if (type == 2) { + * PARCBuffer *address = ccnxCodecTlvDecoder_GetValue(decoder, length); + * // use address, then release it + * } + * } + * ccnxCodecTlvDecoder_Destroy(&innerDecoder); + * } + * PARCReadOnlyBuffer * value = ccnxCodecTlvDecoder_GetValue(decoder, length); + * } + * @endcode + * + * And an even cleaner way is to use ccnxCodecTlvDecoder_GetContainer to pull out sub-decoders + * + * @code + * { + * CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(container); + * unsigned type = ccnxCodecTlvDecoder_GetType(decoder); + * unsigned length = ccnxCodecTlvDecoder_GetLength(decoder); + * if (type == 3) { + * CCNxCodecTlvDecoder *innerDecoder = ccnxCodecTlvDecoder_GetContainer(decoder, length); + * while ( ! ccnxCodecTlvDecoder_IsEmpty(innerDecoder) ) { + * type = ccnxCodecTlvDecoder_GetType(decoder); + * length = ccnxCodecTlvDecoder_GetLength(decoder); + * if (type == 1) { + * PARCBuffer *name = ccnxCodecTlvDecoder_GetValue(decoder, length); + * // use name, then release it + * } else if (type == 2) { + * PARCBuffer *address = ccnxCodecTlvDecoder_GetValue(decoder, length); + * // use address, then release it + * } + * } + * ccnxCodecTlvDecoder_Destroy(&innerDecoder); + * } + * PARCReadOnlyBuffer * value = ccnxCodecTlvDecoder_GetValue(decoder, length); + * } + * @endcode + * + */ + +#ifndef libccnx_ccnx_TlvDecoder_h +#define libccnx_ccnx_TlvDecoder_h + +#include <parc/algol/parc_Buffer.h> +#include <ccnx/common/codec/ccnxCodec_NetworkBuffer.h> +#include <parc/security/parc_Signer.h> +#include <parc/security/parc_Signature.h> + +#include <ccnx/common/codec/ccnxCodec_Error.h> + + +struct ccnx_codec_tlv_decoder; +typedef struct ccnx_codec_tlv_decoder CCNxCodecTlvDecoder; + +/** + * Decodes a TLV-encoded buffer to individual buffers for each Value + * + * Walks through a TLV-encoded buffer returning buffer slices of the + * original. These are 0-copy operations. + * + * The decoder should be based on the CCNxCodecNetworkBufferIoVec, see case 1009 + * + * @param [in] buffer The buffer to parse, must be ready to read. + * + * @return non-null A TLV decoder + * @return null An error + * + * Example: + * @code + * { + * PARCBuffer *input = parcBuffer_Wrap((uint8_t[]) {0xAA, 0xBB, 0x00, 0x04, 0x01, 0x02, 0x03, 0x04}, 8, 0, 8); + * CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(input); + * ccnxCodecTlvDecoder_Destroy(&decoder); + * } + * @endcode + */ +CCNxCodecTlvDecoder *ccnxCodecTlvDecoder_Create(PARCBuffer *buffer); + +/** + * Releases the tlv decoder. + * + * Buffers that have been previously returned remain acquired. The internal + * reference to the input buffer will be released. + * + * @param [in] decoderPtr Pointer to the decoder to destroy + * + * Example: + * @code + * { + * PARCBuffer *input = parcBuffer_Wrap((uint8_t[]) {0xAA, 0xBB, 0x00, 0x04, 0x01, 0x02, 0x03, 0x04}, 8, 0, 8); + * CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(input); + * ccnxCodecTlvDecoder_Destroy(&decoder); + * } + * @endcode + */ +void ccnxCodecTlvDecoder_Destroy(CCNxCodecTlvDecoder **decoderPtr); + +/** + * Tests if there is anything left to decode + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return true There are more bytes available + * @return false At the end of the buffer + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool ccnxCodecTlvDecoder_IsEmpty(CCNxCodecTlvDecoder *decoder); + +/** + * Checks if there are at least `bytes' bytes remaining in the buffer + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return true There are at least `bytes' bytes left + * @return false Buffer underrun + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool ccnxCodecTlvDecoder_EnsureRemaining(CCNxCodecTlvDecoder *decoder, size_t bytes); + +/** + * Returns the bytes remaining in the decoder + * + * The remaining bytes to be decoded + * + * @param [in] decoder An allocated decoder + * + * @retval number The bytes left to decode + * + * Example: + * @code + * <#example#> + * @endcode + */ +size_t ccnxCodecTlvDecoder_Remaining(const CCNxCodecTlvDecoder *decoder); + +/** + * Gets the next bytes as the TLV type + * + * The buffer is advanced the width of the type + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * { + * // GetType |--------| + * // GetLength |--------| + * // GetValue |--------------------| + * PARCBuffer *input = parcBuffer_Wrap((uint8_t[]) {0xAA, 0xBB, 0x00, 0x04, 0x01, 0x02, 0x03, 0x04}, 8, 0, 8); + * CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(input); + * unsigned type = ccnxCodecTlvDecoder_GetType(decoder); + * unsigned length = ccnxCodecTlvDecoder_GetLength(decoder); + * PARCBuffer * value = ccnxCodecTlvDecoder_GetValue(decoder, length); + * // value = 0x01020304 + * } + * @endcode + */ +uint16_t ccnxCodecTlvDecoder_GetType(CCNxCodecTlvDecoder *decoder); + +/** + * Returns the TLV Type but does not advance the decoder + * + * At the current position, decode the next bytes as the TLV type + * + * @param [in] decoder The Decoder object + * + * @return number The TLV type + * + * Example: + * @code + * { + * // PeekType |--------| + * // GetType |--------| + * // GetLength |--------| + * // GetValue |--------------------| + * PARCBuffer *input = parcBuffer_Wrap((uint8_t[]) {0xAA, 0xBB, 0x00, 0x04, 0x01, 0x02, 0x03, 0x04}, 8, 0, 8); + * CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(input); + * unsigned type = ccnxCodecTlvDecoder_PeekType(decoder); + * if( type == 0xAABB ) { + * (void) ccnxCodecTlvDecoder_GetType(decoder); + * unsigned length = ccnxCodecTlvDecoder_GetLength(decoder); + * PARCBuffer * value = ccnxCodecTlvDecoder_GetValue(decoder, length); + * // value = 0x01020304 + * } + * } + * @endcode + */ +uint16_t ccnxCodecTlvDecoder_PeekType(CCNxCodecTlvDecoder *decoder); + +/** + * Gets the next bytes as the TLV length + * + * The buffer is advanced the width of the length + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * { + * // GetType |--------| + * // GetLength |--------| + * // GetValue |--------------------| + * PARCBuffer *input = parcBuffer_Wrap((uint8_t[]) {0xAA, 0xBB, 0x00, 0x04, 0x01, 0x02, 0x03, 0x04}, 8, 0, 8); + * CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(input); + * unsigned type = ccnxCodecTlvDecoder_GetType(decoder); + * unsigned length = ccnxCodecTlvDecoder_GetLength(decoder); + * PARCBuffer * value = ccnxCodecTlvDecoder_GetValue(decoder, length); + * // value = 0x01020304 + * } + * @endcode + */ +uint16_t ccnxCodecTlvDecoder_GetLength(CCNxCodecTlvDecoder *decoder); + +/** + * Returns the next `length' bytes as a value + * + * The buffer is advanced `length' bytes. The returned value is ready for reading. + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * { + * // GetType |--------| + * // GetLength |--------| + * // GetValue |--------------------| + * PARCBuffer *input = parcBuffer_Wrap((uint8_t[]) {0xAA, 0xBB, 0x00, 0x04, 0x01, 0x02, 0x03, 0x04}, 8, 0, 8); + * CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(input); + * unsigned type = ccnxCodecTlvDecoder_GetType(decoder); + * unsigned length = ccnxCodecTlvDecoder_GetLength(decoder); + * PARCBuffer * value = ccnxCodecTlvDecoder_GetValue(decoder, length); + * // value = 0x01020304 + * } + * @endcode + */ +PARCBuffer *ccnxCodecTlvDecoder_GetValue(CCNxCodecTlvDecoder *decoder, uint16_t length); + +/** + * Ensure the current position is of type `type', then return a buffer of the value + * + * If the buffer points to a type of `type', the function will create a buffer of + * the specified length and return the value in a buffer. + * + * The function will return NULL if the types don't match or if there is a + * a decoder underrun (its not as long as the type specifies), or if the length would + * take the end of the input buffer. + * + * @param [in] decoder The TLV decoder object + * @param [in] type The type type to validate + * + * @return non-null A conforming buffer + * @return null An error + * + * Example: + * @code + * { + * // GetBuffer |--------------------------------------------| + * PARCBuffer *input = parcBuffer_Wrap((uint8_t[]) {0xAA, 0xBB, 0x00, 0x04, 0x01, 0x02, 0x03, 0x04}, 8, 0, 8); + * + * CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(input); + * + * PARCBuffer *buffer = ccnxCodecTlvDecoder_GetBuffer(decoder, 0xAABB); + * // buffer contains { 0x01, 0x02, 0x03, 0x04 } + * } + * @endcode + */ +PARCBuffer *ccnxCodecTlvDecoder_GetBuffer(CCNxCodecTlvDecoder *decoder, uint16_t type); + +/** + * The current location is a TLV container (a value that is TLVs) + * + * Returns a TLV decoder that represents the "slice" of the input buffer from + * the current position up to the current position plus `length'. + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return non-null A new sub-decoder + * @return null An error, such as input underrun + * + * Example: + * @code + * { + * // GetType |--------| + * // GetLength |--------| + * // GetContainer |--------------------------------------------| + * // GetBuffer |--------------------------------------------| + * PARCBuffer *input = parcBuffer_Wrap((uint8_t[]) {0x00, 0x01, 0x00, 0x08, 0xAA, 0xBB, 0x00, 0x04, 0x01, 0x02, 0x03, 0x04}, 12, 0, 12); + * + * CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(input); + * + * uint16_t containerType = ccnxCodecTlvDecoder_GetType(decoder); + * size_t containerLength = ccnxCodecTlvDecoder_GetLength(decoder); + * CCNxCodecTlvDecoder *innerDecoder = ccnxCodecTlvDecoder_GetContainer(decoder, containerLength); + * PARCBuffer *buffer = ccnxCodecTlvDecoder_GetBuffer(decoder, 0xAABB); + * + * ccnxCodecTlvDecoder_Destroy(&innerDecoder); + * ccnxCodecTlvDecoder_Destroy(&decoder); + * + * // buffer contains { 0x01, 0x02, 0x03, 0x04 } + * } + * @endcode + */ +CCNxCodecTlvDecoder *ccnxCodecTlvDecoder_GetContainer(CCNxCodecTlvDecoder *decoder, uint16_t length); + +/** + * Decodes the current location as a type, length, and uint8_t value. + * + * Ensures the type is `type' and returns the value as a uint8_t. If the type + * does not match or there is buffer underflow, the function will return false and + * the output will be unchanged. If the TLV length is not "1", it will also return false. + * Otherwise, it returns true and the decoded value. + * + * On success, the decoder is advanced, on failure the decoder will remain at the + * current position. + * + * @param [in] decoder The decoder object + * @param [in] type The TLV type to validate + * @param [out] output The output value + * + * @return true output parameter is valid + * @return false on error (type did not match or buffer underflow) + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_Wrap((uint8_t[]) { 0x10, 0x20, 0x00, 0x01, 0xFF }, 5, 0, 5 ); + * CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(buffer); + * uint8_t value; + * bool success = ccnxCodecTlvDecoder_GetUint8(decoder, 0x1020, &value); + * assert(success && value == 0xFF); + * } + * @endcode + */ +bool ccnxCodecTlvDecoder_GetUint8(CCNxCodecTlvDecoder *decoder, uint16_t type, uint8_t *output); + +/** + * Decodes the current location as a type, length, and uint16_t value. + * + * Ensures the type is `type' and returns the value as a uint16_t. If the type + * does not match or there is buffer underflow, the function will return false and + * the output will be unchanged. If the TLV length is not "2", it will also return false. + * Otherwise, it returns true and the decoded value. + * + * On success, the decoder is advanced, on failure the decoder will remain at the + * current position. + * + * @param [in] decoder The decoder object + * @param [in] type The TLV type to validate + * @param [out] output The output value + * + * @return true output parameter is valid + * @return false on error (type did not match or buffer underflow) + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_Wrap((uint8_t[]) { 0x10, 0x21, 0x00, 0x02, 0xFF, 0xAA }, 6, 0, 6 ); + * CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(buffer); + * uint16_t value; + * bool success = ccnxCodecTlvDecoder_GetUint16(decoder, 0x1021, &value); + * assert(success && value == 0xFFAA); + * } + * @endcode + */ +bool ccnxCodecTlvDecoder_GetUint16(CCNxCodecTlvDecoder *decoder, uint16_t type, uint16_t *output); + +/** + * Decodes the current location as a type, length, and uint32_t value. + * + * Ensures the type is `type' and returns the value as a uint32_t. If the type + * does not match or there is buffer underflow, the function will return false and + * the output will be unchanged. If the TLV length is not "4", it will also return false. + * Otherwise, it returns true and the decoded value. + * + * On success, the decoder is advanced, on failure the decoder will remain at the + * current position. + * + * @param [in] decoder The decoder object + * @param [in] type The TLV type to validate + * @param [out] output The output value + * + * @return true output parameter is valid + * @return false on error (type did not match or buffer underflow) + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_Wrap((uint8_t[]) { 0x10, 0x22, 0x00, 0x04, 0xFF, 0xAA, 0xBB, 0xCC }, 8, 0, 8 ); + * CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(buffer); + * uint32_t value; + * bool success = ccnxCodecTlvDecoder_GetUint32(decoder, 0x1022, &value); + * assert(success && value == 0xFFAABBCC); + * } + * @endcode + */ +bool ccnxCodecTlvDecoder_GetUint32(CCNxCodecTlvDecoder *decoder, uint16_t type, uint32_t *output); + +/** + * Decodes the current location as a type, length, and uint64_t value. + * + * Ensures the type is `type' and returns the value as a uint64_t. If the type + * does not match or there is buffer underflow, the function will return false and + * the output will be unchanged. If the TLV length is not "8", it will also return false. + * Otherwise, it returns true and the decoded value. + * + * On success, the decoder is advanced, on failure the decoder will remain at the + * current position. + * + * @param [in] decoder The decoder object + * @param [in] type The TLV type to validate + * @param [out] output The output value + * + * @return true output parameter is valid + * @return false on error (type did not match or buffer underflow) + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_Wrap((uint8_t[]) { 0x10, 0x23, 0x00, 0x08, 0xFF, 0xAA, 0xBB, 0xCC, 0x00, 0x00, 0x00, 0x00 }, 12, 0, 12 ); + * CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(buffer); + * uint64_t value; + * bool success = ccnxCodecTlvDecoder_GetUint64(decoder, 0x1023, &value); + * assert(success && value == 0xFFAABBCC00000000ULL); + * } + * @endcode + */ +bool ccnxCodecTlvDecoder_GetUint64(CCNxCodecTlvDecoder *decoder, uint16_t type, uint64_t *output); + +/** + * Returns the current byte position of the buffer + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +size_t ccnxCodecTlvDecoder_Position(CCNxCodecTlvDecoder *decoder); + +/** + * Advance the decoder a given number of bytes + * + * Advance the decoder, throwing away a given number of bytes. + * If there are not enough bytes left in the decoder, no action is taken + * + * @param [in] decoder The decoder to advance + * @param [in] length The number of bytes to skip + * + * @return true Advanced the buffer + * @return false Error, likely a buffer underrun (not enough bytes) + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool ccnxCodecTlvDecoder_Advance(CCNxCodecTlvDecoder *decoder, uint16_t length); + +/** + * Decode the current position as a VarInt + * + * A VarInt may be 1 to 8 bytes long. It is interpreted as an unsigned + * integer in network byte order. + * + * @param [in] decoder The TLV decoder + * @param [in] length The number of bytes in the varint + * @param [out] output The value of the varint + * + * @return true Successful decode + * @return fale Error (length too long, too short, or not enough bytes in decoder) + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_Wrap((uint8_t[]) { 0x10, 0x23, 0x00 }, 3, 0, 3 ); + * CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(buffer); + * uint64_t value; + * ccnxCodecTlvDecoder_GetVarInt(decoder, 3, &value); + * // value = 0x0000000000102300 + * } + * @endcode + */ +bool ccnxCodecTlvDecoder_GetVarInt(CCNxCodecTlvDecoder *decoder, uint16_t length, uint64_t *output); + + +/** + * Decode the current position of the Buffer as a VarInt out to 'length' bytes + * + * A VarInt may be 1 to 8 bytes long. It is interpreted as an unsigned + * integer in network byte order. The buffer must have at least 'length' bytes remaining. + * The buffer is advanced. + * + * @param [in] decoder The TLV decoder + * @param [in] length The number of bytes in the varint + * @param [out] output The value of the varint + * + * @return true Successful decode + * @return fale Error (length too long, too short, or not enough bytes in decoder) + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_Wrap((uint8_t[]) { 0x10, 0x23, 0x00 }, 3, 0, 3 ); + * uint64_t value; + * ccnxCodecTlvDecoder_BufferToVarInt(buffer, 3, &value); + * // value = 0x0000000000102300 + * } + * @endcode + */ +bool ccnxCodecTlvDecoder_BufferToVarInt(PARCBuffer *buffer, uint16_t length, uint64_t *output); + +/** + * Determines if the TLV Encoder has an error condition set + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool ccnxCodecTlvDecoder_HasError(const CCNxCodecTlvDecoder *decoder); + +/** + * Sets an error condition. Only one error condition may be set. + * + * Stores a reference counted copy of the CCNxCodecError. If an error is already set, + * this function returns false and does not store a reference count. The previous error + * stays as the current error. + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return true Error condition set + * @return false Error already set, you must clear it first + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool ccnxCodecTlvDecoder_SetError(CCNxCodecTlvDecoder *decoder, CCNxCodecError *error); + +/** + * Clears the error condition, if any + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +void ccnxCodecTlvDecoder_ClearError(CCNxCodecTlvDecoder *decoder); + +/** + * Retrieves the error message + * + * Retrieves the error condition, if any. If no error is set, will return NULL. + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return non-null The error condition set + * @return null No error condition is set + * + * Example: + * @code + * <#example#> + * @endcode + */ +CCNxCodecError *ccnxCodecTlvDecoder_GetError(const CCNxCodecTlvDecoder *encoder); +#endif // libccnx_ccnx_TlvDecoder_h diff --git a/libccnx-common/ccnx/common/codec/ccnxCodec_TlvEncoder.c b/libccnx-common/ccnx/common/codec/ccnxCodec_TlvEncoder.c new file mode 100755 index 00000000..bfc52234 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/ccnxCodec_TlvEncoder.c @@ -0,0 +1,382 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * We use a 2-byte T and a 2-byte L + * + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <LongBow/runtime.h> +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Buffer.h> +#include <ccnx/common/codec/ccnxCodec_NetworkBuffer.h> + +#include <ccnx/common/codec/ccnxCodec_TlvEncoder.h> + +#define NONE_SET 0 +#define START_SET 1 +#define END_SET 2 +#define BOTH_SET 3 + +struct ccnx_codec_tlv_encoder { + CCNxCodecNetworkBuffer *buffer; + + // OR of NONE_SET, START_SET, END_SET + int signatureStartEndSet; + + size_t signatureStart; + size_t signatureEnd; + + CCNxCodecError *error; + PARCSigner *signer; +}; + +CCNxCodecTlvEncoder * +ccnxCodecTlvEncoder_Create(void) +{ + CCNxCodecTlvEncoder *encoder = parcMemory_AllocateAndClear(sizeof(CCNxCodecTlvEncoder)); + assertNotNull(encoder, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(CCNxCodecTlvEncoder)); + + encoder->buffer = ccnxCodecNetworkBuffer_Create(&ParcMemoryMemoryBlock, NULL); + encoder->signatureStartEndSet = NONE_SET; + encoder->error = NULL; + + return encoder; +} + +void +ccnxCodecTlvEncoder_Destroy(CCNxCodecTlvEncoder **encoderPtr) +{ + assertNotNull(encoderPtr, "Parameter must be non-null double pointer"); + assertNotNull(*encoderPtr, "Parameter must dereferecne to non-null pointer"); + CCNxCodecTlvEncoder *encoder = *encoderPtr; + + ccnxCodecNetworkBuffer_Release(&encoder->buffer); + + if (encoder->error) { + ccnxCodecError_Release(&encoder->error); + } + + if (encoder->signer) { + parcSigner_Release(&encoder->signer); + } + + parcMemory_Deallocate((void **) &encoder); + *encoderPtr = NULL; +} + +CCNxCodecTlvEncoder * +ccnxCodecTlvEncoder_Initialize(CCNxCodecTlvEncoder *encoder) +{ + assertNotNull(encoder, "Parameter encoder must be non-null"); + return encoder; +} + +size_t +ccnxCodecTlvEncoder_AppendBuffer(CCNxCodecTlvEncoder *encoder, uint16_t type, PARCBuffer *value) +{ + assertNotNull(encoder, "Parameter encoder must be non-null"); + assertTrue(parcBuffer_Remaining(value) <= UINT16_MAX, "Value length too long, got %zu maximum %u\n", parcBuffer_Remaining(value), UINT16_MAX); + + size_t bytes = 4 + parcBuffer_Remaining(value); + ccnxCodecNetworkBuffer_PutUint16(encoder->buffer, type); + ccnxCodecNetworkBuffer_PutUint16(encoder->buffer, parcBuffer_Remaining(value)); + ccnxCodecNetworkBuffer_PutBuffer(encoder->buffer, value); + + return bytes; +} + +size_t +ccnxCodecTlvEncoder_AppendArray(CCNxCodecTlvEncoder *encoder, uint16_t type, uint16_t length, const uint8_t array[length]) +{ + assertNotNull(encoder, "Parameter encoder must be non-null"); + ccnxCodecNetworkBuffer_PutUint16(encoder->buffer, type); + ccnxCodecNetworkBuffer_PutUint16(encoder->buffer, length); + ccnxCodecNetworkBuffer_PutArray(encoder->buffer, length, array); + return length + 4; +} + +size_t +ccnxCodecTlvEncoder_AppendContainer(CCNxCodecTlvEncoder *encoder, uint16_t type, uint16_t length) +{ + assertNotNull(encoder, "Parameter encoder must be non-null"); + + ccnxCodecNetworkBuffer_PutUint16(encoder->buffer, type); + ccnxCodecNetworkBuffer_PutUint16(encoder->buffer, length); + return 4; +} + +size_t +ccnxCodecTlvEncoder_AppendUint8(CCNxCodecTlvEncoder *encoder, uint16_t type, uint8_t value) +{ + assertNotNull(encoder, "Parameter encoder must be non-null"); + ccnxCodecNetworkBuffer_PutUint16(encoder->buffer, type); + ccnxCodecNetworkBuffer_PutUint16(encoder->buffer, 1); + ccnxCodecNetworkBuffer_PutUint8(encoder->buffer, value); + return 5; +} + +size_t +ccnxCodecTlvEncoder_AppendUint16(CCNxCodecTlvEncoder *encoder, uint16_t type, uint16_t value) +{ + assertNotNull(encoder, "Parameter encoder must be non-null"); + ccnxCodecNetworkBuffer_PutUint16(encoder->buffer, type); + ccnxCodecNetworkBuffer_PutUint16(encoder->buffer, 2); + ccnxCodecNetworkBuffer_PutUint16(encoder->buffer, value); + return 6; +} + +size_t +ccnxCodecTlvEncoder_AppendUint32(CCNxCodecTlvEncoder *encoder, uint16_t type, uint32_t value) +{ + assertNotNull(encoder, "Parameter encoder must be non-null"); + ccnxCodecNetworkBuffer_PutUint16(encoder->buffer, type); + ccnxCodecNetworkBuffer_PutUint16(encoder->buffer, 4); + ccnxCodecNetworkBuffer_PutUint32(encoder->buffer, value); + return 8; +} + +size_t +ccnxCodecTlvEncoder_AppendUint64(CCNxCodecTlvEncoder *encoder, uint16_t type, uint64_t value) +{ + assertNotNull(encoder, "Parameter encoder must be non-null"); + ccnxCodecNetworkBuffer_PutUint16(encoder->buffer, type); + ccnxCodecNetworkBuffer_PutUint16(encoder->buffer, 8); + ccnxCodecNetworkBuffer_PutUint64(encoder->buffer, value); + return 12; +} + +static unsigned +_ccnxCodecTlvEncoder_ComputeVarIntLength(uint64_t value) +{ + unsigned length = 8; + if (value <= 0x00000000000000FFULL) { + length = 1; + } else if (value <= 0x000000000000FFFFULL) { + length = 2; + } else if (value <= 0x0000000000FFFFFFULL) { + length = 3; + } else if (value <= 0x00000000FFFFFFFFULL) { + length = 4; + } else if (value <= 0x000000FFFFFFFFFFULL) { + length = 5; + } else if (value <= 0x0000FFFFFFFFFFFFULL) { + length = 6; + } else if (value <= 0x00FFFFFFFFFFFFFFULL) { + length = 7; + } + + return length; +} + +size_t +ccnxCodecTlvEncoder_AppendVarInt(CCNxCodecTlvEncoder *encoder, uint16_t type, uint64_t value) +{ + assertNotNull(encoder, "Parameter encoder must be non-null"); + + unsigned length = _ccnxCodecTlvEncoder_ComputeVarIntLength(value); + + ccnxCodecNetworkBuffer_PutUint16(encoder->buffer, type); + ccnxCodecNetworkBuffer_PutUint16(encoder->buffer, length); + + // See case 1007 + bool mustContinue = false; + for (int byte = 7; byte >= 0; byte--) { + uint8_t b = (value >> (byte * 8)) & 0xFF; + if (b != 0 || byte == 0 || mustContinue) { + ccnxCodecNetworkBuffer_PutUint8(encoder->buffer, b); + mustContinue = true; + } + } + + return length + 4; +} + +size_t +ccnxCodecTlvEncoder_Position(CCNxCodecTlvEncoder *encoder) +{ + assertNotNull(encoder, "Parameter encoder must be non-null"); + return ccnxCodecNetworkBuffer_Position(encoder->buffer); +} + +size_t +ccnxCodecTlvEncoder_SetPosition(CCNxCodecTlvEncoder *encoder, size_t position) +{ + assertNotNull(encoder, "Parameter encoder must be non-null"); + assertTrue(position <= ccnxCodecNetworkBuffer_Limit(encoder->buffer), + "position beyond end of buffer, got %zu maximum %zu", + position, ccnxCodecNetworkBuffer_Limit(encoder->buffer)); + ccnxCodecNetworkBuffer_SetPosition(encoder->buffer, position); + return position; +} + +void +ccnxCodecTlvEncoder_SetContainerLength(CCNxCodecTlvEncoder *encoder, size_t offset, uint16_t length) +{ + assertNotNull(encoder, "Parameter encoder must be non-null"); + + size_t currentPosition = ccnxCodecNetworkBuffer_Position(encoder->buffer); + + // +2 because we skip over the Type field + ccnxCodecNetworkBuffer_SetPosition(encoder->buffer, offset + 2); + ccnxCodecNetworkBuffer_PutUint16(encoder->buffer, length); + + ccnxCodecNetworkBuffer_SetPosition(encoder->buffer, currentPosition); +} + +void +ccnxCodecTlvEncoder_Finalize(CCNxCodecTlvEncoder *encoder) +{ + assertNotNull(encoder, "Parameter encoder must be non-null"); + + // set the limit to whatever our current possition is. That will truncate the + // packet in case we wrote beyond where we are now. + + ccnxCodecNetworkBuffer_Finalize(encoder->buffer); +} + +PARCBuffer * +ccnxCodecTlvEncoder_CreateBuffer(CCNxCodecTlvEncoder *encoder) +{ + assertNotNull(encoder, "Parameter encoder must be non-null"); + + PARCBuffer *output = ccnxCodecNetworkBuffer_CreateParcBuffer(encoder->buffer); + return output; +} + +CCNxCodecNetworkBufferIoVec * +ccnxCodecTlvEncoder_CreateIoVec(CCNxCodecTlvEncoder *encoder) +{ + assertNotNull(encoder, "Parameter encoder must be non-null"); + return ccnxCodecNetworkBuffer_CreateIoVec(encoder->buffer); +} + +size_t +ccnxCodecTlvEncoder_AppendRawArray(CCNxCodecTlvEncoder *encoder, size_t length, uint8_t array[length]) +{ + assertNotNull(encoder, "Parameter encoder must be non-null"); + ccnxCodecNetworkBuffer_PutArray(encoder->buffer, length, array); + return length; +} + +size_t +ccnxCodecTlvEncoder_PutUint8(CCNxCodecTlvEncoder *encoder, size_t offset, uint8_t value) +{ + assertNotNull(encoder, "Parameter encoder must be non-null"); + size_t position = ccnxCodecNetworkBuffer_Position(encoder->buffer); + ccnxCodecNetworkBuffer_SetPosition(encoder->buffer, offset); + ccnxCodecNetworkBuffer_PutUint8(encoder->buffer, value); + ccnxCodecNetworkBuffer_SetPosition(encoder->buffer, position); + return 1; +} + +size_t +ccnxCodecTlvEncoder_PutUint16(CCNxCodecTlvEncoder *encoder, size_t offset, uint16_t value) +{ + assertNotNull(encoder, "Parameter encoder must be non-null"); + size_t position = ccnxCodecNetworkBuffer_Position(encoder->buffer); + ccnxCodecNetworkBuffer_SetPosition(encoder->buffer, offset); + ccnxCodecNetworkBuffer_PutUint16(encoder->buffer, value); + ccnxCodecNetworkBuffer_SetPosition(encoder->buffer, position); + return 2; +} + +void +ccnxCodecTlvEncoder_MarkSignatureStart(CCNxCodecTlvEncoder *encoder) +{ + assertNotNull(encoder, "Parameter encoder must be non-null"); + encoder->signatureStart = ccnxCodecNetworkBuffer_Position(encoder->buffer); + encoder->signatureStartEndSet |= START_SET; +} + +void +ccnxCodecTlvEncoder_MarkSignatureEnd(CCNxCodecTlvEncoder *encoder) +{ + assertNotNull(encoder, "Parameter encoder must be non-null"); + encoder->signatureEnd = ccnxCodecNetworkBuffer_Position(encoder->buffer); + encoder->signatureStartEndSet |= END_SET; +} + +PARCSignature * +ccnxCodecTlvEncoder_ComputeSignature(CCNxCodecTlvEncoder *encoder) +{ + assertNotNull(encoder, "Parameter encoder must be non-null"); + assertTrue(encoder->signatureStartEndSet == BOTH_SET, "Did not set both start and end positions"); + + return ccnxCodecNetworkBuffer_ComputeSignature(encoder->buffer, encoder->signatureStart, encoder->signatureEnd, encoder->signer); +} + +bool +ccnxCodecTlvEncoder_HasError(const CCNxCodecTlvEncoder *encoder) +{ + assertNotNull(encoder, "Parameter encoder must be non-null"); + if (encoder->error) { + return true; + } + + return false; +} + +bool +ccnxCodecTlvEncoder_SetError(CCNxCodecTlvEncoder *encoder, CCNxCodecError *error) +{ + if (ccnxCodecTlvEncoder_HasError(encoder)) { + return false; + } + + encoder->error = ccnxCodecError_Acquire(error); + return true; +} + +void +ccnxCodecTlvEncoder_ClearError(CCNxCodecTlvEncoder *encoder) +{ + if (ccnxCodecTlvEncoder_HasError(encoder)) { + ccnxCodecError_Release(&encoder->error); + } +} + +CCNxCodecError * +ccnxCodecTlvEncoder_GetError(const CCNxCodecTlvEncoder *encoder) +{ + assertNotNull(encoder, "Parameter encoder must be non-null"); + return encoder->error; +} + +void +ccnxCodecTlvEncoder_SetSigner(CCNxCodecTlvEncoder *encoder, PARCSigner *signer) +{ + assertNotNull(encoder, "Parameter encoder must be non-null"); + if (encoder->signer) { + parcSigner_Release(&encoder->signer); + } + + if (signer) { + encoder->signer = parcSigner_Acquire(signer); + } else { + encoder->signer = NULL; + } +} + +PARCSigner * +ccnxCodecTlvEncoder_GetSigner(const CCNxCodecTlvEncoder *encoder) +{ + assertNotNull(encoder, "Parameter encoder must be non-null"); + return encoder->signer; +} + diff --git a/libccnx-common/ccnx/common/codec/ccnxCodec_TlvEncoder.h b/libccnx-common/ccnx/common/codec/ccnxCodec_TlvEncoder.h new file mode 100644 index 00000000..443eeca0 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/ccnxCodec_TlvEncoder.h @@ -0,0 +1,755 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file ccnxCodec_TlvEncoder.h + * @ingroup networking + * @brief TLV codec for messages + * + * TLV encoder + * + * Terminology: + * type = a field that labels a value + * length = the byte lenth of the value + * value = the data + * header = type + length + * container= a value that contains TLVs + * + * For example, in this structure, the "type 1" TLV is a container that holds a second TLV + * The second TLV is a terminal, and holds an opaque value. + * + * { .type = 1, .length = 20, .value = { .type = 2, .length = 16, .value ="It was a dark a " } } + * + * Example to encode a container that wraps a name and an address: + * @code + * { + * CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + * ccnxCodecTlvEncoder_Initialize(encoder); + * ccnxCodecTlvEncoder_Append(encoder, 1, name); + * ccnxCodecTlvEncoder_Append(encoder, 2, address); + * ccnxCodecTlvEncoder_Finalize(encoder); + * PARCBuffer *nameAndAddress = ccnxCodecTlvEncoder_CreateBuffer(encoder); + * + * // Initialize starts a new buffer + * ccnxCodecTlvEncoder_Initialize(encoder); + * ccnxCodecTlvEncoder_Append(encoder, 3, nameAndAddress); + * ccnxCodecTlvEncoder_Finalize(encoder); + * PARCBuffer *container = ccnxCodecTlvEncoder_CreateBuffer(encoder); + * + * parcBuffer_Destroy(&nameAndAddress); + * ccnxCodecTlvEncoder_Destroy(&encoder); + * + * // now use the container for something... + * } + * @endcode + * + * The TLV will look something like this: + * @code + * { .type = 3, .length = L1 + L2 + overhead, .value = { + * {.type = 1, .length = L1, .value = name}, + * {.type = 2, .length = L2, .value = address} + * } + * } + * @endcode + * + * An alternative way to encode it that does not require recursive encoder is to use Position: + * @code + * { + * // Creates {{T=3, L=length, V={{T=1, L=..., V=name}, {T=2, L=..., V=address}}}} + * CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + * size_t offset = ccnxCodecTlvEncoder_Position(encoder); + * ccnxCodecTlvEncoder_AppendContainer(encoder, 3, 0); + * size_t length = 0; + * length += ccnxCodecTlvEncoder_AppendBuffer(encoder, 1, name); + * length += ccnxCodecTlvEncoder_AppendBuffer(encoder, 2, address); + * ccnxCodecTlvEncoder_SetContainerLength(encoder, offset, length); + * } + * @endcode + * + */ + +#ifndef libccnx_ccnx_TlvEncoder_h +#define libccnx_ccnx_TlvEncoder_h + +#include <parc/algol/parc_Buffer.h> +#include <ccnx/common/codec/ccnxCodec_NetworkBuffer.h> +#include <parc/security/parc_Signer.h> +#include <parc/security/parc_Signature.h> + +#include <ccnx/common/codec/ccnxCodec_Error.h> + +// ====================================================== + +struct ccnx_codec_tlv_encoder; +typedef struct ccnx_codec_tlv_encoder CCNxCodecTlvEncoder; + +/** + * Creates a TLV encoder + * + * The encoder is re-usable, as the state is reset for each Initialize. + * + * @return non-null A TLV encoder + * + * Example: + * @code + * <#example#> + * @endcode + */ +CCNxCodecTlvEncoder *ccnxCodecTlvEncoder_Create(void); + +/** + * Destroys the TLV encoder and all internal state + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +void ccnxCodecTlvEncoder_Destroy(CCNxCodecTlvEncoder **encoderPtr); + +/** + * Initialize the encoder to start a new encoding + * + * <#Paragraphs Of Explanation#> + * + * @param [in] tlv <#description#> + * + * @return non-null The encoder + * + * Example: + * @code + * { + * CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + * ccnxCodecTlvEncoder_Initialize(encoder); + * ccnxCodecTlvEncoder_Append(encoder, 1, name); + * ccnxCodecTlvEncoder_Append(encoder, 2, address); + * ccnxCodecTlvEncoder_Finalize(encoder); + * PARCBuffer *encoded = ccnxCodecTlvEncoder_CreateBuffer(encoder); + * ccnxCodecTlvEncoder_Destroy(&encoder); + * } + * @endcode + */ +CCNxCodecTlvEncoder *ccnxCodecTlvEncoder_Initialize(CCNxCodecTlvEncoder *encoder); + +/** + * Appends a TL container and a PARCBuffer to the enoder + * + * The type is from the parameter `type', the length is from the remaining size + * of the value buffer, and the value comes from `value'. + * + * @param [in] encoder The encoder to append to + * @param [in] type The TLV type + * @param [in] value The length is the remaining buffer size, the position is advanced. + * + * @return number The total bytes of the TLV, including the T and L. + * + * Example: + * @code + * { + * CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + * ccnxCodecTlvEncoder_Initialize(encoder); + * ccnxCodecTlvEncoder_Append(encoder, 1, name); + * ccnxCodecTlvEncoder_Append(encoder, 2, address); + * ccnxCodecTlvEncoder_Finalize(encoder); + * PARCBuffer *encoded = ccnxCodecTlvEncoder_CreateBuffer(encoder); + * ccnxCodecTlvEncoder_Destroy(&encoder); + * } + * @endcode + */ +size_t ccnxCodecTlvEncoder_AppendBuffer(CCNxCodecTlvEncoder *encoder, uint16_t type, PARCBuffer *value); + +/** + * Appends a "TL" container then the bytes of the array + * + * Writes a Type of 'type' and Length of 'length' then appends 'length' bytes from + * the array to the encoder. + * + * @param [in] encoder An allocated CCnxCodecTlvEncoder + * @param [in] type The TLV type + * @param [in] length The TLV length + * @param [in] array The bytes to append + * + * @return number The number of bytes appended (including the T and L) + * + * Example: + * @code + * <#example#> + * @endcode + */ +size_t ccnxCodecTlvEncoder_AppendArray(CCNxCodecTlvEncoder *encoder, uint16_t type, uint16_t length, const uint8_t *array); + +/** + * Appends a TL without a V. + * + * Appends a T and L without a V. Useful for doing a heirarchical encoding where the V + * will be more TLVs. You will either need to know L or go back and fix it up. + * + * @param [in] encoder The encoder to modify + * @param [in] type The TLV type + * @param [in] length The TLV length, use 0 if you do not know. + * + * @return bytes The bytes appended (the size of the T and L) + * + * Example: + * @code + * { + * // Creates {{ T=99, L=.., V=SequenceNumber }, {T=1, L=length, V={{T=2, L=..., V=name}, {T=3, L=..., V=address}}}} + * CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + * ccnxCodecTlvEncoder_Initialize(encoder); + * ccnxCodecTlvEncoder_AppendBuffer(encoder, 99, sequenceNumber); + * size_t offset = ccnxCodecTlvEncoder_Position(encoder); + * ccnxCodecTlvEncoder_AppendContainer(encoder, 1, 0); + * size_t length = 0; + * length += ccnxCodecTlvEncoder_AppendBuffer(encoder, 2, name); + * length += ccnxCodecTlvEncoder_AppendBuffer(encoder, 3, address); + * ccnxCodecTlvEncoder_SetContainerLength(encoder, offset, length); + * } + * @endcode + */ +size_t ccnxCodecTlvEncoder_AppendContainer(CCNxCodecTlvEncoder *encoder, uint16_t type, uint16_t length); + +/** + * Adds a TLV with an 8-bit value + * + * Uses network byte order for the value. The returned size includes + * the type and length lengths plus the value length. + * + * @param [in] encoder The TLV encoder + * @param [in] type The Type value to use for the container + * @param [in] value The value to encode + * + * @return number The number of bytes appended to the encoded (5) + * + * Example: + * @code + * { + * // encoded buffer with be { .type = 1, .length = 1, .value = 0x19 } + * CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + * ccnxCodecTlvEncoder_Initialize(encoder); + * ccnxCodecTlvEncoder_AppendUint8(encoder, 1, (uint8_t) 25); + * ccnxCodecTlvEncoder_Finalize(encoder); + * PARCBuffer *encoded = ccnxCodecTlvEncoder_CreateBuffer(encoder); + * ccnxCodecTlvEncoder_Destroy(&encoder); + * } + * @endcode + */ +size_t ccnxCodecTlvEncoder_AppendUint8(CCNxCodecTlvEncoder *encoder, uint16_t type, uint8_t value); + +/** + * Adds a TLV with a 16-bit value + * + * Uses network byte order for the value. The returned size includes + * the type and length lengths plus the value length. + * + * @param [in] encoder The TLV encoder + * @param [in] type The Type value to use for the container + * @param [in] value The value to encode + * + * @return number The number of bytes appended to the encoded (6) + * + * Example: + * @code + * { + * // encoded buffer with be { .type = 1, .length = 2, .value = 0x1234 } + * CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + * ccnxCodecTlvEncoder_Initialize(encoder); + * ccnxCodecTlvEncoder_AppendUint16(encoder, 1, (uint16_t) 0x1234); + * ccnxCodecTlvEncoder_Finalize(encoder); + * PARCBuffer *encoded = ccnxCodecTlvEncoder_CreateBuffer(encoder); + * ccnxCodecTlvEncoder_Destroy(&encoder); + * } + * @endcode + */ +size_t ccnxCodecTlvEncoder_AppendUint16(CCNxCodecTlvEncoder *encoder, uint16_t type, uint16_t value); + +/** + * Adds a TLV with a 32-bit value + * + * Uses network byte order for the value. The returned size includes + * the type and length lengths plus the value length. + * + * @param [in] encoder The TLV encoder + * @param [in] type The Type value to use for the container + * @param [in] value The value to encode + * + * @return number The number of bytes appended to the encoded (8) + * + * Example: + * @code + * { + * // encoded buffer with be { .type = 1, .length = 4, .value = 0x12345678 } + * CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + * ccnxCodecTlvEncoder_Initialize(encoder); + * ccnxCodecTlvEncoder_AppendUint32(encoder, 1, (uint32_t) 0x12345678); + * ccnxCodecTlvEncoder_Finalize(encoder); + * PARCBuffer *encoded = ccnxCodecTlvEncoder_CreateBuffer(encoder); + * ccnxCodecTlvEncoder_Destroy(&encoder); + * } + * @endcode + */ +size_t ccnxCodecTlvEncoder_AppendUint32(CCNxCodecTlvEncoder *encoder, uint16_t type, uint32_t value); + + +/** + * Adds a TLV with a 64-bit value + * + * Uses network byte order for the value. The returned size includes + * the type and length lengths plus the value length. + * + * @param [in] encoder The TLV encoder + * @param [in] type The Type value to use for the container + * @param [in] value The value to encode + * + * @return number The number of bytes appended to the encoded (8) + * + * Example: + * @code + * { + * // encoded buffer with be { .type = 1, .length = 8, .value = 0x0000000012345678 } + * CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + * ccnxCodecTlvEncoder_Initialize(encoder); + * ccnxCodecTlvEncoder_AppendUint64(encoder, 1, (uint64_t) 0x12345678); + * ccnxCodecTlvEncoder_Finalize(encoder); + * PARCBuffer *encoded = ccnxCodecTlvEncoder_CreateBuffer(encoder); + * ccnxCodecTlvEncoder_Destroy(&encoder); + * } + * @endcode + */ +size_t ccnxCodecTlvEncoder_AppendUint64(CCNxCodecTlvEncoder *encoder, uint16_t type, uint64_t value); + +/** + * Returns the current encoding buffer position + * + * This is useful if you need to backtrack to fill in a length you did not know before. + * + * @param [in] encoder The Tlv encoder object + * + * @return number The byte offset of the encode buffer + * + * Example: + * @code + * { + * // Creates {{ T=99, L=.., V=SequenceNumber }, {T=1, L=length, V={{T=2, L=..., V=name}, {T=3, L=..., V=address}}}} + * CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + * ccnxCodecTlvEncoder_Initialize(encoder); + * ccnxCodecTlvEncoder_AppendBuffer(encoder, 99, sequenceNumber); + * size_t offset = ccnxCodecTlvEncoder_Position(encoder); + * ccnxCodecTlvEncoder_AppendContainer(encoder, 1, 0); + * size_t length = 0; + * length += ccnxCodecTlvEncoder_AppendBuffer(encoder, 2, name); + * length += ccnxCodecTlvEncoder_AppendBuffer(encoder, 3, address); + * ccnxCodecTlvEncoder_SetContainerLength(encoder, offset, length); + * } + * @endcode + */ +size_t ccnxCodecTlvEncoder_Position(CCNxCodecTlvEncoder *encoder); + +/** + * Used to rewind and erase + * + * For example, you called ccnxCodecTlvEncoder_AppendContainer() then found out that + * the container was empty. If you rewind to just before you added the container, it + * is as if the container were never added. + * + * @param [in] position Must be no more than ccnxCodecTlvEncoder_Position() + * + * @return number The position after calling SetPosition + * + * Example: + * @code + * <#example#> + * @endcode + */ +size_t ccnxCodecTlvEncoder_SetPosition(CCNxCodecTlvEncoder *encoder, size_t position); + +/** + * Sets the length field of the container at the given offset + * + * User `ccnxCodecTlvEncoder_Position' before `ccnxCodecTlvEncoder_AppendContainer' to get the container's + * offset. You can then set that container's length with this function. + * + * @param [in] encoder An allocated CCNxCodecTlvEncoder + * @param [in] offset position of the container in the Tlv Encoder + * @param [in] length The container length to set + * + * Example: + * @code + * { + * // Creates {{ T=99, L=.., V=SequenceNumber }, {T=1, L=length, V={{T=2, L=..., V=name}, {T=3, L=..., V=address}}}} + * CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + * ccnxCodecTlvEncoder_Initialize(encoder); + * ccnxCodecTlvEncoder_AppendBuffer(encoder, 99, sequenceNumber); + * size_t offset = ccnxCodecTlvEncoder_Position(encoder); + * ccnxCodecTlvEncoder_AppendContainer(encoder, 1, 0); + * size_t length = 0; + * length += ccnxCodecTlvEncoder_AppendBuffer(encoder, 2, name); + * length += ccnxCodecTlvEncoder_AppendBuffer(encoder, 3, address); + * ccnxCodecTlvEncoder_SetContainerLength(encoder, offset, length); + * } + * @endcode + */ +void ccnxCodecTlvEncoder_SetContainerLength(CCNxCodecTlvEncoder *encoder, size_t offset, uint16_t length); + +/** + * Finalizes the encoding and returns the encoded buffer + * + * The buffer is ready for reading. In specific, it will truncate the buffer at the + * current position, setting the Limit to that location. This will cut off any + * writes to the buffer that have been "erased" by setting the position to an + * earlier location. + * + * @param [in] encoder An allocated CCNxCodecTlvEncoder + * + * Example: + * @code + * { + * CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + * ccnxCodecTlvEncoder_Initialize(encoder); + * ccnxCodecTlvEncoder_Append(encoder, 1, name); + * ccnxCodecTlvEncoder_Append(encoder, 2, address); + * ccnxCodecTlvEncoder_Finalize(encoder); + * PARCBuffer *encoded = ccnxCodecTlvEncoder_CreateBuffer(encoder); + * ccnxCodecTlvEncoder_Destroy(&encoder); + * } + * @endcode + */ +void ccnxCodecTlvEncoder_Finalize(CCNxCodecTlvEncoder *encoder); + +/** + * Creates a PARCBuffer from position 0 to the Limit. + * + * Returns a PARCBuffer from position 0 to the Limit. If the user has called + * ccnxCodecTlvEncoder_SetPosition() to rewind the buffer, the user should likely call + * ccnxCodecTlvEncoder_Finalize() to trim the Limit, otherwise there may be unexpected + * bytes at the end. + * + * The PARCBuffer representation is not the native form of the buffer and will result + * in a deep copy of the buffer. + * + * @param [in] encoder An allocated CCNxCodecTlvEncoder + * + * @return non-null An allocated PARCBuffer, use parcBuffer_Release() on it + * @return null An error. + * + * Example: + * @code + * { + * CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + * ccnxCodecTlvEncoder_Initialize(encoder); + * ccnxCodecTlvEncoder_Append(encoder, 1, name); + * ccnxCodecTlvEncoder_Append(encoder, 2, address); + * ccnxCodecTlvEncoder_Finalize(encoder); + * PARCBuffer *encoded = ccnxCodecTlvEncoder_CreateBuffer(encoder); + * ccnxCodecTlvEncoder_Destroy(&encoder); + * } + * @endcode + */ +PARCBuffer *ccnxCodecTlvEncoder_CreateBuffer(CCNxCodecTlvEncoder *encoder); + +/** + * Creates a vectored I/O representation of the encoder + * + * Returns a CCNxCodecNetworkBufferIoVec from position 0 to the Limit. If the user has called + * ccnxCodecTlvEncoder_SetPosition() to rewind the buffer, the user should likely call + * ccnxCodecTlvEncoder_Finalize() to trim the Limit, otherwise there may be unexpected + * bytes at the end. + * + * The CCNxCodecNetworkBufferIoVec is the native form of the memory and does not involve any copies. + * + * @param [in] encoder An allocated CCNxCodecTlvEncoder + * + * @return non-null An allocated CCNxCodecNetworkBufferIoVec, use CCNxCodecNetworkBufferIoVec_Release() on it + * @return null An error. + * + * Example: + * @code + * { + * CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + * ccnxCodecTlvEncoder_Initialize(encoder); + * ccnxCodecTlvEncoder_Append(encoder, 1, name); + * ccnxCodecTlvEncoder_Append(encoder, 2, address); + * ccnxCodecTlvEncoder_Finalize(encoder); + * CCNxCodecNetworkBufferIoVec *iovec = ccnxCodecTlvEncoder_CreateIoVec(encoder); + * ccnxCodecTlvEncoder_Destroy(&encoder); + * } + * @endcode + */ +CCNxCodecNetworkBufferIoVec *ccnxCodecTlvEncoder_CreateIoVec(CCNxCodecTlvEncoder *encoder); + +/** + * Marks the current position as the start of the signature + * + * @param [in] encoder An allocated CCNxCodecTlvEncoder + * + * Example: + * @code + * { + * CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + * ccnxCodecTlvEncoder_Append(encoder, 1, name); + * ccnxCodecTlvEncoder_MarkSignatureStart(encoder); + * ccnxCodecTlvEncoder_Append(encoder, 2, address); + * ccnxCodecTlvEncoder_MarkSignatureEnd(encoder); + * + * // The signature is calcualted over the TLV field of the "address" + * PARCSignature *sig = ccnxCodecTlvEncoder_ComputeSignature(encoder, signer); + * + * ccnxCodecTlvEncoder_Destroy(&encoder); + * } + * @endcode + */ +void ccnxCodecTlvEncoder_MarkSignatureStart(CCNxCodecTlvEncoder *encoder); + +/** + * Marks the current position as the end (non-inclusive) of the Signature + * Example: + * + * @param [in] encoder An allocated CCNxCodecTlvEncoder + * + * @code + * { + * CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + * ccnxCodecTlvEncoder_Append(encoder, 1, name); + * ccnxCodecTlvEncoder_MarkSignatureStart(encoder); + * ccnxCodecTlvEncoder_Append(encoder, 2, address); + * ccnxCodecTlvEncoder_MarkSignatureEnd(encoder); + * + * // The signature is calcualted over the TLV field of the "address" + * PARCSignature *sig = ccnxCodecTlvEncoder_ComputeSignature(encoder, signer); + * + * ccnxCodecTlvEncoder_Destroy(&encoder); + * } + * @endcode + */ +void ccnxCodecTlvEncoder_MarkSignatureEnd(CCNxCodecTlvEncoder *encoder); + +/** + * Computes the cryptographic signature over the designated area. + * If both a Start and End have not been set, function will assert + * + * + * @param [in] encoder An allocated CCNxCodecTlvEncoder + * + * @retval non-null An allocated PARCSignature + * @retval null An error + * + * Example: + * @code + * { + * CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + * ccnxCodecTlvEncoder_Append(encoder, 1, name); + * ccnxCodecTlvEncoder_MarkSignatureStart(encoder); + * ccnxCodecTlvEncoder_Append(encoder, 2, address); + * ccnxCodecTlvEncoder_MarkSignatureEnd(encoder); + * + * // The signature is calcualted over the TLV field of the "address" + * ccnxCodecTlvEncoder_SetSigner(signer); + * PARCSignature *sig = ccnxCodecTlvEncoder_ComputeSignature(encoder); + * + * ccnxCodecTlvEncoder_Destroy(&encoder); + * } + * @endcode + */ +PARCSignature *ccnxCodecTlvEncoder_ComputeSignature(CCNxCodecTlvEncoder *encoder); + +/** + * Puts a uint8_t at the specified position. + * + * Does not modify the current position of the Tlv Encoder + * + * @param [in] encoder An allocated CCNxCodecTlvEncoder + * + * @return number The number of bytes put + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +size_t ccnxCodecTlvEncoder_PutUint8(CCNxCodecTlvEncoder *encoder, size_t offset, uint8_t value); + +/** + * Puts a uint16_t at the specified position. + * + * Does not modify the current position of the Tlv Encoder + * + * @param [in] encoder An allocated CCNxCodecTlvEncoder + * + * @return number The number of bytes put + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +size_t ccnxCodecTlvEncoder_PutUint16(CCNxCodecTlvEncoder *encoder, size_t offset, uint16_t value); + +/** + * Writes an array to the current position. No "TL" container is written. + * + * Unlike ccnxCodecTlvEncoder_AppendArray, this does not append a Type and Length container for the array. + * + * @param [in] encoder An allocated CCNxCodecTlvEncoder + * @param [in] length The number of bytes from array to write + * @param [in] array The source array + * + * @return number The number of bytes appended to the encoder + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +size_t ccnxCodecTlvEncoder_AppendRawArray(CCNxCodecTlvEncoder *encoder, size_t length, uint8_t *array); + + +/** + * Determines if the TLV Encoder has an error condition set + * + * <#Paragraphs Of Explanation#> + * + * @param [in] encoder An allocated CCNxCodecTlvEncoder + * + * @retval true An error condition is set + * @retval false No error condition is set + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool ccnxCodecTlvEncoder_HasError(const CCNxCodecTlvEncoder *encoder); + +/** + * Sets an error condition. Only one error condition may be set. + * + * Stores a reference counted copy of the CCNxCodecError. If an error is already set, + * this function returns false and does not store a reference to the error. The previous error + * stays as the current error. + * + * @param [in] encoder An allocated CCNxCodecTlvEncoder + * + * @return true Error condition set + * @return false Error already set, you must clear it first + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool ccnxCodecTlvEncoder_SetError(CCNxCodecTlvEncoder *encoder, CCNxCodecError *error); + +/** + * Clears the error condition, if any + * + * <#Paragraphs Of Explanation#> + * + * @param [in] encoder An allocated CCNxCodecTlvEncoder + * + * Example: + * @code + * <#example#> + * @endcode + */ +void ccnxCodecTlvEncoder_ClearError(CCNxCodecTlvEncoder *encoder); + +/** + * Retrieves the error message + * + * Retrieves the error condition, if any. If no error is set, will return NULL. + * + * @param [in] encoder An allocated CCNxCodecTlvEncoder + * + * @return non-null The error condition set + * @return null No error condition is set + * + * Example: + * @code + * <#example#> + * @endcode + */ +CCNxCodecError *ccnxCodecTlvEncoder_GetError(const CCNxCodecTlvEncoder *encoder); + + +/** + * Associates a signer with the encoder for producing signatures or MACs + * + * Stores a reference counted copy of the signer. The reference will be released with the + * CCNxCodecTlvEncoder is released or if this function is called multiple times. + * + * It is allowed to set a NULL singer. + * + * @param [in] encoder An allocated CCNxCodecTlvEncoder + * @param [in] singer Assocaited a PARCSigner with the encoder, storing a reference to it + * + * Example: + * @code + * <#example#> + * @endcode + */ +void ccnxCodecTlvEncoder_SetSigner(CCNxCodecTlvEncoder *encoder, PARCSigner *signer); + +/** + * Returns the PARCSigner associated with the encoder, if any + * + * <#Paragraphs Of Explanation#> + * + * @param [in] encoder An allocated CCNxCodecTlvEncoder + * + * @return non-null The signer assocated with the encoder + * @return null There is no singer associated with the encoder + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCSigner *ccnxCodecTlvEncoder_GetSigner(const CCNxCodecTlvEncoder *encoder); + +/** + * Appends a TLV container holding the value as a VarInt + * + * A VarInt may be 1 to 8 bytes long. It is interpreted as an unsigned + * integer in network byte order. The value of "0" is encoded as a single + * byte of "0". + * + * @param [in] decoder The TLV decoder + * @param [in] type The Type value to use for the container + * @param [in] value The value of the varint + * + * @return number The number of bytes appended + * + * Example: + * @code + * { + * uint64_t value = 0x0000000000102300 + * size_t length = ccnxCodecTlvEncoder_AppendVarInt(encoder, 12, value); + * // length = 7 + * // appedned { 0x00, 0x0C, 0x00, 0x03, 0x10, 0x23, 0x00 } + * } + * @endcode + */ +size_t ccnxCodecTlvEncoder_AppendVarInt(CCNxCodecTlvEncoder *encoder, uint16_t type, uint64_t value); + +#endif // libccnx_ccnx_TlvEncoder_h diff --git a/libccnx-common/ccnx/common/codec/ccnxCodec_TlvPacket.c b/libccnx-common/ccnx/common/codec/ccnxCodec_TlvPacket.c new file mode 100755 index 00000000..9df01867 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/ccnxCodec_TlvPacket.c @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +#include <config.h> +#include <stdio.h> +#include <LongBow/runtime.h> +#include <arpa/inet.h> +#include <ccnx/common/codec/ccnxCodec_TlvPacket.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_Types.h> + +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_PacketDecoder.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_PacketEncoder.h> + +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_FixedHeader.h> + +static CCNxTlvDictionary * +_decodeV1(PARCBuffer *packetBuffer) +{ + CCNxTlvDictionary *packetDictionary = NULL; + + CCNxCodecSchemaV1Types_PacketType packetType = (CCNxCodecSchemaV1Types_PacketType) parcBuffer_GetAtIndex(packetBuffer, 1); + + switch (packetType) { + case CCNxCodecSchemaV1Types_PacketType_Interest: + packetDictionary = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + break; + + case CCNxCodecSchemaV1Types_PacketType_ContentObject: + packetDictionary = ccnxCodecSchemaV1TlvDictionary_CreateContentObject(); + break; + + case CCNxCodecSchemaV1Types_PacketType_InterestReturn: + // not implemented yet + break; + + case CCNxCodecSchemaV1Types_PacketType_Control: + packetDictionary = ccnxCodecSchemaV1TlvDictionary_CreateControl(); + break; + + default: + // unknown type + break; + } + + if (packetDictionary) { + // The packetBuffer may be padded or have extraneous content after the CCNx message. + // Ensure that the buffer limit reflects the CCNx packet length as the decoder uses + // the that limit, not the packetLength from the header, to determine when to stop parsing. + size_t packetBufferLength = ccnxCodecTlvPacket_GetPacketLength(packetBuffer); + assertTrue(packetBufferLength <= parcBuffer_Remaining(packetBuffer), "Short packet buffer"); + parcBuffer_SetLimit(packetBuffer, packetBufferLength); + bool success = ccnxCodecSchemaV1PacketDecoder_BufferDecode(packetBuffer, packetDictionary); + if (!success) { + ccnxTlvDictionary_Release(&packetDictionary); + } + } + return packetDictionary; +} + +CCNxTlvDictionary * +ccnxCodecTlvPacket_Decode(PARCBuffer *packetBuffer) +{ + return _decodeV1(packetBuffer); +} + +bool +ccnxCodecTlvPacket_BufferDecode(PARCBuffer *packetBuffer, CCNxTlvDictionary *packetDictionary) +{ + // Determine the version from the first byte of the buffer + uint8_t version = parcBuffer_GetAtIndex(packetBuffer, 0); + + // The packetBuffer may be padded or have extraneous content after the CCNx message. + // Ensure that the buffer limit reflects the CCNx packet length as the decoder uses + // the that limit, not the packetLength from the header, to determine when to stop parsing. + size_t packetBufferLength = ccnxCodecTlvPacket_GetPacketLength(packetBuffer); + assertTrue(packetBufferLength <= parcBuffer_Remaining(packetBuffer), "Short packet buffer"); + parcBuffer_SetLimit(packetBuffer, packetBufferLength); + + bool success = false; + switch (version) { + case CCNxTlvDictionary_SchemaVersion_V1: + success = ccnxCodecSchemaV1PacketDecoder_BufferDecode(packetBuffer, packetDictionary); + break; + + default: + // will return false + break; + } + + return success; +} + +/* + * We don't have an iovec based decoder yet, so linearize the memory and use a PARCBuffer + * See case 903. + */ +bool +ccnxCodecTlvPacket_IoVecDecode(CCNxCodecNetworkBufferIoVec *vec, CCNxTlvDictionary *packetDictionary) +{ + size_t iovcnt = ccnxCodecNetworkBufferIoVec_GetCount(vec); + const struct iovec *array = ccnxCodecNetworkBufferIoVec_GetArray(vec); + + PARCBuffer *buffer = NULL; + if (iovcnt == 1) { + buffer = parcBuffer_Wrap(array[0].iov_base, array[0].iov_len, 0, array[0].iov_len); + } else if (iovcnt > 1) { + // figure out total size, then linearize it + size_t totalbytes = 0; + for (int i = 0; i < iovcnt; i++) { + totalbytes += array[i].iov_len; + } + + buffer = parcBuffer_Allocate(totalbytes); + for (int i = 0; i < iovcnt; i++) { + parcBuffer_PutArray(buffer, array[i].iov_len, array[i].iov_base); + } + + parcBuffer_Flip(buffer); + } else { + return false; + } + + bool success = ccnxCodecTlvPacket_BufferDecode(buffer, packetDictionary); + parcBuffer_Release(&buffer); + return success; +} + +CCNxCodecNetworkBufferIoVec * +ccnxCodecTlvPacket_DictionaryEncode(CCNxTlvDictionary *packetDictionary, PARCSigner *signer) +{ + CCNxTlvDictionary_SchemaVersion version = ccnxTlvDictionary_GetSchemaVersion(packetDictionary); + + CCNxCodecNetworkBufferIoVec *iovec = NULL; + switch (version) { + case CCNxTlvDictionary_SchemaVersion_V1: + iovec = ccnxCodecSchemaV1PacketEncoder_DictionaryEncode(packetDictionary, signer); + break; + + default: + // will return NULL + break; + } + return iovec; +} + +size_t +ccnxCodecTlvPacket_GetPacketLength(PARCBuffer *packetBuffer) +{ + size_t length = 0; + + // Determine the version from the first byte of the buffer + uint8_t *header = parcBuffer_Overlay(packetBuffer, 0); + + switch (header[0]) { + case CCNxTlvDictionary_SchemaVersion_V1: { // V1 - from metis_TlvSchemaV1.c:_totalPacketLength + CCNxCodecSchemaV1FixedHeader *headerV1 = (CCNxCodecSchemaV1FixedHeader *) header; + length = htons(headerV1->packetLength); + break; + } + default: + break; + } + + return length; +} + +// When new versions are created they need to be incorporated here so enough header information +// can be read to determine how to proceed. +size_t +ccnxCodecTlvPacket_MinimalHeaderLength() +{ + size_t minimumHeaderLength; + minimumHeaderLength = sizeof(CCNxCodecSchemaV1FixedHeader); + return minimumHeaderLength; +} diff --git a/libccnx-common/ccnx/common/codec/ccnxCodec_TlvPacket.h b/libccnx-common/ccnx/common/codec/ccnxCodec_TlvPacket.h new file mode 100755 index 00000000..c2690b9f --- /dev/null +++ b/libccnx-common/ccnx/common/codec/ccnxCodec_TlvPacket.h @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file rta_TlvPacket.h + * @brief Encode and decode a packet using the TLV 1.1 codec + * + * Will choose the appropriate schema based on the packet version + * + */ + +#ifndef TransportRTA_rta_TlvPacketDecoder_h +#define TransportRTA_rta_TlvPacketDecoder_h + +#include <parc/algol/parc_Buffer.h> +#include <parc/security/parc_Signer.h> + +#include <ccnx/common/codec/ccnxCodec_NetworkBuffer.h> + +#include <ccnx/common/internal/ccnx_TlvDictionary.h> + +/** + * Decodes a packet in to a dictionary + * + * The buffer must point to byte 0 of the FixedHeader. It may extend beyond the + * end of the packet. + * + * @param [in] packetBuffer The wire format representation of a packet + * + * @retval non-null An allocated dictionary + * @retval null An error + * + * Example: + * @code + * <#example#> + * @endcode + */ +CCNxTlvDictionary *ccnxCodecTlvPacket_Decode(PARCBuffer *packetBuffer); + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @retval <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool ccnxCodecTlvPacket_BufferDecode(PARCBuffer *packetBuffer, CCNxTlvDictionary *packetDictionary); + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @retval <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool ccnxCodecTlvPacket_IoVecDecode(CCNxCodecNetworkBufferIoVec *vec, CCNxTlvDictionary *packetDictionary); + + +/** + * Encode the packetDictionary to wire format + * + * Will only use the PacketType from FixedHeader in the dictionary, if provided. The packet Version is based + * on the dictionary schema version, and the length fields of the fixed header are calculated. + * If the FixedHeaderDictionary is not provided, the + * PacketType is inferred from the type of CCNx message. + * + * The signer is not stored beyond the call to DictionaryEncode. + * If the dictionary already has a ValidationAlg and ValidationPayload, those are used, not the Signer. + * Otherwise, if the signer is not null, it is used to sign the wire format. + * + * @param [in] packetDictionary The dictionary representation of the packet to encode + * @param [in] signer If not NULL will be used to sign the wire format + * + * @retval non-null An IoVec that can be written to the network + * @retval null an error + * + * Example: + * @code + * <#example#> + * @endcode + */ +CCNxCodecNetworkBufferIoVec *ccnxCodecTlvPacket_DictionaryEncode(CCNxTlvDictionary *packetDictionary, PARCSigner *signer); + +/** + * Return the length of the wire format packet based on information in the header + * + * @param [in] packetBuffer a PARCBuffer containing, at least, the wire format header + * @return length of the message in bytes + * + * Example: + * @code + * <#example#> + * @endcode + */ +size_t ccnxCodecTlvPacket_GetPacketLength(PARCBuffer *packetBuffer); + +/** + * Return the minimal header that must be read to determine type and packet size + * + * @return length of the header which must be read in bytes + * + * Example: + * @code + * <#example#> + * @endcode + */ +size_t ccnxCodecTlvPacket_MinimalHeaderLength(); + +#endif // TransportRTA_rta_TlvPacketDecoder_h diff --git a/libccnx-common/ccnx/common/codec/ccnxCodec_TlvUtilities.c b/libccnx-common/ccnx/common/codec/ccnxCodec_TlvUtilities.c new file mode 100644 index 00000000..4b90abf1 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/ccnxCodec_TlvUtilities.c @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ +#include <config.h> +#include <stdio.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_NameCodec.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_HashCodec.h> +#include <ccnx/common/codec/ccnxCodec_TlvUtilities.h> +#include <LongBow/runtime.h> + +bool +ccnxCodecTlvUtilities_DecodeContainer(CCNxCodecTlvDecoder *decoder, CCNxTlvDictionary *packetDictionary, bool (*typeDecoder)(CCNxCodecTlvDecoder *decoder, CCNxTlvDictionary *packetDictionary, uint16_t type, uint16_t length)) +{ + while (ccnxCodecTlvDecoder_EnsureRemaining(decoder, 4)) { + uint16_t type = ccnxCodecTlvDecoder_GetType(decoder); + uint16_t length = ccnxCodecTlvDecoder_GetLength(decoder); + + if (ccnxCodecTlvDecoder_EnsureRemaining(decoder, length)) { + bool success = typeDecoder(decoder, packetDictionary, type, length); + if (!success) { + return false; + } + } else { + // overflow! The TLV length goes beyond the end of the container + return false; + } + } + + // Make sure we used up the whole buffer. If we're at the end, + // then it was a successful decode, otherwise something is wrong. + return ccnxCodecTlvDecoder_IsEmpty(decoder); +} + +bool +ccnxCodecTlvUtilities_DecodeSubcontainer(CCNxCodecTlvDecoder *decoder, CCNxTlvDictionary *packetDictionary, uint16_t key, uint16_t length, + bool (*subcontainerDecoder)(CCNxCodecTlvDecoder *, CCNxTlvDictionary *)) +{ + bool success = false; + CCNxCodecTlvDecoder *innerDecoder = ccnxCodecTlvDecoder_GetContainer(decoder, length); + if (innerDecoder) { + success = subcontainerDecoder(innerDecoder, packetDictionary); + ccnxCodecTlvDecoder_Destroy(&innerDecoder); + } + return success; +} + +bool +ccnxCodecTlvUtilities_PutAsInteger(CCNxCodecTlvDecoder *decoder, CCNxTlvDictionary *packetDictionary, uint16_t type, uint16_t length, int dictionaryKey) +{ + uint64_t value; + if (ccnxCodecTlvDecoder_GetVarInt(decoder, length, &value)) { + return ccnxTlvDictionary_PutInteger(packetDictionary, dictionaryKey, value); + } + return false; +} + +bool +ccnxCodecTlvUtilities_PutAsName(CCNxCodecTlvDecoder *decoder, CCNxTlvDictionary *packetDictionary, uint16_t type, uint16_t length, int arrayKey) +{ + bool success = false; + CCNxName *name = ccnxCodecSchemaV1NameCodec_DecodeValue(decoder, length); + success = ccnxTlvDictionary_PutName(packetDictionary, arrayKey, name); + ccnxName_Release(&name); + return success; +} + +bool +ccnxCodecTlvUtilities_PutAsBuffer(CCNxCodecTlvDecoder *decoder, CCNxTlvDictionary *packetDictionary, uint16_t type, uint16_t length, int arrayKey) +{ + PARCBuffer *buffer = ccnxCodecTlvDecoder_GetValue(decoder, length); + bool success = ccnxTlvDictionary_PutBuffer(packetDictionary, arrayKey, buffer); + parcBuffer_Release(&buffer); + return success; +} + +bool +ccnxCodecTlvUtilities_PutAsHash(CCNxCodecTlvDecoder *decoder, CCNxTlvDictionary *packetDictionary, uint16_t type, uint16_t length, int arrayKey) +{ + PARCCryptoHash *hash = ccnxCodecSchemaV1HashCodec_DecodeValue(decoder, length); + bool success = false; + if (hash != NULL) { + success = ccnxTlvDictionary_PutObject(packetDictionary, arrayKey, (const PARCObject *) hash); + parcCryptoHash_Release(&hash); + } + return success; +} + +bool +ccnxCodecTlvUtilities_PutAsListBuffer(CCNxCodecTlvDecoder *decoder, CCNxTlvDictionary *packetDictionary, uint16_t type, uint16_t length, int listKey) +{ + PARCBuffer *buffer = ccnxCodecTlvDecoder_GetValue(decoder, length); + bool success = ccnxTlvDictionary_PutListBuffer(packetDictionary, listKey, type, buffer); + parcBuffer_Release(&buffer); + return success; +} + +ssize_t +ccnxCodecTlvUtilities_NestedEncode(CCNxCodecTlvEncoder *outerEncoder, CCNxTlvDictionary *packetDictionary, uint32_t nestedType, + ssize_t (*nestedEncoderFunction)(CCNxCodecTlvEncoder *protoInfoEncoder, CCNxTlvDictionary *packetDictionary)) +{ + size_t startPosition = ccnxCodecTlvEncoder_Position(outerEncoder); + + ccnxCodecTlvEncoder_AppendContainer(outerEncoder, nestedType, 0); + ssize_t nestedLength = nestedEncoderFunction(outerEncoder, packetDictionary); + if (nestedLength > 0) { + ccnxCodecTlvEncoder_SetContainerLength(outerEncoder, startPosition, nestedLength); + } else { + // rewind the container + ccnxCodecTlvEncoder_SetPosition(outerEncoder, startPosition); + return nestedLength; + } + + size_t endPosition = ccnxCodecTlvEncoder_Position(outerEncoder); + return endPosition - startPosition; +} + +ssize_t +ccnxCodecTlvUtilities_EncodeCustomList(CCNxCodecTlvEncoder *encoder, CCNxTlvDictionary *packetDictionary, int listKey) +{ + ssize_t length = 0; + + size_t size = ccnxTlvDictionary_ListSize(packetDictionary, listKey); + for (int i = 0; i < size; i++) { + PARCBuffer *buffer; + uint32_t key; + + ssize_t innerLength = -1; + bool success = ccnxTlvDictionary_ListGetByPosition(packetDictionary, listKey, i, &buffer, &key); + if (success) { + innerLength = ccnxCodecTlvEncoder_AppendBuffer(encoder, key, buffer); + } + + if (innerLength < 0) { + return -1; + } + + length += innerLength; + } + + return length; +} + +bool +ccnxCodecTlvUtilities_GetVarInt(PARCBuffer *input, size_t length, uint64_t *output) +{ + assertNotNull(input, "Parameter input must be non-null"); + assertNotNull(output, "Parameter output must be non-null"); + + bool success = false; + if (length >= 1 && length <= 8 && parcBuffer_Remaining(input) >= length) { + uint64_t value = 0; + for (int i = 0; i < length; i++) { + value = value << 8 | parcBuffer_GetUint8(input); + } + *output = value; + success = true; + } + return success; +} diff --git a/libccnx-common/ccnx/common/codec/ccnxCodec_TlvUtilities.h b/libccnx-common/ccnx/common/codec/ccnxCodec_TlvUtilities.h new file mode 100644 index 00000000..81866701 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/ccnxCodec_TlvUtilities.h @@ -0,0 +1,441 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file ccnxCodec_TlvUtilities.h + * @brief Utility functions common to all the codecs + * + * <#Detailed Description#> + * + */ +#ifndef TransportRTA_ccnxCodec_TlvUtilities_h +#define TransportRTA_ccnxCodec_TlvUtilities_h + +#include <stdbool.h> +#include <stdint.h> +#include <ccnx/common/internal/ccnx_TlvDictionary.h> +#include <ccnx/common/codec/ccnxCodec_TlvEncoder.h> +#include <ccnx/common/codec/ccnxCodec_TlvDecoder.h> + +/** + * Decodes a list of TLV entries + * + * The decoder should point to the first byte of a "type". This function will iterate over all the TLVs + * and call the user function 'typeDecoder' for each type-length. + * + * It is the responsibility of typeDecoder to advance the decoder by 'length' bytes. It should return false + * if it does not consume exactly 'length' bytes. + * + * The function will proceed until it can no longer parse a TLV header (4 bytes). If the function consumes all + * the bytes in the decoder without error, it will return true. If it encounters an error from 'typeDecoder' it + * will return false at that point. If there is an underflow (i.e. 1, 2, or 3 bytes) left in the decoder at the end + * it will return false. + * + * @param [in] decoder The TLV decoder that should point to the start of the TLV list + * @param [in] packetDictionary The dictionary to use to store packet fields + * @param [in] typeDecoder the user-supplied function to call for each TLV found in the container + * + * @return true There were no errors returned by 'typeDecoder' and we consumed the entire decoder buffer + * @return false There was an error or we did not consume the entire decoder buffer. + * + * Example: + * @code + * { + * static bool + * testTypeDecoder(CCNxCodecTlvDecoder *decoder, CCNxTlvDictionary *packetDictionary, uint16_t type, uint16_t length) + * { + * switch (type) { + * case 0x000C: // fallthrough + * case 0x000D: + * ccnxCodecTlvDecoder_Advance(decoder, length); + * return true; + * default: + * return false; + * } + * } + * + * void foo(void) + * { + * // A list of 2 TLV containers (types 0x000C and 0x000D) + * uint8_t metadataContainer[] = { + * 0x00, 0x0C, 0x00, 0x01, // Object Type, length = 1 + * 0x04, // LINK + * 0x00, 0x0D, 0x00, 8, // Creation Time + * 0x00, 0x00, 0x01, 0x43, // 1,388,534,400,000 msec + * 0x4B, 0x19, 0x84, 0x00, + * }; + * + * PARCBuffer *buffer = parcBuffer_Wrap(metadataContainer, sizeof(metadataContainer), 0, sizeof(metadataContainer) ); + * + * // now decode that snippit + * CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(buffer); + * CCNxTlvDictionary *dictionary = ccnxTlvDictionary_Create(10,10); + * + * bool success = ccnxCodecTlvUtilities_DecodeContainer(decoder, dictionary, testTypeDecoder); + * + * ccnxTlvDictionary_Release(&dictionary); + * ccnxCodecTlvDecoder_Destroy(&decoder); + * parcBuffer_Release(&buffer); + * + * assertTrue(success, "The TLV types were known to us"); + * } + * @endcode + */ +bool ccnxCodecTlvUtilities_DecodeContainer(CCNxCodecTlvDecoder *decoder, CCNxTlvDictionary *packetDictionary, bool (*typeDecoder)(CCNxCodecTlvDecoder *decoder, CCNxTlvDictionary *packetDictionary, uint16_t type, uint16_t length)); + +/** + * Creates an inner decoder of for decoding a subcontainer + * + * The decoder should point at the first byte of the "value", which is known to be a subcontainer listing other + * TLVs. This function will create an inner decoder and then call 'ccnxCodecTlvUtilities_DecodeContainer' with it to + * decode the inner TLVs. + * + * @param [in] decoder The decoder that points to the fist byte of a list of TLVs. + * @param [in] packetDictionary Where to put the results + * @param [in] key NOT USED + * @param [in] length The length of the subcontainer. The inner decoder will end after this may bytes. + * @param [in] subcontainerDecoder The function to pass to 'ccnxCodecTlvUtilities_DecodeContainer' for the inner decoder + * + * @return true There were no errors and consumed 'length' bytes + * @return false An error or did not consume 'length' bytes + * + * Example: + * @code + * { + * // The KeyLocator field is known to be a subcontainer containing its own TLV fields. When we encounter that + * // TLV type, we parse the 'value' of it as a subcontainer + * // + * static bool + * rtaTlvSchemaV0NameAuth_DecodeType(CCNxCodecTlvDecoder *decoder, CCNxTlvDictionary *packetDictionary, uint16_t type, uint16_t length) + * { + * bool success = false; + * switch (type) { + * case CCNxCodecSchemaV0_NameAuthKeys_KeyLocator: + * success = ccnxCodecTlvUtilities_DecodeSubcontainer(decoder, packetDictionary, type, length, rtaTlvSchemaV0KeyLocator_Decode); + * break; + * + * case CCNxCodecSchemaV0_NameAuthKeys_CryptoSuite: + * success = ccnxCodecTlvUtilities_PutAsBuffer(decoder, packetDictionary, type, length, CCNxCodecSchemaV0TlvDictionary_ContentObjectFastArray_CRYPTO_SUITE); + * break; + * + * case CCNxCodecSchemaV0_NameAuthKeys_KeyId: + * success = ccnxCodecTlvUtilities_PutAsBuffer(decoder, packetDictionary, type, length, CCNxCodecSchemaV0TlvDictionary_ContentObjectFastArray_KEYID); + * break; + * + * default: + * // if we do not know the TLV type, put it in this container's unknown list + * success = ccnxCodecTlvUtilities_PutAsListBuffer(decoder, packetDictionary, type, length, CCNxCodecSchemaV0TlvDictionary_ContentObjectLists_NAMEAUTH_LIST); + * break; + * } + * return success; + * } + * @endcode + */ +bool +ccnxCodecTlvUtilities_DecodeSubcontainer(CCNxCodecTlvDecoder *decoder, CCNxTlvDictionary *packetDictionary, uint16_t key, uint16_t length, + bool (*subcontainerDecoder)(CCNxCodecTlvDecoder *, CCNxTlvDictionary *)); + +/** + * Decodes 'length' bytes from the decoder and puts it in the dictionary + * + * Reads the next 'length' bytes from the decoder and wraps it in a PARCBuffer. The buffer is saved in the packetDictionary + * under the key 'arrayKey'. + * + * It is an error if there are not 'length' bytes remaining in the decoder. + * + * @param [in] decoder The input to read + * @param [in] packetDictionary The output dictionary to save the buffer in + * @param [in] key The TLV key of the value being read (NOT USED) + * @param [in] length The byte length to read + * @param [in] dictionaryKey The key to use in the packetDictionary + * + * @return true 'length' bytes were read and saved in the packetDictionary + * @return false An error + * + * Example: + * @code + * { + * // A list of 2 TLV containers (types 0x000C and 0x000D) + * uint8_t metadataContainer[] = { + * 0x00, 0x0C, 0x00, 0x01, // Object Type, length = 1 + * 0x04, // LINK + * 0x00, 0x0D, 0x00, 8, // Creation Time + * 0x00, 0x00, 0x01, 0x43, // 1,388,534,400,000 msec + * 0x4B, 0x19, 0x84, 0x00, + * }; + * + * PARCBuffer *buffer = parcBuffer_Wrap(metadataContainer, sizeof(metadataContainer), 0, sizeof(metadataContainer) ); + * + * // now decode that snippit + * CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(buffer); + * CCNxTlvDictionary *dictionary = ccnxTlvDictionary_Create(10,10); + * + * uint16_t tlvtype = ccnxCodecTlvDecoder_GetType(decoder); + * uint16_t tlvlength = ccnxCodecTlvDecoder_GetLength(decoder); + * + * // The buffer will contain the one byte 0x04. + * bool success = ccnxCodecTlvUtilities_PutAsBuffer(decoder, dictionary, tlvtype, tlvlength, CCNxCodecSchemaV0TlvDictionary_ContentObjectFastArray_OBJ_TYPE); + * + * ccnxTlvDictionary_Release(&dictionary); + * ccnxCodecTlvDecoder_Destroy(&decoder); + * parcBuffer_Release(&buffer); + * + * assertTrue(success, "There was an unknown TLV at position %zu", ccnxCodecTlvDecoder_Position(decoder)); + * } + * @endcode + */ +bool +ccnxCodecTlvUtilities_PutAsBuffer(CCNxCodecTlvDecoder *decoder, CCNxTlvDictionary *packetDictionary, uint16_t type, uint16_t length, int dictionaryKey); + +/** + * Decodes a `PARCCryptoHash` value of 'length' bytes from the decoder and puts it in the dictionary. + * + * It is an error if there are not 'length' bytes remaining in the decoder. + * + * @param [in] decoder The input to read + * @param [in] packetDictionary The output dictionary to save the buffer in + * @param [in] key The TLV key of the value being read (NOT USED) + * @param [in] length The byte length to read + * @param [in] dictionaryKey The key to use in the packetDictionary + * + * @return true 'length' bytes were read and saved in the packetDictionary + * @return false An error + * + * Example: + * @code + * { + * // A list of 2 TLV containers (types 0x000C and 0x000D) + * uint8_t hashContainer[] = { + * 0x00, 0x01, 0x00, 0x20, // SHA256 hash, length = 0x20 + * 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + * 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + * 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + * 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + * }; + * + * PARCBuffer *buffer = parcBuffer_Wrap(hashContainer, sizeof(hashContainer), 0, sizeof(hashContainer) ); + * + * // now decode that snippit + * CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(buffer); + * CCNxTlvDictionary *dictionary = ccnxTlvDictionary_Create(10,10); + * + * uint16_t tlvtype = ccnxCodecTlvDecoder_GetType(decoder); + * uint16_t tlvlength = ccnxCodecTlvDecoder_GetLength(decoder); + * + * bool success = ccnxCodecTlvUtilities_PutAsHash(decoder, dictionary, tlvtype, tlvlength, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_OBJHASH_RESTRICTION); + * + * ccnxTlvDictionary_Release(&dictionary); + * ccnxCodecTlvDecoder_Destroy(&decoder); + * parcBuffer_Release(&buffer); + * } + * @endcode + */ +bool +ccnxCodecTlvUtilities_PutAsHash(CCNxCodecTlvDecoder *decoder, CCNxTlvDictionary *packetDictionary, uint16_t type, uint16_t length, int dictionaryKey); + +/** + * Decodes the value as a VarInt and saves it as an Integer in the Dictionary + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool +ccnxCodecTlvUtilities_PutAsInteger(CCNxCodecTlvDecoder *decoder, CCNxTlvDictionary *packetDictionary, uint16_t type, uint16_t length, int dictionaryKey); + + +/** + * Decodes 'length' bytes from the decoder and puts it in the dictionary as a CCNxName + * + * Reads the next 'length' bytes from the decoder and wraps it in a CCNxName. The name is saved in the packetDictionary + * under the key 'arrayKey'. + * + * It is an error if there are not 'length' bytes remaining in the decoder. + * + * @param [in] decoder The input to read + * @param [in] packetDictionary The output dictionary to save the name in + * @param [in] key The TLV key of the value being read (NOT USED) + * @param [in] length The byte length to read + * @param [in] dictionaryKey The key to use in the packetDictionary + * + * @return true 'length' bytes were read and saved in the packetDictionary + * @return false An error + * + * Example: + * @code + * { + * // A list of 2 TLV containers (types 0x000C and 0x000D) + * uint8_t metadataContainer[] = { + * 0x00, 0x00, 0x00, 9, // type = name, length = 9 + * 0x00, 0x02, 0x00, 5, // type = binary, length = 5 + * 'h', 'e', 'l', 'l', // "hello" + * 'o', + * }; + * + * PARCBuffer *buffer = parcBuffer_Wrap(metadataContainer, sizeof(metadataContainer), 0, sizeof(metadataContainer) ); + * + * // now decode that snippit + * CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(buffer); + * CCNxTlvDictionary *dictionary = ccnxTlvDictionary_Create(10,10); + * + * uint16_t tlvtype = ccnxCodecTlvDecoder_GetType(decoder); + * uint16_t tlvlength = ccnxCodecTlvDecoder_GetLength(decoder); + * + * // Saves "lci:/hello" + * bool success = ccnxCodecTlvUtilities_PutAsName(decoder, dictionary, tlvtype, tlvlength, CCNxCodecSchemaV0TlvDictionary_ContentObjectFastArray_NAME); + * + * ccnxTlvDictionary_Release(&dictionary); + * ccnxCodecTlvDecoder_Destroy(&decoder); + * parcBuffer_Release(&buffer); + * + * assertTrue(success, "The Name failed to decode or some other error"); + * } + * @endcode + */ +bool +ccnxCodecTlvUtilities_PutAsName(CCNxCodecTlvDecoder *decoder, CCNxTlvDictionary *packetDictionary, uint16_t type, uint16_t length, int arrayKey); + +/** + * Reads 'length' bytes from the decoder and appends a PARCBuffer to a list in packetDictionary + * + * Saves a buffer as part of a List in the packet dictionary. This is primarily used for unknown TLV types that + * do not have a specific decoder. + * + * @param [in] decoder The decoder to read + * @param [in] packetDictionary The dictionary to append the buffer in + * @param [in] type The TLV type of the buffer (saved as part of the list entry) + * @param [in] length The length to wrap in the buffer + * @param [in] listKey The list key in packetDictionary + * + * @return true Success + * @return false Failure or error + * + * Example: + * @code + * { + * // If we exhaust all the known keys in the Name Authenticator, the default case will save the TLV in + * // the container's list in the packet dictionary. + * // + * static bool + * rtaTlvSchemaV0NameAuth_DecodeType(CCNxCodecTlvDecoder *decoder, CCNxTlvDictionary *packetDictionary, uint16_t type, uint16_t length) + * { + * bool success = false; + * switch (type) { + * case CCNxCodecSchemaV0_NameAuthKeys_KeyLocator: + * success = ccnxCodecTlvUtilities_DecodeSubcontainer(decoder, packetDictionary, type, length, rtaTlvSchemaV0KeyLocator_Decode); + * break; + * + * case CCNxCodecSchemaV0_NameAuthKeys_CryptoSuite: + * success = ccnxCodecTlvUtilities_PutAsBuffer(decoder, packetDictionary, type, length, CCNxCodecSchemaV0TlvDictionary_ContentObjectFastArray_CRYPTO_SUITE); + * break; + * + * case CCNxCodecSchemaV0_NameAuthKeys_KeyId: + * success = ccnxCodecTlvUtilities_PutAsBuffer(decoder, packetDictionary, type, length, CCNxCodecSchemaV0TlvDictionary_ContentObjectFastArray_KEYID); + * break; + * + * default: + * // if we do not know the TLV type, put it in this container's unknown list + * success = ccnxCodecTlvUtilities_PutAsListBuffer(decoder, packetDictionary, type, length, CCNxCodecSchemaV0TlvDictionary_ContentObjectLists_NAMEAUTH_LIST); + * break; + * } + * return success; + * } + * @endcode + */ +bool +ccnxCodecTlvUtilities_PutAsListBuffer(CCNxCodecTlvDecoder *decoder, CCNxTlvDictionary *packetDictionary, uint16_t type, uint16_t length, int listKey); + +/** + * Encodes a nested TLV container (the opposite of ccnxCodecTlvUtilities_DecodeSubcontainer) + * + * Appends a TLV header (4 bytes) to the encoder using 'nestedType' as the TLV type. It then calls 'nestedEncoderFunction' to + * encode the 'value' of the container. If 'nestedEncoderFunction' returns positive bytes it will go back and fill in the proper TLV length. + * If 'nestedEncoderFunction' returns 0 or negative bytes, it rewinds the encoder to the original position before appending the TLV header. + * + * @param [in] outerEncoder The encoder to append to + * @param [in] packetDictionary The dictionary to read from + * @param [in] nestedType the TLV type to use for the nested value + * @param [in] nestedEncoderFunction The function to call to write the inner value + * + * @return non-negative The total number of bytes appended to 'outerEncoder' + * @return -1 An error + * + * Example: + * @code + * { + * // If the dictionary contains a KeyName Name, then encode the KeyName continer using 'rtaTlvSchemaV0KeyName_Encode'. Use + * // the value 'CCNxCodecSchemaV0_KeyLocatorKeys_KeyName' as the TLV type for the subcontainer. + * // + * if (ccnxTlvDictionary_IsValueBuffer(packetDictionary, CCNxCodecSchemaV0TlvDictionary_ContentObjectFastArray_KEYNAME_NAME)) { + * keynameLength = ccnxCodecTlvUtilities_NestedEncode(keyLocatorEncoder, packetDictionary, CCNxCodecSchemaV0_KeyLocatorKeys_KeyName, rtaTlvSchemaV0KeyName_Encode); + * } + * } + * @endcode + */ +ssize_t +ccnxCodecTlvUtilities_NestedEncode(CCNxCodecTlvEncoder *outerEncoder, CCNxTlvDictionary *packetDictionary, uint32_t nestedType, + ssize_t (*nestedEncoderFunction)(CCNxCodecTlvEncoder *innerEncoder, CCNxTlvDictionary *packetDictionary)); + +/** + * Reads the list 'listKey' from the dictionary and encodes them all as TLV entries + * + * <#Paragraphs Of Explanation#> + * + * @param [in] encoder The encoder to append to + * @param [in] packetDictionary The dictionary to read from + * @param [in] listKey The list key to read from packetDictionary + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +ssize_t +ccnxCodecTlvUtilities_EncodeCustomList(CCNxCodecTlvEncoder *encoder, CCNxTlvDictionary *packetDictionary, int listKey); + + +/** + * Parses the input buffer as a VarInt + * + * Parses the bytes of the input buffer as a network byte order variable length integer. + * Between 1 and 'length' bytes will be parses, where 'length' must be from 1 to 8. + * The buffer will be advanced as the bytes are read. + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * { + * PARCBuffer *buffer = parcBuffer_Wrap((uint8_t[]) { 0x10, 0x23, 0x00 }, 3, 0, 3 ); + * uint64_t value; + * ccnxCodecTlvUtilities_GetVarInt(buffer, 3, &value); + * // value = 0x0000000000102300 + * } + * @endcode + */ +bool ccnxCodecTlvUtilities_GetVarInt(PARCBuffer *input, size_t length, uint64_t *output); + +#endif // TransportRTA_ccnxCodec_TlvUtilities_h diff --git a/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_CryptoSuite.c b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_CryptoSuite.c new file mode 100755 index 00000000..588694b0 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_CryptoSuite.c @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +#include <config.h> + +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_CryptoSuite.h> + + +bool +ccnxCodecSchemaV1CryptoSuite_ParcToTlv(PARCCryptoSuite parcSuite, CCNxCodecSchemaV1TlvDictionary_CryptoSuite *outputValue) +{ + bool matchFound = false; + switch (parcSuite) { + case PARCCryptoSuite_RSA_SHA256: + *outputValue = CCNxCodecSchemaV1TlvDictionary_CryptoSuite_RsaSha256; + matchFound = true; + break; + + case PARCCryptoSuite_DSA_SHA256: + // not supported yet + break; + + case PARCCryptoSuite_RSA_SHA512: + // not supported yet + break; + + case PARCCryptoSuite_HMAC_SHA256: + *outputValue = CCNxCodecSchemaV1TlvDictionary_CryptoSuite_HmacSha256; + matchFound = true; + break; + + case PARCCryptoSuite_HMAC_SHA512: + // not supported yet + break; + + case PARCCryptoSuite_NULL_CRC32C: + *outputValue = CCNxCodecSchemaV1TlvDictionary_CryptoSuite_CRC32C; + matchFound = true; + break; + + default: + // unknown + break; + } + return matchFound; +} + +bool +ccnxCodecSchemaV1CryptoSuite_TlvToParc(CCNxCodecSchemaV1TlvDictionary_CryptoSuite tlvValue, PARCCryptoSuite *outputSuite) +{ + bool matchFound = false; + switch (tlvValue) { + case CCNxCodecSchemaV1TlvDictionary_CryptoSuite_RsaSha256: + *outputSuite = PARCCryptoSuite_RSA_SHA256; + matchFound = true; + break; + + case CCNxCodecSchemaV1TlvDictionary_CryptoSuite_CRC32C: + *outputSuite = PARCCryptoSuite_NULL_CRC32C; + matchFound = true; + break; + + case CCNxCodecSchemaV1TlvDictionary_CryptoSuite_HmacSha256: + *outputSuite = PARCCryptoSuite_HMAC_SHA256; + matchFound = true; + break; + + case CCNxCodecSchemaV1TlvDictionary_CryptoSuite_EcSecp256K1: + // not supported yet + break; + + default: + // unknown + break; + } + return matchFound; +} + +bool +ccnxCodecSchemaV1CryptoSuite_SignAndHashToTlv(PARCSigningAlgorithm signAlgorithm, PARCCryptoHashType hashType, CCNxCodecSchemaV1TlvDictionary_CryptoSuite *outputValue) +{ + bool matchFound = false; + switch (signAlgorithm) { + case PARCSigningAlgorithm_RSA: { + switch (hashType) { + case PARCCryptoHashType_SHA256: + *outputValue = CCNxCodecSchemaV1TlvDictionary_CryptoSuite_RsaSha256; + matchFound = true; + + default: + break; + } + break; + } + + case PARCSigningAlgorithm_HMAC: { + switch (hashType) { + case PARCCryptoHashType_SHA256: + *outputValue = CCNxCodecSchemaV1TlvDictionary_CryptoSuite_HmacSha256; + matchFound = true; + default: + break; + } + break; + } + + case PARCSigningAlgortihm_NULL: { + switch (hashType) { + case PARCCryptoHashType_CRC32C: + *outputValue = CCNxCodecSchemaV1TlvDictionary_CryptoSuite_CRC32C; + matchFound = true; + default: + break; + } + break; + } + default: + break; + } + return matchFound; +} + diff --git a/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_CryptoSuite.h b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_CryptoSuite.h new file mode 100755 index 00000000..68e90749 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_CryptoSuite.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file ccnxCodecSchemaV1_CryptoSuite.h + * @brief Translates between PARC CryptoSuite values and the wire encoding + * + * <#Detailed Description#> + * + */ + +#include <stdbool.h> +#include <parc/security/parc_CryptoHashType.h> +#include <parc/security/parc_SigningAlgorithm.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_TlvDictionary.h> + +/** + * Converts a PARC Crypto Suite to its TLV value + * + * Looks up the PARC cryptosuite value and returns the corresponding TLV wire format value. + * If no match is found, returns false and outputSuite is not modified. + * + * + * @param [in] parcSuite The PARC cryptosuite + * @param [out] outputValue The wire encoding equivalent + * + * @retval true if supported suite and outputValue set. + * @retval false if not supported. + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool ccnxCodecSchemaV1CryptoSuite_ParcToTlv(PARCCryptoSuite parcSuite, CCNxCodecSchemaV1TlvDictionary_CryptoSuite *outputValue); + +/** + * Converts a wire format cryptosuite value to the PARC cryptosuite + * + * Looks up the TLV wire format value and returns the corresponding PARC cryptosuite. + * If no match is found, returns false and outputSuite is not modified. + * + * @param [in] tlvValue The wire format value + * @param [out] outputValue The PARC equivalent + * + * @return true if match found and parcSuite set. + * @return false if no match found. + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool ccnxCodecSchemaV1CryptoSuite_TlvToParc(CCNxCodecSchemaV1TlvDictionary_CryptoSuite tlvValue, PARCCryptoSuite *outputSuite); + +/** + * Lookup a signing algorithm and hash type and convert to a wire format value + * + * Based on a PARCSigner's algorithm and hash type, find the corresponding wire format crypto suite. + * + * @param [in] signAlgorithm The signing algorithm + * @param [in] hashType The hash used by the signing algorithm + * @param [out] outputValue The wire format value + * + * @retval true if supported suite and outputValue set. + * @retval false if not supported. + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool ccnxCodecSchemaV1CryptoSuite_SignAndHashToTlv(PARCSigningAlgorithm signAlgorithm, PARCCryptoHashType hashType, CCNxCodecSchemaV1TlvDictionary_CryptoSuite *outputValue); diff --git a/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_FixedHeader.h b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_FixedHeader.h new file mode 100755 index 00000000..3055c054 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_FixedHeader.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file ccnxCodecSchemaV1_FixedHeader.h + * @brief common definitions and functions for the FixedHeader + * + * See ccnxCodecSchemaV1_Packet.h for an overview of the version 1 codec + * + * This is the one file you need to include for all FixedHeader operations. It will + * include all the Decoders and Encoders. + * + */ + +#ifndef TransportRTA_ccnxCodecSchemaV1_FixedHeader_h +#define TransportRTA_ccnxCodecSchemaV1_FixedHeader_h + +typedef struct __attribute__ ((__packed__)) rta_tlv_schema_v1_fixed_header { + uint8_t version; + uint8_t packetType; + uint16_t packetLength; + uint8_t reserved[3]; + uint8_t headerLength; +} CCNxCodecSchemaV1FixedHeader; + +typedef struct __attribute__ ((__packed__)) rta_tlv_schema_v1_interest_header { + uint8_t version; + uint8_t packetType; + uint16_t packetLength; + uint8_t hopLimit; + uint8_t returnCode; + uint8_t flags; + uint8_t headerLength; +} CCNxCodecSchemaV1InterestHeader; + + +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_FixedHeaderDecoder.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_FixedHeaderEncoder.h> +#endif // TransportRTA_ccnxCodecSchemaV1_FixedHeader_h diff --git a/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_FixedHeaderDecoder.c b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_FixedHeaderDecoder.c new file mode 100755 index 00000000..fcec49b3 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_FixedHeaderDecoder.c @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> + +#include <LongBow/runtime.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Buffer.h> + +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_FixedHeader.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_Types.h> +#include <ccnx/common/codec/ccnxCodec_TlvUtilities.h> + +static const size_t _fixedHeaderBytes = 8; + +static const int _fixedHeader_VersionOffset = 0; +static const int _fixedHeader_PacketTypeOffset = 1; +static const int _fixedHeader_PacketLengthOffset = 2; +static const int _fixedHeader_HopLimitOffset = 4; +static const int _fixedHeader_ReturnCodeOffset = 5; +static const int _fixedHeader_FlagsOffset = 6; +static const int _fixedHeader_HeaderLengthOffset = 7; + +bool +ccnxCodecSchemaV1FixedHeaderDecoder_Decode(CCNxCodecTlvDecoder *decoder, CCNxTlvDictionary *packetDictionary) +{ + if (ccnxCodecTlvDecoder_EnsureRemaining(decoder, _fixedHeaderBytes)) { + PARCBuffer *buffer = ccnxCodecTlvDecoder_GetValue(decoder, _fixedHeaderBytes); + bool success = ccnxTlvDictionary_PutBuffer(packetDictionary, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_FixedHeader, buffer); + + // validation + parcBuffer_SetPosition(buffer, _fixedHeader_VersionOffset); + uint8_t version = parcBuffer_GetUint8(buffer); + + parcBuffer_SetPosition(buffer, _fixedHeader_PacketLengthOffset); + uint16_t packetLength = parcBuffer_GetUint16(buffer); + + parcBuffer_SetPosition(buffer, _fixedHeader_ReturnCodeOffset); + uint8_t interestReturnCode = parcBuffer_GetUint8(buffer); + + parcBuffer_SetPosition(buffer, _fixedHeader_HopLimitOffset); + uint8_t hopLimit = parcBuffer_GetUint8(buffer); + + parcBuffer_SetPosition(buffer, _fixedHeader_HeaderLengthOffset); + uint8_t headerLength = parcBuffer_GetUint8(buffer); + + if (version != 1) { + CCNxCodecError *error = ccnxCodecError_Create(TLV_ERR_VERSION, __func__, __LINE__, _fixedHeader_VersionOffset); + ccnxCodecTlvDecoder_SetError(decoder, error); + ccnxCodecError_Release(&error); + success = false; + } else if (packetLength < _fixedHeaderBytes) { + CCNxCodecError *error = ccnxCodecError_Create(TLV_ERR_PACKETLENGTH_TOO_SHORT, __func__, __LINE__, _fixedHeader_PacketTypeOffset); + ccnxCodecTlvDecoder_SetError(decoder, error); + ccnxCodecError_Release(&error); + success = false; + } else if (headerLength < _fixedHeaderBytes) { + CCNxCodecError *error = ccnxCodecError_Create(TLV_ERR_HEADERLENGTH_TOO_SHORT, __func__, __LINE__, _fixedHeader_HeaderLengthOffset); + ccnxCodecTlvDecoder_SetError(decoder, error); + ccnxCodecError_Release(&error); + success = false; + } else if (packetLength < headerLength) { + CCNxCodecError *error = ccnxCodecError_Create(TLV_ERR_PACKETLENGTHSHORTER, __func__, __LINE__, _fixedHeader_PacketTypeOffset); + ccnxCodecTlvDecoder_SetError(decoder, error); + ccnxCodecError_Release(&error); + success = false; + } + + // decoder now points to just past the fixed header + parcBuffer_Release(&buffer); + + // Set the hoplimit in the dictionary. + ccnxTlvDictionary_PutInteger(packetDictionary, + CCNxCodecSchemaV1TlvDictionary_MessageFastArray_HOPLIMIT, hopLimit); + + // Set the InterestReturn code in the dictionary. + ccnxTlvDictionary_PutInteger(packetDictionary, + CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_InterestReturnCode, + interestReturnCode); + + return success; + } else { + CCNxCodecError *error = ccnxCodecError_Create(TLV_ERR_DECODE, __func__, __LINE__, ccnxCodecTlvDecoder_Position(decoder)); + ccnxCodecTlvDecoder_SetError(decoder, error); + ccnxCodecError_Release(&error); + return false; + } +} + +int +ccnxCodecSchemaV1FixedHeaderDecoder_GetVersion(CCNxTlvDictionary *packetDictionary) +{ + PARCBuffer *fixedHeader = ccnxTlvDictionary_GetBuffer(packetDictionary, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_FixedHeader); + if (fixedHeader != NULL) { + parcBuffer_SetPosition(fixedHeader, _fixedHeader_VersionOffset); + uint8_t version = parcBuffer_GetUint8(fixedHeader); + return version; + } + + return -1; +} + +int +ccnxCodecSchemaV1FixedHeaderDecoder_GetPacketType(CCNxTlvDictionary *packetDictionary) +{ + PARCBuffer *fixedHeader = ccnxTlvDictionary_GetBuffer(packetDictionary, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_FixedHeader); + if (fixedHeader != NULL) { + parcBuffer_SetPosition(fixedHeader, _fixedHeader_PacketTypeOffset); + uint8_t packetType = parcBuffer_GetUint8(fixedHeader); + return packetType; + } + + return -1; +} + +int +ccnxCodecSchemaV1FixedHeaderDecoder_GetPacketLength(CCNxTlvDictionary *packetDictionary) +{ + PARCBuffer *fixedHeader = ccnxTlvDictionary_GetBuffer(packetDictionary, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_FixedHeader); + if (fixedHeader != NULL) { + parcBuffer_SetPosition(fixedHeader, _fixedHeader_PacketLengthOffset); + uint16_t payloadLength = parcBuffer_GetUint16(fixedHeader); + return payloadLength; + } + + return -1; +} + +int +ccnxCodecSchemaV1FixedHeaderDecoder_GetHeaderLength(CCNxTlvDictionary *packetDictionary) +{ + int length = -1; + PARCBuffer *fixedHeader = ccnxTlvDictionary_GetBuffer(packetDictionary, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_FixedHeader); + if (fixedHeader != NULL) { + parcBuffer_SetPosition(fixedHeader, _fixedHeader_HeaderLengthOffset); + uint8_t headerLength = parcBuffer_GetUint8(fixedHeader); + + // 8 is the minimum size of headerLength + if (headerLength >= _fixedHeaderBytes) { + length = headerLength; + } + } + + return length; +} + +int +ccnxCodecSchemaV1FixedHeaderDecoder_GetOptionalHeaderLength(CCNxTlvDictionary *packetDictionary) +{ + int headerLength = ccnxCodecSchemaV1FixedHeaderDecoder_GetHeaderLength(packetDictionary); + return headerLength - _fixedHeaderBytes; +} + +int +ccnxCodecSchemaV1FixedHeaderDecoder_GetHopLimit(CCNxTlvDictionary *packetDictionary) +{ + PARCBuffer *fixedHeader = ccnxTlvDictionary_GetBuffer(packetDictionary, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_FixedHeader); + if (fixedHeader != NULL) { + parcBuffer_SetPosition(fixedHeader, _fixedHeader_HopLimitOffset); + uint8_t hopLimit = parcBuffer_GetUint8(fixedHeader); + return hopLimit; + } + + return -1; +} + +int +ccnxCodecSchemaV1FixedHeaderDecoder_GetReturnCode(CCNxTlvDictionary *packetDictionary) +{ + PARCBuffer *fixedHeader = ccnxTlvDictionary_GetBuffer(packetDictionary, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_FixedHeader); + if (fixedHeader != NULL) { + parcBuffer_SetPosition(fixedHeader, _fixedHeader_ReturnCodeOffset); + uint8_t returnCode = parcBuffer_GetUint8(fixedHeader); + return returnCode; + } + + return -1; +} + +int +ccnxCodecSchemaV1FixedHeaderDecoder_GetFlags(CCNxTlvDictionary *packetDictionary) +{ + PARCBuffer *fixedHeader = ccnxTlvDictionary_GetBuffer(packetDictionary, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_FixedHeader); + if (fixedHeader != NULL) { + parcBuffer_SetPosition(fixedHeader, _fixedHeader_FlagsOffset); + uint8_t flags = parcBuffer_GetUint8(fixedHeader); + return flags; + } + + return -1; +} diff --git a/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_FixedHeaderDecoder.h b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_FixedHeaderDecoder.h new file mode 100755 index 00000000..5d15f40b --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_FixedHeaderDecoder.h @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ +#ifndef Libccnx_ccnxCodecSchemaV1_FixedHeaderDecoder_h +#define Libccnx_ccnxCodecSchemaV1_FixedHeaderDecoder_h + +#include <stdbool.h> + +#include <parc/algol/parc_Buffer.h> + +#include <ccnx/common/internal/ccnx_TlvDictionary.h> +#include <ccnx/common/codec/ccnxCodec_TlvDecoder.h> + +/** + * The decode a V1 fixed header + * + * The decoder should point to byte 0 of the Fixed Header. + * It will be advanced to the first byte following it. + * The results are put in the provided. + * + * @param [in] decoder The decoder to parse + * @param [in] dictionary The results go directly in to the provided dictionary. + * + * @return true Fully parsed interest, no errors + * @return false Error decoding, decoder is left pointing to the first byte of the error + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool ccnxCodecSchemaV1FixedHeaderDecoder_Decode(CCNxCodecTlvDecoder *decoder, CCNxTlvDictionary *packetDictionary); + +/** + * A convenience function to return the version + * + * <#Paragraphs Of Explanation#> + * + * @param [in] fixedHeaderDictionary The FixedHeader dictionary + * + * @return positive The Version + * @return -1 The field does not exist + * + * Example: + * @code + * <#example#> + * @endcode + */ +int ccnxCodecSchemaV1FixedHeaderDecoder_GetVersion(CCNxTlvDictionary *packetDictionary); + +/** + * A convenience function to return the PacketType + * + * <#Paragraphs Of Explanation#> + * + * @param [in] fixedHeaderDictionary The FixedHeader dictionary + * + * @return positive The PacketType + * @return -1 The field does not exist + * + * Example: + * @code + * <#example#> + * @endcode + */ +int ccnxCodecSchemaV1FixedHeaderDecoder_GetPacketType(CCNxTlvDictionary *packetDictionary); + +/** + * A convenience function to return the PacketLength + * + * The PacketLength is measured from byte 0 to the end of the packet + * + * @param [in] fixedHeaderDictionary The FixedHeader dictionary + * + * @return positive The packet length (in host byte order) + * @return -1 The field does not exist + * + * Example: + * @code + * <#example#> + * @endcode + */ +int ccnxCodecSchemaV1FixedHeaderDecoder_GetPacketLength(CCNxTlvDictionary *packetDictionary); + +/** + * A convenience function to return the HeaderLength + * + * In a version 1 packet, the header length includes the fixed header. It is measured from + * byte 0 to the end of the hop-by-hop headers. + * + * @param [in] fixedHeaderDictionary The FixedHeader dictionary + * + * @return positive The header length + * @return -1 The field does not exist + * + * Example: + * @code + * <#example#> + * @endcode + */ +int ccnxCodecSchemaV1FixedHeaderDecoder_GetHeaderLength(CCNxTlvDictionary *packetDictionary); + +/** + * Returns the bytes of the optional headers + * + * Computes ccnxCodecSchemaV1FixedHeaderDecoder_GetHeaderLength() - sizeof(fixedHeader) + * + * @param [in] fixedHeaderDictionary The FixedHeader dictionary + * + * @retval non-negative The length of the optional headers + * @retval negative An error + * + * Example: + * @code + * <#example#> + * @endcode + */ +int ccnxCodecSchemaV1FixedHeaderDecoder_GetOptionalHeaderLength(CCNxTlvDictionary *packetDictionary); + + +/** + * A convenience function to return the ReturnCode of an Interest or InterestReturn + * + * @param [in] fixedHeaderDictionary The FixedHeader dictionary + * + * @return positive The Return Code + * @return -1 The field does not exist + * + * Example: + * @code + * <#example#> + * @endcode + */ +int ccnxCodecSchemaV1FixedHeaderDecoder_GetReturnCode(CCNxTlvDictionary *packetDictionary); + +/** + * A convenience function to return the header Flags + * + * @param [in] fixedHeaderDictionary The FixedHeader dictionary + * + * @return positive The flags + * @return -1 The field does not exist + * + * Example: + * @code + * <#example#> + * @endcode + */ +int ccnxCodecSchemaV1FixedHeaderDecoder_GetFlags(CCNxTlvDictionary *packetDictionary); + +#endif // Libccnx_ccnxCodecSchemaV1_FixedHeaderDecoder_h diff --git a/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_FixedHeaderEncoder.c b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_FixedHeaderEncoder.c new file mode 100755 index 00000000..3b6eca67 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_FixedHeaderEncoder.c @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <arpa/inet.h> + +#include <LongBow/runtime.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Buffer.h> + +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_FixedHeader.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_Types.h> +#include <ccnx/common/codec/ccnxCodec_TlvUtilities.h> + +ssize_t +ccnxCodecSchemaV1FixedHeaderEncoder_EncodeHeader(CCNxCodecTlvEncoder *fixedHeaderEncoder, const CCNxCodecSchemaV1FixedHeader *header) +{ + assertNotNull(fixedHeaderEncoder, "Parameter fixedHeaderEncoder must be non-null"); + assertNotNull(header, "Parameter header must be non-null"); + trapIllegalValueIf(header->version != 1, "Header wrong version, must be 1"); + + CCNxCodecSchemaV1InterestHeader copy; + + memcpy(©, header, sizeof(CCNxCodecSchemaV1InterestHeader)); + + copy.packetLength = htons(header->packetLength); + + switch (header->packetType) { + case CCNxCodecSchemaV1Types_PacketType_Interest: + copy.returnCode = 0; + break; + + case CCNxCodecSchemaV1Types_PacketType_InterestReturn: + // nothing to do, all fields used + break; + + default: + copy.hopLimit = 0; + copy.returnCode = 0; + copy.flags = 0; + break; + } + + ccnxCodecTlvEncoder_AppendRawArray(fixedHeaderEncoder, sizeof(CCNxCodecSchemaV1FixedHeader), (uint8_t *) ©); + return sizeof(CCNxCodecSchemaV1FixedHeader); +} diff --git a/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_FixedHeaderEncoder.h b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_FixedHeaderEncoder.h new file mode 100755 index 00000000..0981f4bd --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_FixedHeaderEncoder.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file ccnxCodecSchemaV1_FixedHeaderEncoder.h + * @brief <#Brief Description#> + * + * <#Detailed Description#> + * + */ +#ifndef Libccnx_ccnxCodecSchemaV1_FixedHeaderEncoder_h +#define Libccnx_ccnxCodecSchemaV1_FixedHeaderEncoder_h + +#include <ccnx/common/codec/ccnxCodec_TlvEncoder.h> + +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_FixedHeader.h> + +/** + * Set the values in the fixed header + * + * Put the provided fixed header at the given byte location. The provided fixed header is not put + * in as-is (i.e byte for byte), but is parsed and put in the correct byte positions and encodings + * assuming the fixed header starts at the given position. + * + * The encoder is returned to its current position after putting the header. + * + * @param [in] encoder The encoder to append the fixed header in to + * @param [in] The header, in host byte order + * + * @return Number The bytes appended, or -1 on error + * + * Example: + * @code + * <#example#> + * @endcode + */ +ssize_t ccnxCodecSchemaV1FixedHeaderEncoder_EncodeHeader(CCNxCodecTlvEncoder *encoder, const CCNxCodecSchemaV1FixedHeader *header); +#endif // Libccnx_ccnxCodecSchemaV1_FixedHeaderEncoder_h diff --git a/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_HashCodec.c b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_HashCodec.c new file mode 100644 index 00000000..23a559cc --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_HashCodec.c @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +#include <config.h> + +#include <stdlib.h> + +#include <LongBow/runtime.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Buffer.h> + +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_Types.h> +#include <ccnx/common/codec/ccnxCodec_TlvUtilities.h> + +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_HashCodec.h> + +/** + * These are the accepted sizes for the pre-defined hash types. + * Hash TLVs with lengths that do not match one of these values will be deemed + * invalid and not parse correctly. + */ +static const size_t _PARCCryptoHashType_SHA256_Sizes[] = { 32 }; +static const size_t _PARCCryptoHashType_SHA512_Sizes[] = { 32, 64 }; + +static bool +_ccnxCodecSchemaV1HashCodec_ValidHashSize(size_t size, size_t numSizes, size_t sizes[numSizes]) +{ + for (size_t i = 0; i < numSizes; i++) { + if (sizes[i] == size) { + return true; + } + } + return false; +} + +ssize_t +ccnxCodecSchemaV1HashCodec_Encode(CCNxCodecTlvEncoder *encoder, const PARCCryptoHash *hash) +{ + PARCBuffer *digest = parcCryptoHash_GetDigest(hash); + PARCCryptoHashType hashType = parcCryptoHash_GetDigestType(hash); + size_t digestLength = parcBuffer_Remaining(digest); + + uint16_t tlvHashType = CCNxCodecSchemaV1Types_HashType_App; + bool validHash = true; + switch (hashType) { + case PARCCryptoHashType_SHA256: + tlvHashType = CCNxCodecSchemaV1Types_HashType_SHA256; + validHash = _ccnxCodecSchemaV1HashCodec_ValidHashSize(digestLength, + sizeof(_PARCCryptoHashType_SHA256_Sizes) / sizeof(size_t), (size_t *) _PARCCryptoHashType_SHA256_Sizes); + break; + case PARCCryptoHashType_SHA512: + tlvHashType = CCNxCodecSchemaV1Types_HashType_SHA512; + validHash = _ccnxCodecSchemaV1HashCodec_ValidHashSize(digestLength, + sizeof(_PARCCryptoHashType_SHA512_Sizes) / sizeof(size_t), (size_t *) _PARCCryptoHashType_SHA512_Sizes); + break; + default: + break; + } + + if (validHash) { + return ccnxCodecTlvEncoder_AppendBuffer(encoder, tlvHashType, digest); + } else { + CCNxCodecError *error = ccnxCodecError_Create(TLV_MISSING_MANDATORY, __func__, __LINE__, ccnxCodecTlvEncoder_Position(encoder)); + ccnxCodecTlvEncoder_SetError(encoder, error); + ccnxCodecError_Release(&error); + + return -1; + } +} + +static bool +_ccnxCodecSchemaV1HashCodec_ValidHash(uint16_t hashType, uint16_t hashSize) +{ + bool validHash = true; + + switch (hashType) { + case CCNxCodecSchemaV1Types_HashType_SHA256: + validHash = _ccnxCodecSchemaV1HashCodec_ValidHashSize(hashSize, + sizeof(_PARCCryptoHashType_SHA256_Sizes) / sizeof(size_t), (size_t *) _PARCCryptoHashType_SHA256_Sizes); + break; + case CCNxCodecSchemaV1Types_HashType_SHA512: + validHash = _ccnxCodecSchemaV1HashCodec_ValidHashSize(hashSize, + sizeof(_PARCCryptoHashType_SHA512_Sizes) / sizeof(size_t), (size_t *) _PARCCryptoHashType_SHA512_Sizes); + break; + default: + break; + } + + return validHash; +} + +PARCCryptoHash * +ccnxCodecSchemaV1HashCodec_DecodeValue(CCNxCodecTlvDecoder *decoder, size_t limit) +{ + PARCCryptoHash *hash = NULL; + uint16_t hashType = 0; + uint16_t length = 0; + + if (ccnxCodecTlvDecoder_EnsureRemaining(decoder, 4)) { + hashType = ccnxCodecTlvDecoder_GetType(decoder); + length = ccnxCodecTlvDecoder_GetLength(decoder); + + if (length > limit) { + CCNxCodecError *error = ccnxCodecError_Create(TLV_MISSING_MANDATORY, __func__, __LINE__, ccnxCodecTlvDecoder_Position(decoder)); + ccnxCodecTlvDecoder_SetError(decoder, error); + ccnxCodecError_Release(&error); + return NULL; + } + + if (ccnxCodecTlvDecoder_EnsureRemaining(decoder, length)) { + PARCBuffer *value = ccnxCodecTlvDecoder_GetValue(decoder, length); + switch (hashType) { + case CCNxCodecSchemaV1Types_HashType_SHA256: + hash = parcCryptoHash_Create(PARCCryptoHashType_SHA256, value); + break; + case CCNxCodecSchemaV1Types_HashType_SHA512: + hash = parcCryptoHash_Create(PARCCryptoHashType_SHA512, value); + break; + case CCNxCodecSchemaV1Types_HashType_App: + hash = parcCryptoHash_Create(PARCCryptoHashType_NULL, value); + break; + } + parcBuffer_Release(&value); + } + } + + // Verify the hash size, if one was parsed correctly. + if (hash != NULL && !_ccnxCodecSchemaV1HashCodec_ValidHash(hashType, length)) { + parcCryptoHash_Release(&hash); + } + + return hash; +} diff --git a/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_HashCodec.h b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_HashCodec.h new file mode 100755 index 00000000..f03b3746 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_HashCodec.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file ccnxCodecSchemaV1_HashCodec.h + * @brief A cryptographic hash digest encoded + * + */ + +#ifndef __CCNx_Common__ccnxCodecSchemaV1_HashCodec__ +#define __CCNx_Common__ccnxCodecSchemaV1_HashCodec__ + +#include <ccnx/common/codec/ccnxCodec_TlvEncoder.h> +#include <ccnx/common/codec/ccnxCodec_TlvDecoder.h> + +#include <parc/security/parc_CryptoHash.h> + +/** + * Encodes the hash, but without a "TL" container + * + * Will append the Link in it's well-known TLV format, but without any + * "TL" container. + * + * If the link does not have a name, will return -1 with the error TLV_MISSING_MANDATORY. + * + * @param [in] encoder The hash will be appended to the encoder + * @param [in] hash The hash to append + * + * @retval non-negative The number of bytes appended to the encoder + * @retval negative An error, look at the CCNxCodecError of the encoder + * + * Example: + * @code + * <#example#> + * @endcode + */ +ssize_t ccnxCodecSchemaV1HashCodec_Encode(CCNxCodecTlvEncoder *encoder, const PARCCryptoHash *hash); + +/** + * The decoder points to the first byte of the "value" of something that is a Link + * + * For a KeyName, decoder should be pointed to the "value" of the KeyName. for a ContentObject + * of type Link, it should be the first byte of the Payload. + * + * A link is the tuple {Name, [KeyId], [Hash]}, where KeyId is the keyIdRestriction and + * Hash is the ContentObjectHash restriction to use in an Interest for Name. + * No additional fields are allowed in the Link. + * + * @param [in] decoder The Tlv Decoder pointing to the start of the Name value + * @param [in] length the length of the Hash value + * + * @return non-null A parsed name + * @return null An error, check the decoder's error message + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCCryptoHash *ccnxCodecSchemaV1HashCodec_DecodeValue(CCNxCodecTlvDecoder *decoder, size_t length); + +#endif // __CCNx_Common__ccnxCodecSchemaV1_HashCodec__ diff --git a/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_LinkCodec.c b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_LinkCodec.c new file mode 100755 index 00000000..cc78c28e --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_LinkCodec.c @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> + +#include <LongBow/runtime.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Buffer.h> + +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_Types.h> +#include <ccnx/common/codec/ccnxCodec_TlvUtilities.h> + +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_NameCodec.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_LinkCodec.h> + +ssize_t +ccnxCodecSchemaV1LinkCodec_Encode(CCNxCodecTlvEncoder *encoder, const CCNxLink *link) +{ + ssize_t length = 0; + + const CCNxName *name = ccnxLink_GetName(link); + if (name) { + length += ccnxCodecSchemaV1NameCodec_Encode(encoder, CCNxCodecSchemaV1Types_Link_Name, name); + + PARCBuffer *keyid = ccnxLink_GetKeyID(link); + if (keyid) { + length += ccnxCodecTlvEncoder_AppendBuffer(encoder, CCNxCodecSchemaV1Types_Link_KeyIdRestriction, keyid); + } + + PARCBuffer *hash = ccnxLink_GetContentObjectHash(link); + if (hash) { + length += ccnxCodecTlvEncoder_AppendBuffer(encoder, CCNxCodecSchemaV1Types_Link_ContentObjectHashRestriction, hash); + } + } else { + length = -1; + CCNxCodecError *error = ccnxCodecError_Create(TLV_MISSING_MANDATORY, __func__, __LINE__, ccnxCodecTlvEncoder_Position(encoder)); + ccnxCodecTlvEncoder_SetError(encoder, error); + ccnxCodecError_Release(&error); + } + + return length; +} + +typedef struct decoded_link { + CCNxName *linkName; + PARCBuffer *linkKeyId; + PARCBuffer *linkHash; +} _DecodedLink; + +static int +_decodeField(CCNxCodecTlvDecoder *decoder, _DecodedLink *decodedLink) +{ + int errorCode = TLV_ERR_NO_ERROR; + + uint16_t type = ccnxCodecTlvDecoder_GetType(decoder); + uint16_t length = ccnxCodecTlvDecoder_GetLength(decoder); + + if (ccnxCodecTlvDecoder_EnsureRemaining(decoder, length)) { + switch (type) { + case CCNxCodecSchemaV1Types_Link_Name: + if (decodedLink->linkName == NULL) { + decodedLink->linkName = ccnxCodecSchemaV1NameCodec_DecodeValue(decoder, length); + } else { + errorCode = TLV_ERR_DUPLICATE_FIELD; + } + break; + + case CCNxCodecSchemaV1Types_Link_KeyIdRestriction: + if (decodedLink->linkKeyId == NULL) { + decodedLink->linkKeyId = ccnxCodecTlvDecoder_GetValue(decoder, length); + } else { + errorCode = TLV_ERR_DUPLICATE_FIELD; + } + break; + + case CCNxCodecSchemaV1Types_Link_ContentObjectHashRestriction: + if (decodedLink->linkHash == NULL) { + decodedLink->linkHash = ccnxCodecTlvDecoder_GetValue(decoder, length); + } else { + errorCode = TLV_ERR_DUPLICATE_FIELD; + } + break; + + default: + // we do not support unknown TLVs + errorCode = TLV_ERR_DECODE; + break; + } + } else { + errorCode = TLV_ERR_TOO_LONG; + } + + return errorCode; +} + +static void +_decodecLinkCleanup(_DecodedLink *decodedLink) +{ + if (decodedLink->linkName) { + ccnxName_Release(&decodedLink->linkName); + } + + if (decodedLink->linkKeyId) { + parcBuffer_Release(&decodedLink->linkKeyId); + } + + if (decodedLink->linkHash) { + parcBuffer_Release(&decodedLink->linkHash); + } +} + +CCNxLink * +ccnxCodecSchemaV1LinkCodec_DecodeValue(CCNxCodecTlvDecoder *decoder, uint16_t linkLength) +{ + int errorCode = TLV_ERR_NO_ERROR; + + CCNxLink *link = NULL; + + _DecodedLink decodedLink; + memset(&decodedLink, 0, sizeof(_DecodedLink)); + + if (ccnxCodecTlvDecoder_EnsureRemaining(decoder, linkLength)) { + while (errorCode == TLV_ERR_NO_ERROR && ccnxCodecTlvDecoder_EnsureRemaining(decoder, 4)) { + errorCode = _decodeField(decoder, &decodedLink); + } + } else { + errorCode = TLV_ERR_TOO_LONG; + } + + if (errorCode == TLV_ERR_NO_ERROR && decodedLink.linkName == NULL) { + errorCode = TLV_ERR_DECODE; + } + + if (errorCode != TLV_ERR_NO_ERROR) { + CCNxCodecError *error = ccnxCodecError_Create(errorCode, __func__, __LINE__, ccnxCodecTlvDecoder_Position(decoder)); + ccnxCodecTlvDecoder_SetError(decoder, error); + ccnxCodecError_Release(&error); + } else { + link = ccnxLink_Create(decodedLink.linkName, decodedLink.linkKeyId, decodedLink.linkHash); + } + + // cleanup any partial memory allocations + _decodecLinkCleanup(&decodedLink); + + return link; +} diff --git a/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_LinkCodec.h b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_LinkCodec.h new file mode 100755 index 00000000..4163aa1d --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_LinkCodec.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file ccnxCodecSchemaV1_LinkCodec.h + * @brief A Link is a well-known value, not a TLV field + * + * A Link may be the "value" of a TLV element, such as the KeyName. It can also occur + * in the payload of a ContentObject whose PayloadType is Link. + * + */ + +#ifndef __CCNx_Common__ccnxCodecSchemaV1_LinkCodec__ +#define __CCNx_Common__ccnxCodecSchemaV1_LinkCodec__ + +#include <ccnx/common/ccnx_Link.h> +#include <ccnx/common/codec/ccnxCodec_TlvEncoder.h> +#include <ccnx/common/codec/ccnxCodec_TlvDecoder.h> + +/** + * Encodes the link, but without a "TL" container + * + * Will append the Link in it's well-known TLV format, but without any + * "TL" container. + * + * If the link does not have a name, will return -1 with the error TLV_MISSING_MANDATORY. + * + * @param [in] encoder The link will be appended to the encoder + * @param [in] link The link to append + * + * @retval non-negative The number of bytes appended to the encoder + * @retval negative An error, look at the CCNxCodecError of the encoder + * + * Example: + * @code + * <#example#> + * @endcode + */ +ssize_t ccnxCodecSchemaV1LinkCodec_Encode(CCNxCodecTlvEncoder *encoder, const CCNxLink *link); + +/** + * The decoder points to the first byte of the "value" of something that is a Link + * + * For a KeyName, decoder should be pointed to the "value" of the KeyName. for a ContentObject + * of type Link, it should be the first byte of the Payload. + * + * A link is the tuple {Name, [KeyId], [Hash]}, where KeyId is the keyIdRestriction and + * Hash is the ContentObjectHash restriction to use in an Interest for Name. + * No additional fields are allowed in the Link. + * + * @param [in] decoder The Tlv Decoder pointing to the start of the Name value + * @param [in] length the length of the Link value + * + * @return non-null A parsed name + * @return null An error, check the decoder's error message + * + * Example: + * @code + * <#example#> + * @endcode + */ +CCNxLink *ccnxCodecSchemaV1LinkCodec_DecodeValue(CCNxCodecTlvDecoder *decoder, uint16_t length); + +#endif /* defined(__CCNx_Common__ccnxCodecSchemaV1_LinkCodec__) */ diff --git a/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_ManifestDecoder.c b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_ManifestDecoder.c new file mode 100644 index 00000000..87665de1 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_ManifestDecoder.c @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +/** + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> + +#include <LongBow/runtime.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Object.h> +#include <parc/algol/parc_Buffer.h> + +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_Types.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_ManifestDecoder.h> + +#include <ccnx/common/codec/ccnxCodec_TlvUtilities.h> +#include <ccnx/common/ccnx_PayloadType.h> +#include <ccnx/common/ccnx_InterestReturn.h> +#include <ccnx/common/ccnx_Manifest.h> +#include <ccnx/common/ccnx_ManifestHashGroup.h> + +static bool +_decodeHashGroupMetadata(CCNxCodecTlvDecoder *decoder, CCNxManifestHashGroup *group, size_t length) +{ + size_t offset = 0; + bool success = true; + + while (offset < length) { + uint16_t type = ccnxCodecTlvDecoder_GetType(decoder); + uint16_t value_length = ccnxCodecTlvDecoder_GetLength(decoder); + PARCBuffer *value = ccnxCodecTlvDecoder_GetValue(decoder, value_length); + + offset += (4 + value_length); + + switch (type) { + case CCNxCodecSchemaV1Types_CCNxManifestHashGroupMetadata_Locator: { + char *nameString = parcBuffer_ToString(value); + const CCNxName *locator = ccnxName_CreateFromCString(nameString); + ccnxManifestHashGroup_SetLocator(group, locator); + parcMemory_Deallocate(&nameString); + ccnxName_Release((CCNxName **) &locator); + break; + } + case CCNxCodecSchemaV1Types_CCNxManifestHashGroupMetadata_DataSize: { + uint64_t dataSize = parcBuffer_GetUint64(value); + ccnxManifestHashGroup_SetDataSize(group, dataSize); + break; + } + case CCNxCodecSchemaV1Types_CCNxManifestHashGroupMetadata_BlockSize: { + uint64_t blockSize = parcBuffer_GetUint64(value); + ccnxManifestHashGroup_SetBlockSize(group, blockSize); + break; + } + case CCNxCodecSchemaV1Types_CCNxManifestHashGroupMetadata_EntrySize: { + uint64_t entrySize = parcBuffer_GetUint64(value); + ccnxManifestHashGroup_SetEntrySize(group, entrySize); + break; + } + case CCNxCodecSchemaV1Types_CCNxManifestHashGroupMetadata_TreeHeight: { + uint64_t treeHeight = parcBuffer_GetUint64(value); + ccnxManifestHashGroup_SetTreeHeight(group, treeHeight); + break; + } + case CCNxCodecSchemaV1Types_CCNxManifestHashGroupMetadata_OverallDataSha256: { + ccnxManifestHashGroup_SetOverallDataDigest(group, value); + break; + } + } + + parcBuffer_Release(&value); + } + + return success; +} + +static bool +_decodeHashGroup(CCNxCodecTlvDecoder *decoder, CCNxTlvDictionary *packetDictionary, CCNxManifestHashGroup *group, size_t length) +{ + bool success = true; + size_t offset = 0; + + while (offset < length) { + uint16_t type = ccnxCodecTlvDecoder_GetType(decoder); + uint16_t value_length = ccnxCodecTlvDecoder_GetLength(decoder); + + offset += (4 + value_length); + + switch (type) { + case CCNxCodecSchemaV1Types_CCNxManifestHashGroup_Metadata: { + success = _decodeHashGroupMetadata(decoder, group, value_length); + if (!success) { + return false; + } + break; + } + + case CCNxCodecSchemaV1Types_CCNxManifestHashGroup_DataPointer: { + PARCBuffer *buffer = ccnxCodecTlvDecoder_GetValue(decoder, value_length); + ccnxManifestHashGroup_AppendPointer(group, CCNxManifestHashGroupPointerType_Data, buffer); + parcBuffer_Release(&buffer); + break; + } + + case CCNxCodecSchemaV1Types_CCNxManifestHashGroup_ManifestPointer: { + PARCBuffer *buffer = ccnxCodecTlvDecoder_GetValue(decoder, value_length); + ccnxManifestHashGroup_AppendPointer(group, CCNxManifestHashGroupPointerType_Manifest, buffer); + parcBuffer_Release(&buffer); + break; + } + + default: + // if we do not know the TLV type, put it in this container's unknown list + success = ccnxCodecTlvUtilities_PutAsListBuffer(decoder, packetDictionary, type, value_length, CCNxCodecSchemaV1TlvDictionary_Lists_MESSAGE_LIST); + break; + } + + if (!success) { + CCNxCodecError *error = ccnxCodecError_Create(TLV_ERR_DECODE, __func__, __LINE__, ccnxCodecTlvDecoder_Position(decoder)); + ccnxCodecTlvDecoder_SetError(decoder, error); + ccnxCodecError_Release(&error); + } + } + + CCNxManifestInterface *manifest = ccnxManifestInterface_GetInterface(packetDictionary); + manifest->addHashGroup(packetDictionary, group); + + return success; +} + +static bool +_decodeType(CCNxCodecTlvDecoder *decoder, CCNxTlvDictionary *packetDictionary, uint16_t type, uint16_t length) +{ + bool success = false; + switch (type) { + case CCNxCodecSchemaV1Types_CCNxMessage_Name: { + success = ccnxCodecTlvUtilities_PutAsName(decoder, packetDictionary, type, length, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_NAME); + break; + } + + case CCNxCodecSchemaV1Types_CCNxMessage_HashGroup: { + CCNxManifestHashGroup *group = ccnxManifestHashGroup_Create(); + success = _decodeHashGroup(decoder, packetDictionary, group, length); + ccnxManifestHashGroup_Release(&group); + break; + } + + default: + // if we do not know the TLV type, put it in this container's unknown list + success = ccnxCodecTlvUtilities_PutAsListBuffer(decoder, packetDictionary, type, length, CCNxCodecSchemaV1TlvDictionary_Lists_MESSAGE_LIST); + break; + } + + if (!success) { + CCNxCodecError *error = ccnxCodecError_Create(TLV_ERR_DECODE, __func__, __LINE__, ccnxCodecTlvDecoder_Position(decoder)); + ccnxCodecTlvDecoder_SetError(decoder, error); + ccnxCodecError_Release(&error); + } + return success; +} + +/* + * We are given a decoder that points to the first TLV of a list of TLVs. We keep walking the + * list until we come to the end of the decoder. + */ +bool +ccnxCodecSchemaV1ManifestDecoder_Decode(CCNxCodecTlvDecoder *decoder, CCNxTlvDictionary *manifestDictionary) +{ + return ccnxCodecTlvUtilities_DecodeContainer(decoder, manifestDictionary, _decodeType); +} diff --git a/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_ManifestDecoder.h b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_ManifestDecoder.h new file mode 100755 index 00000000..1726e41e --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_ManifestDecoder.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +/** + * @file ccnxCodecSchemaV1_ManifestDecoder.h + * @brief Decode the body of a CCNx Manifest. + * + */ + +#ifndef TransportRTA_ccnxCodecSchemaV1_ManifestDecoder_h +#define TransportRTA_ccnxCodecSchemaV1_ManifestDecoder_h + +#include <stdbool.h> + +#include <ccnx/common/internal/ccnx_TlvDictionary.h> +#include <ccnx/common/codec/ccnxCodec_TlvDecoder.h> + +/** + * Decode a V1 Manifest. + * + * The decoder should point to byte 0 of a Manifest (message) TLV. + * The results are put in the provided dictionary. + * It is an error if the message does not extend to the end of + * the decoder. + * + * @param [in] decoder The decoder to parse + * @param [in] manifestDictionary The results go directly in to the provided dictionary. + * + * @return true Fully parsed interest, no errors + * @return false Error decoding, decoder is left pointing to the first byte of the error + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool ccnxCodecSchemaV1ManifestDecoder_Decode(CCNxCodecTlvDecoder *decoder, CCNxTlvDictionary *manifestDictionary); + +#endif // TransportRTA_ccnxCodecSchemaV1_ManifestDecoder_h diff --git a/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_ManifestEncoder.c b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_ManifestEncoder.c new file mode 100644 index 00000000..0d8f52bf --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_ManifestEncoder.c @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +/** + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> + +#include <LongBow/runtime.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Object.h> +#include <parc/algol/parc_Buffer.h> +#include <parc/algol/parc_BufferComposer.h> + +#include <ccnx/common/ccnx_Manifest.h> +#include <ccnx/common/ccnx_ManifestHashGroup.h> + +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_Types.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_ManifestEncoder.h> + +#include <ccnx/common/codec/ccnxCodec_TlvUtilities.h> +#include <ccnx/common/ccnx_PayloadType.h> +#include <ccnx/common/ccnx_InterestReturn.h> + +static size_t +_appendPointer(CCNxCodecTlvEncoder *encoder, CCNxManifestHashGroupPointer *ptr) +{ + const PARCBuffer *digest = ccnxManifestHashGroupPointer_GetDigest(ptr); + CCNxManifestHashGroupPointerType type = ccnxManifestHashGroupPointer_GetType(ptr); + + ssize_t length = -1; + switch (type) { + case CCNxManifestHashGroupPointerType_Data: + length = ccnxCodecTlvEncoder_AppendBuffer(encoder, CCNxCodecSchemaV1Types_CCNxManifestHashGroup_DataPointer, (PARCBuffer *) digest); + break; + case CCNxManifestHashGroupPointerType_Manifest: + length = ccnxCodecTlvEncoder_AppendBuffer(encoder, CCNxCodecSchemaV1Types_CCNxManifestHashGroup_ManifestPointer, (PARCBuffer *) digest); + break; + default: + assertTrue(false, "Invalid pointer type %d", type); + } + + if (length < 0) { + CCNxCodecError *error = ccnxCodecError_Create(TLV_MISSING_MANDATORY, __func__, __LINE__, ccnxCodecTlvEncoder_Position(encoder)); + ccnxCodecTlvEncoder_SetError(encoder, error); + ccnxCodecError_Release(&error); + } + + return length; +} + +ssize_t +_appendMetadata(CCNxCodecTlvEncoder *encoder, CCNxManifestHashGroup *group) +{ + ssize_t length = 0; + + // Pre-populate this field -- we'll come back and fill in the length after we're done + size_t startPosition = ccnxCodecTlvEncoder_Position(encoder); + ccnxCodecTlvEncoder_AppendContainer(encoder, CCNxCodecSchemaV1Types_CCNxManifestHashGroup_Metadata, length); + + // Now append all metadata that exists in the hash group. + const CCNxName *locator = ccnxManifestHashGroup_GetLocator(group); + if (locator != NULL) { + char *nameString = ccnxName_ToString(locator); + PARCBuffer *nameBuffer = parcBuffer_AllocateCString(nameString); + length += ccnxCodecTlvEncoder_AppendBuffer(encoder, CCNxCodecSchemaV1Types_CCNxManifestHashGroupMetadata_Locator, nameBuffer); + parcBuffer_Release(&nameBuffer); + parcMemory_Deallocate(&nameString); + } + + size_t dataSize = ccnxManifestHashGroup_GetDataSize(group); + if (dataSize > 0) { + length += ccnxCodecTlvEncoder_AppendUint64(encoder, CCNxCodecSchemaV1Types_CCNxManifestHashGroupMetadata_DataSize, dataSize); + } + + size_t blockSize = ccnxManifestHashGroup_GetBlockSize(group); + if (blockSize > 0) { + length += ccnxCodecTlvEncoder_AppendUint64(encoder, CCNxCodecSchemaV1Types_CCNxManifestHashGroupMetadata_BlockSize, blockSize); + } + + size_t entrySize = ccnxManifestHashGroup_GetEntrySize(group); + if (entrySize > 0) { + length += ccnxCodecTlvEncoder_AppendUint64(encoder, CCNxCodecSchemaV1Types_CCNxManifestHashGroupMetadata_EntrySize, entrySize); + } + + size_t treeSize = ccnxManifestHashGroup_GetTreeHeight(group); + if (treeSize > 0) { + length += ccnxCodecTlvEncoder_AppendUint64(encoder, CCNxCodecSchemaV1Types_CCNxManifestHashGroupMetadata_TreeHeight, treeSize); + } + + const PARCBuffer *dataDigest = ccnxManifestHashGroup_GetOverallDataDigest(group); + if (dataDigest != NULL) { + length += ccnxCodecTlvEncoder_AppendBuffer(encoder, CCNxCodecSchemaV1Types_CCNxManifestHashGroupMetadata_OverallDataSha256, (PARCBuffer *) dataDigest); + } + + // Rewind back to the container opening and fill in the length + size_t endPosition = ccnxCodecTlvEncoder_Position(encoder); + ccnxCodecTlvEncoder_PutUint16(encoder, startPosition, CCNxCodecSchemaV1Types_CCNxManifestHashGroup_Metadata); + ccnxCodecTlvEncoder_PutUint16(encoder, startPosition + 2, length); + ccnxCodecTlvEncoder_SetPosition(encoder, endPosition); + + return endPosition - startPosition; +} + +ssize_t +ccnxCodecSchemaV1ManifestEncoder_Encode(CCNxCodecTlvEncoder *encoder, CCNxTlvDictionary *packetDictionary) +{ + ssize_t length = 0; + + ssize_t numHashGroups = ccnxTlvDictionary_ListSize(packetDictionary, CCNxCodecSchemaV1TlvDictionary_Lists_HASH_GROUP_LIST); + for (size_t i = 0; i < numHashGroups; i++) { + // Skip past the TL of the hash group to append the pointers inside + ssize_t groupLength = 0; + ccnxCodecTlvEncoder_AppendContainer(encoder, CCNxCodecSchemaV1Types_CCNxMessage_HashGroup, groupLength); + + CCNxManifestInterface *interface = ccnxManifestInterface_GetInterface(packetDictionary); + CCNxManifestHashGroup *group = interface->getHashGroup(packetDictionary, i); + + // Encode any metadata, if present. + if (ccnxManifestHashGroup_HasMetadata(group)) { + groupLength += _appendMetadata(encoder, group); + } + + // Append the HashGroup pointers + size_t numPointers = ccnxManifestHashGroup_GetNumberOfPointers(group); + for (size_t p = 0; p < numPointers; p++) { + CCNxManifestHashGroupPointer *ptr = ccnxManifestHashGroup_GetPointerAtIndex(group, p); + ssize_t ptrLength = _appendPointer(encoder, ptr); + if (ptrLength < 0) { + return ptrLength; + } + groupLength += ptrLength; + } + + // Now that we know the overall length, rewind back to the start and append the TL + // part of the container. + size_t endPosition = ccnxCodecTlvEncoder_Position(encoder); + ssize_t offset = endPosition - groupLength - 4; + ccnxCodecTlvEncoder_PutUint16(encoder, offset, CCNxCodecSchemaV1Types_CCNxMessage_HashGroup); + ccnxCodecTlvEncoder_PutUint16(encoder, offset + 2, groupLength); + ccnxCodecTlvEncoder_SetPosition(encoder, endPosition); + + length += groupLength + 4; + + ccnxManifestHashGroup_Release(&group); + } + + return length; +} diff --git a/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_ManifestEncoder.h b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_ManifestEncoder.h new file mode 100755 index 00000000..4951cf80 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_ManifestEncoder.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +/** + * @file ccnxCodecSchemaV1_ManifestEncoder.h + * @brief Encode a V1 Manifest. + * + */ + +#ifndef ccnx_common_ccnxCodecSchemaV1_ManifestEncoder_h +#define ccnx_common_ccnxCodecSchemaV1_ManifestEncoder_h + +#include <ccnx/common/internal/ccnx_TlvDictionary.h> +#include <ccnx/common/codec/ccnxCodec_TlvEncoder.h> + +ssize_t ccnxCodecSchemaV1ManifestEncoder_Encode(CCNxCodecTlvEncoder *encoder, CCNxTlvDictionary *packetDictionary); + +#endif // ccnx_common_ccnxCodecSchemaV1_ManifestEncoder_h diff --git a/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_MessageDecoder.c b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_MessageDecoder.c new file mode 100644 index 00000000..f0dea7a6 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_MessageDecoder.c @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> + +#include <LongBow/runtime.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Object.h> +#include <parc/algol/parc_Buffer.h> + +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_Types.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_MessageDecoder.h> + +#include <ccnx/common/codec/ccnxCodec_TlvUtilities.h> +#include <ccnx/common/ccnx_PayloadType.h> +#include <ccnx/common/ccnx_InterestReturn.h> + +static bool +_translateWirePayloadTypeToCCNxPayloadType(CCNxCodecSchemaV1Types_PayloadType wireFormatType, CCNxPayloadType *payloadTypePtr) +{ + bool success = true; + switch (wireFormatType) { + case CCNxCodecSchemaV1Types_PayloadType_Data: + *payloadTypePtr = CCNxPayloadType_DATA; + break; + + case CCNxCodecSchemaV1Types_PayloadType_Key: + *payloadTypePtr = CCNxPayloadType_KEY; + break; + + case CCNxCodecSchemaV1Types_PayloadType_Link: + *payloadTypePtr = CCNxPayloadType_LINK; + break; + + default: + // unknown type + success = false; + } + return success; +} + +/** + * Translates the wire format value for the PayloadType to CCNxPayloadType + */ +static bool +_decodePayloadType(CCNxCodecTlvDecoder *decoder, CCNxTlvDictionary *packetDictionary, uint16_t length) +{ + CCNxPayloadType payloadType; + + uint64_t wireFormatVarInt; + bool success = ccnxCodecTlvDecoder_GetVarInt(decoder, length, &wireFormatVarInt); + if (success) { + CCNxCodecSchemaV1Types_PayloadType wireFormatType = (CCNxCodecSchemaV1Types_PayloadType) wireFormatVarInt; + + success = _translateWirePayloadTypeToCCNxPayloadType(wireFormatType, &payloadType); + } + + if (success) { + success = ccnxTlvDictionary_PutInteger(packetDictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_PAYLOADTYPE, payloadType); + } + + return success; +} + +static bool +_decodeType(CCNxCodecTlvDecoder *decoder, CCNxTlvDictionary *packetDictionary, uint16_t type, uint16_t length) +{ + bool success = false; + switch (type) { + case CCNxCodecSchemaV1Types_CCNxMessage_Name: + success = ccnxCodecTlvUtilities_PutAsName(decoder, packetDictionary, type, length, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_NAME); + break; + + case CCNxCodecSchemaV1Types_CCNxMessage_Payload: + success = ccnxCodecTlvUtilities_PutAsBuffer(decoder, packetDictionary, type, length, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_PAYLOAD); + break; + + case CCNxCodecSchemaV1Types_CCNxMessage_KeyIdRestriction: + success = ccnxCodecTlvUtilities_PutAsHash(decoder, packetDictionary, type, length, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_KEYID_RESTRICTION); + break; + + case CCNxCodecSchemaV1Types_CCNxMessage_ContentObjectHashRestriction: + success = ccnxCodecTlvUtilities_PutAsHash(decoder, packetDictionary, type, length, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_OBJHASH_RESTRICTION); + break; + + case CCNxCodecSchemaV1Types_CCNxMessage_PayloadType: + success = _decodePayloadType(decoder, packetDictionary, length); + break; + + case CCNxCodecSchemaV1Types_CCNxMessage_ExpiryTime: + success = ccnxCodecTlvUtilities_PutAsInteger(decoder, packetDictionary, type, length, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_EXPIRY_TIME); + break; + + case CCNxCodecSchemaV1Types_CCNxMessage_EndChunkNumber: + success = ccnxCodecTlvUtilities_PutAsInteger(decoder, packetDictionary, type, length, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_ENDSEGMENT); + break; + + default: + // if we do not know the TLV type, put it in this container's unknown list + success = ccnxCodecTlvUtilities_PutAsListBuffer(decoder, packetDictionary, type, length, CCNxCodecSchemaV1TlvDictionary_Lists_MESSAGE_LIST); + break; + } + + if (!success) { + CCNxCodecError *error = ccnxCodecError_Create(TLV_ERR_DECODE, __func__, __LINE__, ccnxCodecTlvDecoder_Position(decoder)); + ccnxCodecTlvDecoder_SetError(decoder, error); + ccnxCodecError_Release(&error); + } + + return success; +} + +/* + * We are given a decoder that points to the first TLV of a list of TLVs. We keep walking the + * list until we come to the end of the decoder. + */ +bool +ccnxCodecSchemaV1MessageDecoder_Decode(CCNxCodecTlvDecoder *decoder, CCNxTlvDictionary *packetDictionary) +{ + return ccnxCodecTlvUtilities_DecodeContainer(decoder, packetDictionary, _decodeType); +} diff --git a/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_MessageDecoder.h b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_MessageDecoder.h new file mode 100755 index 00000000..30a998aa --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_MessageDecoder.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file ccnxCodecSchemaV1_MessageDecoder.h + * @brief <#Brief Description#> + * + * Decodes the CCNx message body for an Interest or a ContentObject. + * + * The current CPI Control packet does not use the MessageDecoder or MessageEncoder. It is handled + * entirely in the Packet{De,En}coder. + * + */ + +#ifndef TransportRTA_ccnxCodecSchemaV1_MessageDecoder_h +#define TransportRTA_ccnxCodecSchemaV1_MessageDecoder_h + +#include <stdbool.h> + +#include <ccnx/common/internal/ccnx_TlvDictionary.h> +#include <ccnx/common/codec/ccnxCodec_TlvDecoder.h> + +/** + * The decode a V1 message, maybe any message type + * + * The decoder should point to byte 0 of the message TLV + * The results are put in the provided dictionary. + * It is an error if the message does not extend to the end of + * the decoder. + * + * @param [in] decoder The decoder to parse + * @param [in] contentObjectDictionary The results go directly in to the provided dictionary. + * + * @return true Fully parsed interest, no errors + * @return false Error decoding, decoder is left pointing to the first byte of the error + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool ccnxCodecSchemaV1MessageDecoder_Decode(CCNxCodecTlvDecoder *decoder, CCNxTlvDictionary *contentObjectDictionary); + + +#endif // TransportRTA_ccnxCodecSchemaV1_MessageDecoder_h diff --git a/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_MessageEncoder.c b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_MessageEncoder.c new file mode 100755 index 00000000..7aa14627 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_MessageEncoder.c @@ -0,0 +1,340 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> + +#include <LongBow/runtime.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Buffer.h> + +#include <ccnx/common/ccnx_PayloadType.h> +#include <ccnx/common/ccnx_InterestReturn.h> + +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_ManifestEncoder.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_MessageEncoder.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_NameCodec.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_HashCodec.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_Types.h> + +#include <ccnx/common/codec/ccnxCodec_TlvUtilities.h> + +#include <ccnx/common/ccnx_Manifest.h> +#include <ccnx/common/ccnx_ManifestHashGroup.h> + +static ssize_t +_encodeName(CCNxCodecTlvEncoder *encoder, CCNxTlvDictionary *packetDictionary) +{ + ssize_t length = -1; + CCNxName *name = ccnxTlvDictionary_GetName(packetDictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_NAME); + if (name != NULL) { + length = ccnxCodecSchemaV1NameCodec_Encode(encoder, CCNxCodecSchemaV1Types_CCNxMessage_Name, name); + } + + // required field for everything except CCNxContentObjects + if (!ccnxTlvDictionary_IsContentObject(packetDictionary) && length < 0) { + CCNxCodecError *error = ccnxCodecError_Create(TLV_MISSING_MANDATORY, __func__, __LINE__, ccnxCodecTlvEncoder_Position(encoder)); + ccnxCodecTlvEncoder_SetError(encoder, error); + ccnxCodecError_Release(&error); + } else if (ccnxTlvDictionary_IsContentObject(packetDictionary) && name == NULL) { + length = 0; + } + + return length; +} + +static ssize_t +_encodeJsonPayload(CCNxCodecTlvEncoder *encoder, CCNxTlvDictionary *packetDictionary) +{ + ssize_t length = 0; + PARCJSON *json = ccnxTlvDictionary_GetJson(packetDictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_PAYLOAD); + if (json != NULL) { + char *jsonString = parcJSON_ToCompactString(json); + size_t len = strlen(jsonString); + length = ccnxCodecTlvEncoder_AppendArray(encoder, CCNxCodecSchemaV1Types_CCNxMessage_Payload, len, (uint8_t *) jsonString); + } + return length; +} + +static ssize_t +_encodePayload(CCNxCodecTlvEncoder *encoder, CCNxTlvDictionary *packetDictionary) +{ + ssize_t length = 0; + PARCBuffer *buffer = ccnxTlvDictionary_GetBuffer(packetDictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_PAYLOAD); + if (buffer != NULL) { + length = ccnxCodecTlvEncoder_AppendBuffer(encoder, CCNxCodecSchemaV1Types_CCNxMessage_Payload, buffer); + } + return length; +} + +static ssize_t +_encodePayloadType(CCNxCodecTlvEncoder *encoder, CCNxTlvDictionary *packetDictionary) +{ + ssize_t length = 0; + if (ccnxTlvDictionary_IsValueInteger(packetDictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_PAYLOADTYPE)) { + CCNxPayloadType payloadType = (CCNxPayloadType) ccnxTlvDictionary_GetInteger(packetDictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_PAYLOADTYPE); + + CCNxCodecSchemaV1Types_PayloadType wireFormatType = CCNxCodecSchemaV1Types_PayloadType_Data; + + switch (payloadType) { + case CCNxPayloadType_KEY: + wireFormatType = CCNxCodecSchemaV1Types_PayloadType_Key; + break; + + case CCNxPayloadType_LINK: + wireFormatType = CCNxCodecSchemaV1Types_PayloadType_Link; + break; + + default: + // anything else is encoded as DATA + break; + } + + length = ccnxCodecTlvEncoder_AppendUint8(encoder, CCNxCodecSchemaV1Types_CCNxMessage_PayloadType, wireFormatType); + } else if (ccnxTlvDictionary_IsValueBuffer(packetDictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_PAYLOADTYPE)) { + PARCBuffer *buffer = ccnxTlvDictionary_GetBuffer(packetDictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_PAYLOADTYPE); + length = ccnxCodecTlvEncoder_AppendBuffer(encoder, CCNxCodecSchemaV1Types_CCNxMessage_PayloadType, buffer); + } + + return length; +} + +static ssize_t +_encodeExpiryTime(CCNxCodecTlvEncoder *encoder, CCNxTlvDictionary *packetDictionary) +{ + ssize_t length = 0; + if (ccnxTlvDictionary_IsValueInteger(packetDictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_EXPIRY_TIME)) { + uint64_t millis = ccnxTlvDictionary_GetInteger(packetDictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_EXPIRY_TIME); + length = ccnxCodecTlvEncoder_AppendUint64(encoder, CCNxCodecSchemaV1Types_CCNxMessage_ExpiryTime, millis); + } else if (ccnxTlvDictionary_IsValueBuffer(packetDictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_EXPIRY_TIME)) { + PARCBuffer *buffer = ccnxTlvDictionary_GetBuffer(packetDictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_EXPIRY_TIME); + length = ccnxCodecTlvEncoder_AppendBuffer(encoder, CCNxCodecSchemaV1Types_CCNxMessage_ExpiryTime, buffer); + } + + return length; +} + +static ssize_t +_encodeEndChunkNumber(CCNxCodecTlvEncoder *encoder, CCNxTlvDictionary *packetDictionary) +{ + ssize_t length = 0; + if (ccnxTlvDictionary_IsValueInteger(packetDictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_ENDSEGMENT)) { + uint64_t endChunkId = ccnxTlvDictionary_GetInteger(packetDictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_ENDSEGMENT); + length = ccnxCodecTlvEncoder_AppendVarInt(encoder, CCNxCodecSchemaV1Types_CCNxMessage_EndChunkNumber, endChunkId); + } else { + PARCBuffer *buffer = ccnxTlvDictionary_GetBuffer(packetDictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_ENDSEGMENT); + if (buffer != NULL) { + length = ccnxCodecTlvEncoder_AppendBuffer(encoder, CCNxCodecSchemaV1Types_CCNxMessage_EndChunkNumber, buffer); + } + } + return length; +} + +static ssize_t +_encodeKeyIdRestriction(CCNxCodecTlvEncoder *encoder, CCNxTlvDictionary *packetDictionary) +{ + ssize_t length = 0; + PARCCryptoHash *hash = ccnxTlvDictionary_GetObject(packetDictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_KEYID_RESTRICTION); + if (hash != NULL) { + size_t startPosition = ccnxCodecTlvEncoder_Position(encoder); + ccnxCodecTlvEncoder_AppendContainer(encoder, CCNxCodecSchemaV1Types_CCNxMessage_KeyIdRestriction, 0); + length = ccnxCodecSchemaV1HashCodec_Encode(encoder, hash); + if (length < 0) { + return length; + } + + ccnxCodecTlvEncoder_SetContainerLength(encoder, startPosition, length); + length += 4; // this accounts for the TL fields + } + return length; +} + +static ssize_t +_encodeContentObjectHashRestriction(CCNxCodecTlvEncoder *encoder, CCNxTlvDictionary *packetDictionary) +{ + ssize_t length = 0; + PARCCryptoHash *hash = ccnxTlvDictionary_GetObject(packetDictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_OBJHASH_RESTRICTION); + if (hash != NULL) { + size_t startPosition = ccnxCodecTlvEncoder_Position(encoder); + ccnxCodecTlvEncoder_AppendContainer(encoder, CCNxCodecSchemaV1Types_CCNxMessage_ContentObjectHashRestriction, 0); + length = ccnxCodecSchemaV1HashCodec_Encode(encoder, hash); + if (length < 0) { + return length; + } + + ccnxCodecTlvEncoder_SetContainerLength(encoder, startPosition, length); + length += 4; // this accounts for the TL fields + } + return length; +} + + +static ssize_t +_encodeContentObject(CCNxCodecTlvEncoder *encoder, CCNxTlvDictionary *packetDictionary) +{ + ssize_t length = 0; + ssize_t result; + + result = _encodeName(encoder, packetDictionary); + if (result < 0) { + return result; + } + length += result; + + result = _encodePayloadType(encoder, packetDictionary); + if (result < 0) { + return result; + } + length += result; + + result = _encodeExpiryTime(encoder, packetDictionary); + if (result < 0) { + return result; + } + length += result; + + result = _encodeEndChunkNumber(encoder, packetDictionary); + if (result < 0) { + return result; + } + length += result; + + result = _encodePayload(encoder, packetDictionary); + if (result < 0) { + return result; + } + length += result; + + return length; +} + +static ssize_t +_encodeInterest(CCNxCodecTlvEncoder *encoder, CCNxTlvDictionary *packetDictionary) +{ + ssize_t length = 0; + ssize_t result; + + result = _encodeName(encoder, packetDictionary); + if (result < 0) { + return result; + } + length += result; + + result = _encodeKeyIdRestriction(encoder, packetDictionary); + if (result < 0) { + return result; + } + length += result; + + result = _encodeContentObjectHashRestriction(encoder, packetDictionary); + if (result < 0) { + return result; + } + length += result; + + result = _encodePayload(encoder, packetDictionary); + if (result < 0) { + return result; + } + length += result; + + return length; +} + +static ssize_t +_encodeControl(CCNxCodecTlvEncoder *encoder, CCNxTlvDictionary *packetDictionary) +{ + ssize_t length = 0; + ssize_t result; + + result = _encodeName(encoder, packetDictionary); + if (result < 0) { + return result; + } + length += result; + + result = _encodeJsonPayload(encoder, packetDictionary); + if (result < 0) { + return result; + } + length += result; + + return length; +} + +static ssize_t +_encodeManifest(CCNxCodecTlvEncoder *encoder, CCNxTlvDictionary *packetDictionary) +{ + ssize_t length = 0; + ssize_t result; + + result = _encodeName(encoder, packetDictionary); + if (result < 0) { + return result; + } + length += result; + + result = ccnxCodecSchemaV1ManifestEncoder_Encode(encoder, packetDictionary); + if (result < 0) { + return result; + } + length += result; + + return length; +} + +ssize_t +ccnxCodecSchemaV1MessageEncoder_Encode(CCNxCodecTlvEncoder *encoder, CCNxTlvDictionary *packetDictionary) +{ + assertNotNull(encoder, "Parameter encoder must be non-null"); + assertNotNull(packetDictionary, "Parameter packetDictionary must be non-null"); + + ssize_t length = -1; + + if (ccnxTlvDictionary_IsInterest(packetDictionary)) { + length = _encodeInterest(encoder, packetDictionary); + } else if (ccnxTlvDictionary_IsInterestReturn(packetDictionary)) { + length = _encodeInterest(encoder, packetDictionary); + } else if (ccnxTlvDictionary_IsContentObject(packetDictionary)) { + length = _encodeContentObject(encoder, packetDictionary); + } else if (ccnxTlvDictionary_IsControl(packetDictionary)) { + length = _encodeControl(encoder, packetDictionary); + } else if (ccnxTlvDictionary_IsManifest(packetDictionary)) { + length = _encodeManifest(encoder, packetDictionary); + } else { + CCNxCodecError *error = ccnxCodecError_Create(TLV_ERR_PACKETTYPE, __func__, __LINE__, ccnxCodecTlvEncoder_Position(encoder)); + ccnxCodecTlvEncoder_SetError(encoder, error); + ccnxCodecError_Release(&error); + length = -1; + } + + + if (length >= 0) { + // Put custom fields all last + ssize_t customLength = ccnxCodecTlvUtilities_EncodeCustomList(encoder, packetDictionary, CCNxCodecSchemaV1TlvDictionary_Lists_MESSAGE_LIST); + if (customLength < 0) { + return customLength; + } + length += customLength; + } + + return length; +} diff --git a/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_MessageEncoder.h b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_MessageEncoder.h new file mode 100755 index 00000000..d49417fb --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_MessageEncoder.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file ccnxCodecSchemaV1_MessageEncoder.h + * @brief Encode the list of optional headers + * + */ + +#ifndef TransportRTA_ccnxCodecSchemaV1_MessageEncoder_h +#define TransportRTA_ccnxCodecSchemaV1_MessageEncoder_h + +#include <ccnx/common/internal/ccnx_TlvDictionary.h> +#include <ccnx/common/codec/ccnxCodec_TlvEncoder.h> +#include <parc/security/parc_Signer.h> + +/** + * Encodes the body of the CCNxMessage + * + * Encodes an Interest, ContentObject, or Control message + * + * @param [in] encoder Appends the CCNx message to the encoder + * @param [in] packetDictionary The fields to encode + * + * @return non-negative Total bytes appended to encoder + * @return -1 An error + * + * Example: + * @code + * <#example#> + * @endcode + */ +ssize_t ccnxCodecSchemaV1MessageEncoder_Encode(CCNxCodecTlvEncoder *encoder, CCNxTlvDictionary *packetDictionary); + +#endif // TransportRTA_ccnxCodecSchemaV1_MessageEncoder_h diff --git a/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_NameCodec.c b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_NameCodec.c new file mode 100755 index 00000000..a769477f --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_NameCodec.c @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <LongBow/runtime.h> +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Object.h> +#include <parc/algol/parc_Buffer.h> + +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_NameCodec.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_NameSegmentCodec.h> + +size_t +ccnxCodecSchemaV1NameCodec_Encode(CCNxCodecTlvEncoder *encoder, uint16_t type, const CCNxName *name) +{ + assertNotNull(encoder, "Parameter encoder must be non-null"); + assertNotNull(name, "Parameter name must be non-null"); + + size_t containerPosition = ccnxCodecTlvEncoder_Position(encoder); + size_t containerLength = ccnxCodecTlvEncoder_AppendContainer(encoder, type, 0); + + size_t segmentCount = ccnxName_GetSegmentCount(name); + size_t innerLength = 0; + for (int i = 0; i < segmentCount; i++) { + CCNxNameSegment *segment = ccnxName_GetSegment(name, i); + innerLength += ccnxCodecSchemaV1NameSegmentCodec_Encode(encoder, segment); + } + + // now go back and fixup the container's length + ccnxCodecTlvEncoder_SetContainerLength(encoder, containerPosition, innerLength); + + return containerLength + innerLength; +} + +CCNxName * +ccnxCodecSchemaV1NameCodec_Decode(CCNxCodecTlvDecoder *decoder, uint16_t type) +{ + CCNxName *name = NULL; + if (ccnxCodecTlvDecoder_EnsureRemaining(decoder, 4)) { + uint16_t tlvtype = ccnxCodecTlvDecoder_PeekType(decoder); + if (tlvtype == type) { + // call just for the side-effect of advancing the buffer + (void) ccnxCodecTlvDecoder_GetType(decoder); + uint16_t length = ccnxCodecTlvDecoder_GetLength(decoder); + + name = ccnxCodecSchemaV1NameCodec_DecodeValue(decoder, length); + } + } + + return name; +} + +CCNxName * +ccnxCodecSchemaV1NameCodec_DecodeValue(CCNxCodecTlvDecoder *decoder, uint16_t length) +{ + CCNxName *name = NULL; + if (ccnxCodecTlvDecoder_EnsureRemaining(decoder, length)) { + name = ccnxName_Create(); + size_t nameEnd = ccnxCodecTlvDecoder_Position(decoder) + length; + + while (ccnxCodecTlvDecoder_Position(decoder) < nameEnd) { + CCNxNameSegment *segment = ccnxCodecSchemaV1NameSegmentCodec_Decode(decoder); + ccnxName_Append(name, segment); + ccnxNameSegment_Release(&segment); + } + } + return name; +} diff --git a/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_NameCodec.h b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_NameCodec.h new file mode 100755 index 00000000..6c3298a2 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_NameCodec.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file ccnxCodecSchemaV1_NameCodec.h + * @brief TLV codec for CCNx types + * + * <#Detailed Description#> + * + */ + +#ifndef CCNxCodecSchemaV1_NameCodec_h +#define CCNxCodecSchemaV1_NameCodec_h + +#include <ccnx/common/codec/ccnxCodec_TlvEncoder.h> +#include <ccnx/common/codec/ccnxCodec_TlvDecoder.h> +#include <ccnx/common/ccnx_Name.h> + +/** + * Encodes the name to the TLV Encoder + * + * Will append the Name after the current encoder location + * + * @param [in] type The TLV type to use for the Name container + * + * @return bytes The number of bytes appended to the encoder + * + * Example: + * @code + * <#example#> + * @endcode + */ +size_t ccnxCodecSchemaV1NameCodec_Encode(CCNxCodecTlvEncoder *encoder, uint16_t type, const CCNxName *name); + +/** + * Decode the buffer as a CCNxName beginning at the current position + * + * The buffer must be pointing to the beginnig of the "type". The decoder will + * verify that the type matches `type'. If it does not match, it will return NULL. + * + * @param [in] decoder The decoder + * @param [in] type The TLV type that the decoder should currently be pointing at + * + * @return non-null The CCNxName decoded + * @return null An error: either type did not match or some other error + * + * Example: + * @code + * <#example#> + * @endcode + */ +CCNxName *ccnxCodecSchemaV1NameCodec_Decode(CCNxCodecTlvDecoder *decoder, uint16_t type); + +/** + * The decoder points to the first byte of the Name "value" + * + * <#Paragraphs Of Explanation#> + * + * @param [in] decoder The Tlv Decoder pointing to the start of the Name value + * @param [in] length the length of the Name value + * + * @return non-null A parsed name + * @return null An error + * + * Example: + * @code + * <#example#> + * @endcode + */ +CCNxName *ccnxCodecSchemaV1NameCodec_DecodeValue(CCNxCodecTlvDecoder *decoder, uint16_t length); +#endif // CCNxCodecSchemaV1_NameCodec_h diff --git a/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_NameSegmentCodec.c b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_NameSegmentCodec.c new file mode 100755 index 00000000..55d02bab --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_NameSegmentCodec.c @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <LongBow/runtime.h> +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Buffer.h> + +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_NameSegmentCodec.h> + +size_t +ccnxCodecSchemaV1NameSegmentCodec_Encode(CCNxCodecTlvEncoder *encoder, CCNxNameSegment *segment) +{ + assertTrue(ccnxNameSegment_Length(segment) <= UINT16_MAX, + "Name segment too long! length %zu maximum %u", + ccnxNameSegment_Length(segment), + UINT16_MAX); + + uint16_t segment_type = ccnxNameSegment_GetType(segment); + PARCBuffer *value = ccnxNameSegment_GetValue(segment); + + return ccnxCodecTlvEncoder_AppendBuffer(encoder, segment_type, value); +} + +CCNxNameSegment * +ccnxCodecSchemaV1NameSegmentCodec_Decode(CCNxCodecTlvDecoder *decoder) +{ + CCNxNameSegment *segment = NULL; + + if (ccnxCodecTlvDecoder_EnsureRemaining(decoder, 4)) { + uint16_t type = ccnxCodecTlvDecoder_GetType(decoder); + uint16_t length = ccnxCodecTlvDecoder_GetLength(decoder); + + if (ccnxCodecTlvDecoder_EnsureRemaining(decoder, length)) { + PARCBuffer *value = ccnxCodecTlvDecoder_GetValue(decoder, length); + segment = ccnxNameSegment_CreateTypeValue(type, value); + parcBuffer_Release(&value); + } + } + + return segment; +} diff --git a/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_NameSegmentCodec.h b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_NameSegmentCodec.h new file mode 100755 index 00000000..c6797cff --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_NameSegmentCodec.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file ccnxCodecSchemaV1_NameSegmentCodec.h + * @brief TLV codec for CCNx types + * + * Encode/decode a CCNx name segment using the V1 schema + * + */ + +#ifndef CCNxCodecSchemaV1_NameSegmentCodec_h +#define CCNxCodecSchemaV1_NameSegmentCodec_h + +#include <ccnx/common/codec/ccnxCodec_TlvEncoder.h> +#include <ccnx/common/codec/ccnxCodec_TlvDecoder.h> +#include <ccnx/common/ccnx_Name.h> + +/** + * Encodes the name segment using the segment type as the TLV type + * + * Appends the name segment to the encoder. The TLV type is implicit in + * the CCNxNameSegment. + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return number The number of bytes appended, including the type and length. + * + * Example: + * @code + * <#example#> + * @endcode + */ +size_t ccnxCodecSchemaV1NameSegmentCodec_Encode(CCNxCodecTlvEncoder *encoder, CCNxNameSegment *segment); + +/** + * Decodes the current location of the decoder as a CCNxNameSegment + * + * <#Paragraphs Of Explanation#> + * + * @param [in] decoder The decoder object + * + * @return non-null A CCNxNameSement + * @return null An error, such as buffer underrun + * + * Example: + * @code + * <#example#> + * @endcode + */ +CCNxNameSegment *ccnxCodecSchemaV1NameSegmentCodec_Decode(CCNxCodecTlvDecoder *decoder); +#endif // CCNxCodecSchemaV1_NameSegmentCodec_h diff --git a/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_OptionalHeadersDecoder.c b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_OptionalHeadersDecoder.c new file mode 100755 index 00000000..48554b9a --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_OptionalHeadersDecoder.c @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> + +#include <LongBow/runtime.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Buffer.h> + +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_OptionalHeadersDecoder.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_Types.h> +#include <ccnx/common/codec/ccnxCodec_TlvUtilities.h> + +static bool +_decodeType(CCNxCodecTlvDecoder *decoder, CCNxTlvDictionary *packetDictionary, uint16_t type, uint16_t length) +{ + bool success = false; + switch (type) { + case CCNxCodecSchemaV1Types_OptionalHeaders_InterestFragment: + success = ccnxCodecTlvUtilities_PutAsBuffer(decoder, packetDictionary, type, length, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_INTFRAG); + break; + + case CCNxCodecSchemaV1Types_OptionalHeaders_ContentObjectFragment: + success = ccnxCodecTlvUtilities_PutAsBuffer(decoder, packetDictionary, type, length, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_OBJFRAG); + break; + + case CCNxCodecSchemaV1Types_OptionalHeaders_InterestLifetime: + // its a time, so use an Integer + success = ccnxCodecTlvUtilities_PutAsInteger(decoder, packetDictionary, type, length, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_InterestLifetime); + break; + + case CCNxCodecSchemaV1Types_OptionalHeaders_RecommendedCacheTime: + // its a time, so use an Integer + success = ccnxCodecTlvUtilities_PutAsInteger(decoder, packetDictionary, type, length, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_RecommendedCacheTime); + break; + case CCNxCodecSchemaV1Types_OptionalHeaders_PathLabel: + success = ccnxCodecTlvUtilities_PutAsInteger(decoder, packetDictionary, type, length, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_PathLabel); + break; + + default: { + // if we do not know the TLV type, put it in this container's unknown list + success = ccnxCodecTlvUtilities_PutAsListBuffer(decoder, packetDictionary, type, length, CCNxCodecSchemaV1TlvDictionary_Lists_HEADERS); + } + break; + } + if (!success) { + CCNxCodecError *error = ccnxCodecError_Create(TLV_ERR_DECODE, __func__, __LINE__, ccnxCodecTlvDecoder_Position(decoder)); + ccnxCodecTlvDecoder_SetError(decoder, error); + ccnxCodecError_Release(&error); + } + return success; +} + +/* + * We are given a decoder that points to the first TLV of a list of TLVs. We keep walking the + * list until we come to the end of the decoder. + */ +bool +ccnxCodecSchemaV1OptionalHeadersDecoder_Decode(CCNxCodecTlvDecoder *decoder, CCNxTlvDictionary *packetDictionary) +{ + return ccnxCodecTlvUtilities_DecodeContainer(decoder, packetDictionary, _decodeType); +} + +// ==== Getters + +PARCBuffer * +ccnxCodecSchemaV1OptionalHeadersDecoder_GetInterestFragmentHeader(CCNxTlvDictionary *packetDictionary) +{ + PARCBuffer *buffer = ccnxTlvDictionary_GetBuffer(packetDictionary, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_INTFRAG); + return buffer; +} + +PARCBuffer * +ccnxCodecSchemaV1OptionalHeadersDecoder_GetContentObjectFragmentHeader(CCNxTlvDictionary *packetDictionary) +{ + PARCBuffer *buffer = ccnxTlvDictionary_GetBuffer(packetDictionary, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_OBJFRAG); + return buffer; +} + +uint64_t +ccnxCodecSchemaV1OptionalHeadersDecoder_GetInterestLifetimeHeader(CCNxTlvDictionary *packetDictionary) +{ + uint64_t lifetime = ccnxTlvDictionary_GetInteger(packetDictionary, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_InterestLifetime); + return lifetime; +} + +uint64_t +ccnxCodecSchemaV1OptionalHeadersDecoder_GetRecommendedCacheTimeHeader(CCNxTlvDictionary *packetDictionary) +{ + uint64_t cachetime = ccnxTlvDictionary_GetInteger(packetDictionary, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_RecommendedCacheTime); + return cachetime; +} + +uint64_t +ccnxCodecSchemaV1OptionalHeadersDecoder_GetPathLabel(CCNxTlvDictionary *packetDictionary) +{ + uint64_t pathLabel = ccnxTlvDictionary_GetInteger(packetDictionary, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_PathLabel); + return pathLabel; +} + +PARCBuffer * +ccnxCodecSchemaV1OptionalHeadersDecoder_GetCustomType(CCNxTlvDictionary *packetDictionary, uint32_t key) +{ + PARCBuffer *buffer = ccnxTlvDictionary_ListGetByType(packetDictionary, CCNxCodecSchemaV1TlvDictionary_Lists_HEADERS, key); + return buffer; +} diff --git a/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_OptionalHeadersDecoder.h b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_OptionalHeadersDecoder.h new file mode 100755 index 00000000..94463ad1 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_OptionalHeadersDecoder.h @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file ccnxCodecSchemaV1_OptionalHeadersDecoder.h + * @brief Decode the list of optional headers + * + * A reference to each optional header will be stored in the provided CCNxTlvDictionary. + * + */ + +#ifndef TransportRTA_ccnxCodecSchemaV1_OptionalHeadersDecoder_h +#define TransportRTA_ccnxCodecSchemaV1_OptionalHeadersDecoder_h + +#include <stdbool.h> + +#include <parc/algol/parc_Buffer.h> + +#include <ccnx/common/internal/ccnx_TlvDictionary.h> +#include <ccnx/common/codec/ccnxCodec_TlvDecoder.h> + +/** + * The decode a list of Version 1 optional headers + * + * The decoder should point to the first byte of the first optional header. + * The decoder will advance until the end of the buffer. + * It is an error for the last optional header to either go beyond the end of the + * decoder or for it to underrun the end of the decoder. It must exactly align. + * + * @param [in] decoder The decoder to parse + * @param [in] packetDictionary The results go directly in to the provided dictionary. + * + * @return true Fully parsed interest, no errors + * @return false Error decoding, decoder is left pointing to the first byte of the error + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool ccnxCodecSchemaV1OptionalHeadersDecoder_Decode(CCNxCodecTlvDecoder *decoder, CCNxTlvDictionary *packetDictionary); + +/** + * A convenience function to return the Interest Fragment header buffer + * + * <#Paragraphs Of Explanation#> + * + * @param [in] packetDictionary The packet dictionary + * + * @return non-null The header buffer + * @return null The header does not exist + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCBuffer *ccnxCodecSchemaV1OptionalHeadersDecoder_GetInterestFragmentHeader(CCNxTlvDictionary *packetDictionary); + +/** + * A convenience function to return the Content Object Fragment header buffer + * + * <#Paragraphs Of Explanation#> + * + * @param [in] packetDictionary The packet dictionary + * + * @return non-null The header buffer + * @return null The header does not exist + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCBuffer *ccnxCodecSchemaV1OptionalHeadersDecoder_GetContentObjectFragmentHeader(CCNxTlvDictionary *packetDictionary); + +/** + * A convenience function to return the Interest Lifetime header value + * + * Returns the byte array of the Interest Lifetime, which is encoded as a uint64_t milli-seconds + * since the UTC epoch. The PARCBuffer returned wraps the underlying memory so any changes to the + * buffer will be reflected in the header. + * + * @param [in] packetDictionary The packet dictionary + * + * @return number The Interest Lifetime + * + * Example: + * @code + * <#example#> + * @endcode + */ +uint64_t ccnxCodecSchemaV1OptionalHeadersDecoder_GetInterestLifetimeHeader(CCNxTlvDictionary *packetDictionary); + +/** + * A convenience function to return the Recommended Cache Time (RCT) header + * + * Returns the byte array of the RCT, which is encoded as a uint64_t milli-seconds + * since the UTC epoch. The PARCBuffer returned wraps the underlying memory so any changes to the + * buffer will be reflected in the header. + * + * @param [in] packetDictionary The packet dictionary + * + * @return number The Recommended Cache Time + * + * Example: + * @code + * <#example#> + * @endcode + */ +uint64_t ccnxCodecSchemaV1OptionalHeadersDecoder_GetRecommendedCacheTimeHeader(CCNxTlvDictionary *packetDictionary); + +uint64_t ccnxCodecSchemaV1OptionalHeadersDecoder_GetRecommendedPathLabel(CCNxTlvDictionary *packetDictionary); + +/** + * Retrieves a TLV header that is not part of the V1 schema spec + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return non-NULL The header + * @return NULL The header field does not exist + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCBuffer *ccnxCodecSchemaV1OptionalHeadersDecoder_GetCustomType(CCNxTlvDictionary *packetDictionary, uint32_t key); + +#endif // TransportRTA_ccnxCodecSchemaV1_OptionalHeadersDecoder_h diff --git a/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_OptionalHeadersEncoder.c b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_OptionalHeadersEncoder.c new file mode 100755 index 00000000..f108a1c3 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_OptionalHeadersEncoder.c @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> + +#include <LongBow/runtime.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Buffer.h> + +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_OptionalHeadersEncoder.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_Types.h> +#include <ccnx/common/codec/ccnxCodec_TlvUtilities.h> + +static ssize_t +_EncodeInterestLifetime(CCNxCodecTlvEncoder *optionalHeadersEncoder, CCNxTlvDictionary *packetDictionary) +{ + ssize_t length = 0; + + // allow either encoding as an Integer or as a Buffer + + if (ccnxTlvDictionary_IsValueInteger(packetDictionary, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_InterestLifetime)) { + uint64_t lifetime = ccnxTlvDictionary_GetInteger(packetDictionary, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_InterestLifetime); + length = ccnxCodecTlvEncoder_AppendVarInt(optionalHeadersEncoder, CCNxCodecSchemaV1Types_OptionalHeaders_InterestLifetime, lifetime); + } else if (ccnxTlvDictionary_IsValueBuffer(packetDictionary, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_InterestLifetime)) { + PARCBuffer *lifetime = ccnxTlvDictionary_GetBuffer(packetDictionary, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_InterestLifetime); + length = ccnxCodecTlvEncoder_AppendBuffer(optionalHeadersEncoder, CCNxCodecSchemaV1Types_OptionalHeaders_InterestLifetime, lifetime); + } + + return length; +} + +static ssize_t +_EncodeRecommendedCacheTime(CCNxCodecTlvEncoder *optionalHeadersEncoder, CCNxTlvDictionary *packetDictionary) +{ + ssize_t length = 0; + + // allow either encoding as an Integer or as a Buffer + + if (ccnxTlvDictionary_IsValueInteger(packetDictionary, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_RecommendedCacheTime)) { + uint64_t cacheTime = ccnxTlvDictionary_GetInteger(packetDictionary, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_RecommendedCacheTime); + length = ccnxCodecTlvEncoder_AppendVarInt(optionalHeadersEncoder, CCNxCodecSchemaV1Types_OptionalHeaders_RecommendedCacheTime, cacheTime); + } else if (ccnxTlvDictionary_IsValueBuffer(packetDictionary, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_RecommendedCacheTime)) { + PARCBuffer *cacheTime = ccnxTlvDictionary_GetBuffer(packetDictionary, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_RecommendedCacheTime); + length = ccnxCodecTlvEncoder_AppendBuffer(optionalHeadersEncoder, CCNxCodecSchemaV1Types_OptionalHeaders_RecommendedCacheTime, cacheTime); + } + + return length; +} + +static ssize_t +_EncodePathLabel(CCNxCodecTlvEncoder *optionalHeadersEncoder, CCNxTlvDictionary *packetDictionary) +{ + ssize_t length = 0; + + if (ccnxTlvDictionary_IsValueInteger(packetDictionary, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_PathLabel)) { + uint16_t pathLabel = ccnxTlvDictionary_GetInteger(packetDictionary, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_PathLabel); + length = ccnxCodecTlvEncoder_AppendVarInt(optionalHeadersEncoder, CCNxCodecSchemaV1Types_OptionalHeaders_PathLabel, pathLabel); + } else if (ccnxTlvDictionary_IsValueBuffer(packetDictionary, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_PathLabel)) { + PARCBuffer *pathLabel = ccnxTlvDictionary_GetBuffer(packetDictionary, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_PathLabel); + length = ccnxCodecTlvEncoder_AppendBuffer(optionalHeadersEncoder, CCNxCodecSchemaV1Types_OptionalHeaders_PathLabel, pathLabel); + } + + return length; +} + +static ssize_t +_EncodeInterestFrag(CCNxCodecTlvEncoder *optionalHeadersEncoder, CCNxTlvDictionary *packetDictionary) +{ + ssize_t length = 0; + PARCBuffer *buffer = ccnxTlvDictionary_GetBuffer(packetDictionary, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_INTFRAG); + if (buffer != NULL) { + length = ccnxCodecTlvEncoder_AppendBuffer(optionalHeadersEncoder, CCNxCodecSchemaV1Types_OptionalHeaders_InterestFragment, buffer); + } + return length; +} + +static ssize_t +_EncodeContentObjectFrag(CCNxCodecTlvEncoder *optionalHeadersEncoder, CCNxTlvDictionary *packetDictionary) +{ + ssize_t length = 0; + PARCBuffer *buffer = ccnxTlvDictionary_GetBuffer(packetDictionary, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_OBJFRAG); + if (buffer != NULL) { + length = ccnxCodecTlvEncoder_AppendBuffer(optionalHeadersEncoder, CCNxCodecSchemaV1Types_OptionalHeaders_ContentObjectFragment, buffer); + } + return length; +} + +static ssize_t +_EncodeInterestHeaders(CCNxCodecTlvEncoder *optionalHeadersEncoder, CCNxTlvDictionary *packetDictionary) +{ + ssize_t length = 0; + ssize_t intFragLength = _EncodeInterestFrag(optionalHeadersEncoder, packetDictionary); + if (intFragLength < 0) { + return intFragLength; + } + length += intFragLength; + + ssize_t intLifeLength = _EncodeInterestLifetime(optionalHeadersEncoder, packetDictionary); + if (intLifeLength < 0) { + return intLifeLength; + } + length += intLifeLength; + + ssize_t customLength = ccnxCodecTlvUtilities_EncodeCustomList(optionalHeadersEncoder, packetDictionary, CCNxCodecSchemaV1TlvDictionary_Lists_HEADERS); + if (customLength < 0) { + return customLength; + } + length += customLength; + + return length; +} + +static ssize_t +_EncodeContentObjectHeaders(CCNxCodecTlvEncoder *optionalHeadersEncoder, CCNxTlvDictionary *packetDictionary) +{ + ssize_t length = 0; + ssize_t result; + + result = _EncodeContentObjectFrag(optionalHeadersEncoder, packetDictionary); + if (result < 0) { + return result; + } + length += result; + + result = _EncodeRecommendedCacheTime(optionalHeadersEncoder, packetDictionary); + if (result < 0) { + return result; + } + length += result; + + result = _EncodePathLabel(optionalHeadersEncoder, packetDictionary); + if (result < 0) { + return result; + } + length += result; + + result = ccnxCodecTlvUtilities_EncodeCustomList(optionalHeadersEncoder, packetDictionary, CCNxCodecSchemaV1TlvDictionary_Lists_HEADERS); + if (result < 0) { + return result; + } + length += result; + + return length; +} + + +ssize_t +ccnxCodecSchemaV1OptionalHeadersEncoder_Encode(CCNxCodecTlvEncoder *optionalHeadersEncoder, CCNxTlvDictionary *packetDictionary) +{ + assertNotNull(optionalHeadersEncoder, "Parameter optionalHeadersEncoder must be non-null"); + assertNotNull(packetDictionary, "Parameter packetDictionary must be non-null"); + + ssize_t result = 0; + if (ccnxTlvDictionary_IsInterest(packetDictionary) || ccnxTlvDictionary_IsInterestReturn(packetDictionary)) { + result = _EncodeInterestHeaders(optionalHeadersEncoder, packetDictionary); + } else if (ccnxTlvDictionary_IsContentObject(packetDictionary) || ccnxTlvDictionary_IsManifest(packetDictionary)) { + result = _EncodeContentObjectHeaders(optionalHeadersEncoder, packetDictionary); + } else if (ccnxTlvDictionary_IsControl(packetDictionary)) { + result = ccnxCodecTlvUtilities_EncodeCustomList(optionalHeadersEncoder, packetDictionary, CCNxCodecSchemaV1TlvDictionary_Lists_HEADERS); + } else { + CCNxCodecError *error = ccnxCodecError_Create(TLV_ERR_PACKETTYPE, __func__, __LINE__, ccnxCodecTlvEncoder_Position(optionalHeadersEncoder)); + ccnxCodecTlvEncoder_SetError(optionalHeadersEncoder, error); + ccnxCodecError_Release(&error); + result = -1; + } + + return result; +} diff --git a/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_OptionalHeadersEncoder.h b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_OptionalHeadersEncoder.h new file mode 100755 index 00000000..3f7632ca --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_OptionalHeadersEncoder.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file ccnxCodecSchemaV1_OptionalHeadersEncoder.h + * @brief Encode the list of optional headers + * + */ + +#ifndef TransportRTA_ccnxCodecSchemaV1_OptionalHeadersEncoder_h +#define TransportRTA_ccnxCodecSchemaV1_OptionalHeadersEncoder_h + +#include <ccnx/common/internal/ccnx_TlvDictionary.h> +#include <ccnx/common/codec/ccnxCodec_TlvEncoder.h> + +/** + * Appends the Optional Headers to the encoderder + * + * <#Paragraphs Of Explanation#> + * + * @param [in] encoder An allocated encoder to append to + * @param [in] packetDictionary The dictionary containing the optional headers + * + * @return non-negative Total bytes appended to encoder + * @return -1 An error + * + * Example: + * @code + * <#example#> + * @endcode + */ +ssize_t ccnxCodecSchemaV1OptionalHeadersEncoder_Encode(CCNxCodecTlvEncoder *encoder, CCNxTlvDictionary *packetDictionary); + +#endif // TransportRTA_ccnxCodecSchemaV1_OptionalHeadersEncoder_h diff --git a/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_PacketDecoder.c b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_PacketDecoder.c new file mode 100644 index 00000000..692b3291 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_PacketDecoder.c @@ -0,0 +1,289 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +#include <config.h> +#include <stdio.h> +#include <sys/time.h> + +#include <LongBow/runtime.h> +#include <parc/algol/parc_Memory.h> +#include <ccnx/common/codec/ccnxCodec_TlvEncoder.h> +#include <ccnx/common/internal/ccnx_WireFormatFacadeV1.h> + +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_PacketDecoder.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_Types.h> + +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_FixedHeader.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_OptionalHeadersDecoder.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_MessageDecoder.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_ManifestDecoder.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_ValidationDecoder.h> + +typedef struct rta_tlv_schema_v1_data { + CCNxCodecTlvDecoder *decoder; + CCNxTlvDictionary *packetDictionary; +} _CCNxCodecSchemaV1Data; + +/** + * Decodes the per-hop optional headers + * + * @param [in] data The packet decoder state + * + * @return true successful decode + * @return false A decoding error + * + * Example: + * @code + * <#example#> + * @endcode + */ +static bool +_decodeOptionalHeaders(_CCNxCodecSchemaV1Data *data) +{ + size_t optionalHeaderLength = ccnxCodecSchemaV1FixedHeaderDecoder_GetOptionalHeaderLength(data->packetDictionary); + CCNxCodecTlvDecoder *optionalHeaderDecoder = ccnxCodecTlvDecoder_GetContainer(data->decoder, optionalHeaderLength); + + bool success = ccnxCodecSchemaV1OptionalHeadersDecoder_Decode(optionalHeaderDecoder, data->packetDictionary); + + ccnxCodecTlvDecoder_Destroy(&optionalHeaderDecoder); + return success; +} + +/** + * Decodes the "value" of the CPI "TLV" + * + * the CPI packet is encoded as a single TLV container of type 0xBEEF (detected in _decodeMessage). + * At this point, the cpiDecoder wraps the CPI payload, which is the encapsulated JSON + * + * @param [in] cpiDecoder Decoder wrapping the value + * @param [in] packetDictionary where to place the results + * + * @retval true Good decode + * @retval false An error + * + * Example: + * @code + * <#example#> + * @endcode + */ +static bool +_decodeCPI(CCNxCodecTlvDecoder *cpiDecoder, CCNxTlvDictionary *packetDictionary) +{ + // we just take the whole contents of the decoder and put in the the PAYLOAD dictionary entry. + size_t length = ccnxCodecTlvDecoder_Remaining(cpiDecoder); + PARCBuffer *payload = ccnxCodecTlvDecoder_GetValue(cpiDecoder, length); + + PARCJSON *json = parcJSON_ParseBuffer(payload); + + bool success = ccnxTlvDictionary_PutJson(packetDictionary, + CCNxCodecSchemaV1TlvDictionary_MessageFastArray_PAYLOAD, json); + parcJSON_Release(&json); + parcBuffer_Release(&payload); + return success; +} + +/** + * Decodes the CCNx message inside a TLV packet + * + * Creates an inner decoder that slices the decode buffer then passes that and our + * message dictionary to the appropriate inner decoder. + * + * @param [in] data The packet decoder state + * + * @return true successful decode + * @return false A decoding error + * + * Example: + * @code + * <#example#> + * @endcode + */ +static bool +_decodeMessage(_CCNxCodecSchemaV1Data *data) +{ + bool success = false; + + if (ccnxCodecTlvDecoder_EnsureRemaining(data->decoder, 4)) { + // what kind of message are we looking at? + // Note that this is based on the TLV container, not the fixed head PacketType + uint16_t tlv_type = ccnxCodecTlvDecoder_GetType(data->decoder); + uint16_t tlv_length = ccnxCodecTlvDecoder_GetLength(data->decoder); + + // ensure its a proper tlv type + switch (tlv_type) { + case CCNxCodecSchemaV1Types_MessageType_Interest: // fallthrough + case CCNxCodecSchemaV1Types_MessageType_ContentObject: // fallthrough + case CCNxCodecSchemaV1Types_MessageType_Control: // fallthrough + case CCNxCodecSchemaV1Types_MessageType_Manifest: // fallthrough + break; + + default: + return false; + } + + // cross check with the fixed header value + // ccnxCodecSchemaV1FixedHeaderDecoder_Decode ensures that PacketLength is not less than HeaderLength + size_t messageLength = ccnxCodecSchemaV1FixedHeaderDecoder_GetPacketLength(data->packetDictionary) - ccnxCodecSchemaV1FixedHeaderDecoder_GetHeaderLength(data->packetDictionary); + + if (tlv_length <= messageLength && ccnxCodecTlvDecoder_EnsureRemaining(data->decoder, tlv_length)) { + // This decode is for the "value" of the message, it does not include the wrapper + CCNxCodecTlvDecoder *messageDecoder = ccnxCodecTlvDecoder_GetContainer(data->decoder, tlv_length); + + if (tlv_type == CCNxCodecSchemaV1Types_MessageType_Control) { + // the CPI messages are not a proper "message" in that there's no inner TLV, its just data + success = _decodeCPI(messageDecoder, data->packetDictionary); + } else if (tlv_type == CCNxCodecSchemaV1Types_MessageType_Manifest) { + ccnxTlvDictionary_SetMessageType_Manifest(data->packetDictionary, CCNxTlvDictionary_SchemaVersion_V1); + success = ccnxCodecSchemaV1ManifestDecoder_Decode(messageDecoder, data->packetDictionary); + } else { + success = ccnxCodecSchemaV1MessageDecoder_Decode(messageDecoder, data->packetDictionary); + } + + ccnxCodecTlvDecoder_Destroy(&messageDecoder); + } else { + // raise an error + CCNxCodecError *error = ccnxCodecError_Create(TLV_ERR_TOO_LONG, __func__, __LINE__, ccnxCodecTlvDecoder_Position(data->decoder)); + ccnxCodecTlvDecoder_SetError(data->decoder, error); + ccnxCodecError_Release(&error); + } + } + + return success; +} + +static bool +_decodeValidationAlg(_CCNxCodecSchemaV1Data *data) +{ + bool success = false; + + if (ccnxCodecTlvDecoder_EnsureRemaining(data->decoder, 4)) { + // what kind of message are we looking at? + // Note that this is based on the TLV container, not the fixed head PacketType + uint16_t tlv_type = ccnxCodecTlvDecoder_GetType(data->decoder); + uint16_t tlv_length = ccnxCodecTlvDecoder_GetLength(data->decoder); + + if (tlv_type == CCNxCodecSchemaV1Types_MessageType_ValidationAlg && + ccnxCodecTlvDecoder_EnsureRemaining(data->decoder, tlv_length)) { + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_GetContainer(data->decoder, tlv_length); + + success = ccnxCodecSchemaV1ValidationDecoder_DecodeAlg(decoder, data->packetDictionary); + + ccnxCodecTlvDecoder_Destroy(&decoder); + } else { + // raise and error + if (!ccnxCodecTlvDecoder_EnsureRemaining(data->decoder, tlv_length)) { + // tlv_length goes beyond the decoder + CCNxCodecError *error = ccnxCodecError_Create(TLV_ERR_TOO_LONG, __func__, __LINE__, ccnxCodecTlvDecoder_Position(data->decoder)); + ccnxCodecTlvDecoder_SetError(data->decoder, error); + ccnxCodecError_Release(&error); + } else { + // not CCNxCodecSchemaV1Types_MessageType_ValidationAlg + CCNxCodecError *error = ccnxCodecError_Create(TLV_ERR_DECODE, __func__, __LINE__, ccnxCodecTlvDecoder_Position(data->decoder)); + ccnxCodecTlvDecoder_SetError(data->decoder, error); + ccnxCodecError_Release(&error); + } + } + } + + return success; +} + +static bool +_decodeValidationPayload(_CCNxCodecSchemaV1Data *data) +{ + bool success = false; + + if (ccnxCodecTlvDecoder_EnsureRemaining(data->decoder, 4)) { + // what kind of message are we looking at? + // Note that this is based on the TLV container, not the fixed head PacketType + uint16_t tlv_type = ccnxCodecTlvDecoder_GetType(data->decoder); + uint16_t tlv_length = ccnxCodecTlvDecoder_GetLength(data->decoder); + + if (tlv_type == CCNxCodecSchemaV1Types_MessageType_ValidationPayload && + ccnxCodecTlvDecoder_EnsureRemaining(data->decoder, tlv_length)) { + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_GetContainer(data->decoder, tlv_length); + + success = ccnxCodecSchemaV1ValidationDecoder_DecodePayload(decoder, data->packetDictionary); + + ccnxCodecTlvDecoder_Destroy(&decoder); + } + } + + return success; +} + +bool +ccnxCodecSchemaV1PacketDecoder_Decode(CCNxCodecTlvDecoder *packetDecoder, CCNxTlvDictionary *packetDictionary) +{ + bool decodeSuccess = false; + + _CCNxCodecSchemaV1Data data; + + // we temporarily store this reference, but we do not destroy it. This + // is just to pass the reference down the decode chain, it is not + // stored beyond the immediate scope. Therefore, no reference acquired. + data.packetDictionary = packetDictionary; + data.decoder = packetDecoder; + + if (ccnxCodecSchemaV1FixedHeaderDecoder_Decode(data.decoder, data.packetDictionary)) { + if (_decodeOptionalHeaders(&data)) { + // Record the position we'd start the signature verification at + size_t signatureStartPosition = ccnxCodecTlvDecoder_Position(data.decoder); + + + // Mark the beginning of the ContentObject hash region. + CCNxWireFormatFacadeV1_Implementation.setContentObjectHashRegionStart(data.packetDictionary, signatureStartPosition); + + if (_decodeMessage(&data)) { + // If there's anything else left, it must be the validation alg and payload + if (!ccnxCodecTlvDecoder_IsEmpty(data.decoder)) { + if (_decodeValidationAlg(&data)) { + // at this point, we've advanced to the end of the validation algorithm, + // that's where we would end signature verification + size_t signatureStopPosition = ccnxCodecTlvDecoder_Position(data.decoder); + + CCNxWireFormatFacadeV1_Implementation.setProtectedRegionStart(data.packetDictionary, signatureStartPosition); + CCNxWireFormatFacadeV1_Implementation.setProtectedRegionLength(data.packetDictionary, signatureStopPosition - signatureStartPosition); + + if (_decodeValidationPayload(&data)) { + decodeSuccess = true; + } + } + } else { + // nothing after the message, so that's a successful decode + decodeSuccess = true; + } + + // Mark the length of the ContentObject hash region (to the end of the packet). + size_t contentObjectHashRegionLength = ccnxCodecTlvDecoder_Position(data.decoder) - signatureStartPosition; + CCNxWireFormatFacadeV1_Implementation.setContentObjectHashRegionLength(data.packetDictionary, contentObjectHashRegionLength); + } + } + } + + return decodeSuccess; +} + +bool +ccnxCodecSchemaV1PacketDecoder_BufferDecode(PARCBuffer *packetBuffer, CCNxTlvDictionary *packetDictionary) +{ + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(packetBuffer); + bool success = ccnxCodecSchemaV1PacketDecoder_Decode(decoder, packetDictionary); + ccnxCodecTlvDecoder_Destroy(&decoder); + return success; +} diff --git a/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_PacketDecoder.h b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_PacketDecoder.h new file mode 100755 index 00000000..9c1defd9 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_PacketDecoder.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file ccnxCodecSchemaV1_PacketDecoder.h + * @brief Decoder for the version 1 TLV Packet + * + * The Schema version 1 Dictionary is organized in containers: FixedHeader, OptionalHeaders, (Interest, ContentObject, Control), Verification. + * + * Each container is its own dictionary. + * + * Example: + * @code + * { + * CCNxTlvDictionary *packetDictionary = ccnxTlvDictionary_Create(); + * ccnxCodecSchemaV1PacketDecoder_Decode(packetBuffer, packetDictionary); + * // the fields in the packetDictionary are now set + * } + * @endcode + * + */ + +#ifndef CCNxCodecSchemaV1_PacketDecoder_h +#define CCNxCodecSchemaV1_PacketDecoder_h + +#include <parc/algol/parc_Buffer.h> +#include <ccnx/common/internal/ccnx_TlvDictionary.h> +#include <ccnx/common/codec/ccnxCodec_TlvDecoder.h> + + +/** + * Decode a packet in to a dictionary. + * + * The buffer should be set at the start of the fixed header. This call is equivalent + * to ccnxCodecSchemaV1PacketDecoder_Decode(), except it allocates and destroys a temporary + * CCNxCodecTlvDecoder. + * + * The dictionary will be filled in with all fields available in the packetBuffer. + * + * Caveat: there is no way to find out where the error was if returned "false" + * + * @param [in] buffer The packet buffer + * @param [in] packetDictionary The dictionary to fill in + * + * @return true Successful decode + * @return false There was an error somewhere + * + * Example: + * @code + * { + * } + * @endcode + */ +bool ccnxCodecSchemaV1PacketDecoder_BufferDecode(PARCBuffer *packetBuffer, CCNxTlvDictionary *packetDictionary); + +/** + * Decode in to in to a dictionary. + * + * The buffer should be set at the start of the fixed header. + * + * The dictionary will be filled in with all fields available in the packetDecoder. + * + * Caveat: there is no way to find out where the error was if returned "false" + * + * @param [in] buffer The packet buffer + * @param [in] packetDictionary The dictionary to fill in + * + * @return true Successful decode + * @return false There was an error somewhere + * + * Example: + * @code + * { + * } + * @endcode + */ +bool ccnxCodecSchemaV1PacketDecoder_Decode(CCNxCodecTlvDecoder *packetDecoder, CCNxTlvDictionary *packetDictionary); + +#endif // CCNxCodecSchemaV1_PacketDecoder_h diff --git a/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_PacketEncoder.c b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_PacketEncoder.c new file mode 100755 index 00000000..bddd32d4 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_PacketEncoder.c @@ -0,0 +1,353 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +#include <config.h> +#include <stdio.h> +#include <sys/time.h> +#include <inttypes.h> + +#include <LongBow/runtime.h> +#include <parc/algol/parc_Memory.h> + +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_Types.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_PacketEncoder.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_FixedHeaderEncoder.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_OptionalHeadersEncoder.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_MessageEncoder.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_ValidationEncoder.h> + +#include <ccnx/common/internal/ccnx_InterestDefault.h> +#include <ccnx/common/internal/ccnx_ValidationFacadeV1.h> +#include <ccnx/common/internal/ccnx_WireFormatMessageInterface.h> + +// ===================================================== +// Private API + +static uint8_t +_getHopLimit(CCNxTlvDictionary *packetDictionary) +{ + uint8_t hoplimit = (uint8_t) CCNxInterestDefault_HopLimit; + + if (ccnxTlvDictionary_IsValueInteger(packetDictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_HOPLIMIT)) { + hoplimit = (uint8_t) ccnxTlvDictionary_GetInteger(packetDictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_HOPLIMIT); + } + return hoplimit; +} + +static uint8_t +_getInterestReturnCode(CCNxTlvDictionary *packetDictionary) +{ + uint8_t returnCode = (uint8_t) 0; + + if (ccnxTlvDictionary_IsValueInteger(packetDictionary, + CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_InterestReturnCode)) { + returnCode = + (uint8_t) ccnxTlvDictionary_GetInteger(packetDictionary, + CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_InterestReturnCode); + } + return returnCode; +} + +/** + * Creates a fixed header from the given parameters and encodes in network byte order + * + * All parameters in host byte order. + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return non-negative The total bytes appended to the encode buffer + * @return -1 An error + * + * Example: + * @code + * <#example#> + * @endcode + */ +static ssize_t +_encodeFixedHeader(CCNxCodecTlvEncoder *fixedHeaderEncoder, + CCNxTlvDictionary *packetDictionary, + int packetType, + ssize_t headerLength, + ssize_t packetLength) +{ + CCNxCodecSchemaV1FixedHeader fixedHeader; + memset(&fixedHeader, 0, sizeof(fixedHeader)); + + fixedHeader.version = 1; + fixedHeader.packetType = packetType; + fixedHeader.packetLength = packetLength; + fixedHeader.headerLength = headerLength; + + if ((packetType == CCNxCodecSchemaV1Types_PacketType_Interest) || + (packetType == CCNxCodecSchemaV1Types_PacketType_InterestReturn)) { + CCNxCodecSchemaV1InterestHeader *interestHeader = (CCNxCodecSchemaV1InterestHeader *) &fixedHeader; + interestHeader->hopLimit = _getHopLimit(packetDictionary); + if (packetType == CCNxCodecSchemaV1Types_PacketType_InterestReturn) { + interestHeader->returnCode = _getInterestReturnCode(packetDictionary); + } + } + + return ccnxCodecSchemaV1FixedHeaderEncoder_EncodeHeader(fixedHeaderEncoder, &fixedHeader); +} + +static ssize_t +_encodeOptionalHeaders(CCNxCodecTlvEncoder *optionalHeaderEncoder, CCNxTlvDictionary *packetDictionary) +{ + // Optional Headers do not have a container, so just append them right to the buffer + size_t optionalHeadersLength = 0; + optionalHeadersLength = ccnxCodecSchemaV1OptionalHeadersEncoder_Encode(optionalHeaderEncoder, packetDictionary); + return optionalHeadersLength; +} + +/** + * CPI payload is simply a dump of the PAYLOAD dictionary entry. + * + * There are no inner TLVs of this message, so it is not encoded like a normal message + * with a call to ccnxCodecSchemaV1MessageEncoder_Encode(). Rather it is written here. + * + * @param [<#in#> | <#out#> | <#in,out#>] <#name#> <#description#> + * + * @return non-negative The number of bytes appended to the buffer + * @return negative An error + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +static ssize_t +_encodeCPI(CCNxCodecTlvEncoder *cpiEncoder, CCNxTlvDictionary *packetDictionary) +{ + // Optional Headers do not have a container, so just append them right to the buffer + size_t payloadLength = 0; + + if (ccnxTlvDictionary_IsValueJson(packetDictionary, + CCNxCodecSchemaV1TlvDictionary_MessageFastArray_PAYLOAD)) { + PARCJSON *json = ccnxTlvDictionary_GetJson(packetDictionary, + CCNxCodecSchemaV1TlvDictionary_MessageFastArray_PAYLOAD); + if (json) { + char *jsonString = parcJSON_ToCompactString(json); + + payloadLength = strlen(jsonString); + ccnxCodecTlvEncoder_AppendRawArray(cpiEncoder, payloadLength, (uint8_t * ) jsonString); + parcMemory_Deallocate((void **) &jsonString); + } + } else { + PARCBuffer *payload = ccnxTlvDictionary_GetBuffer(packetDictionary, + CCNxCodecSchemaV1TlvDictionary_MessageFastArray_PAYLOAD); + + payloadLength = parcBuffer_Remaining(payload); + uint8_t *overlay = parcBuffer_Overlay(payload, 0); + ccnxCodecTlvEncoder_AppendRawArray(cpiEncoder, payloadLength, overlay); + } + return payloadLength; +} + +/** + * Encode the CCNx Message + * + * <#Paragraphs Of Explanation#> + * + * @param [out] packetTypePtr The type to use for the PacketType based on the message type + * + * @retval non-negative the bytes appended to the encoder + * @retval negative An error + * + * Example: + * @code + * <#example#> + * @endcode + */ +static ssize_t +_encodeMessage(CCNxCodecTlvEncoder *packetEncoder, CCNxTlvDictionary *packetDictionary, CCNxCodecSchemaV1Types_PacketType *packetTypePtr) +{ + ssize_t startPosition = ccnxCodecTlvEncoder_Position(packetEncoder); + ssize_t innerLength = -1; + + // what kind of message is it? need this to set the packetTypePtr + + if (ccnxTlvDictionary_IsInterest(packetDictionary)) { + *packetTypePtr = CCNxCodecSchemaV1Types_PacketType_Interest; + ccnxCodecTlvEncoder_AppendContainer(packetEncoder, CCNxCodecSchemaV1Types_MessageType_Interest, 0); + innerLength = ccnxCodecSchemaV1MessageEncoder_Encode(packetEncoder, packetDictionary); + } else if (ccnxTlvDictionary_IsInterestReturn(packetDictionary)) { + *packetTypePtr = CCNxCodecSchemaV1Types_PacketType_InterestReturn; + ccnxCodecTlvEncoder_AppendContainer(packetEncoder, CCNxCodecSchemaV1Types_MessageType_Interest, 0); + innerLength = ccnxCodecSchemaV1MessageEncoder_Encode(packetEncoder, packetDictionary); + } else if (ccnxTlvDictionary_IsContentObject(packetDictionary)) { + *packetTypePtr = CCNxCodecSchemaV1Types_PacketType_ContentObject; + ccnxCodecTlvEncoder_AppendContainer(packetEncoder, CCNxCodecSchemaV1Types_MessageType_ContentObject, 0); + innerLength = ccnxCodecSchemaV1MessageEncoder_Encode(packetEncoder, packetDictionary); + } else if (ccnxTlvDictionary_IsControl(packetDictionary)) { + *packetTypePtr = CCNxCodecSchemaV1Types_PacketType_Control; + ccnxCodecTlvEncoder_AppendContainer(packetEncoder, CCNxCodecSchemaV1Types_MessageType_Control, 0); + innerLength = _encodeCPI(packetEncoder, packetDictionary); + } else if (ccnxTlvDictionary_IsManifest(packetDictionary)) { + *packetTypePtr = CCNxCodecSchemaV1Types_PacketType_ContentObject; + ccnxCodecTlvEncoder_AppendContainer(packetEncoder, CCNxCodecSchemaV1Types_MessageType_Manifest, 0); + innerLength = ccnxCodecSchemaV1MessageEncoder_Encode(packetEncoder, packetDictionary); + } + + if (innerLength >= 0) { + // For a 0 length message, we do not backup and erase the TLV container. + ccnxCodecTlvEncoder_SetContainerLength(packetEncoder, startPosition, innerLength); + ssize_t endPosition = ccnxCodecTlvEncoder_Position(packetEncoder); + innerLength = endPosition - startPosition; + } else { + CCNxCodecError *error = ccnxCodecError_Create(TLV_MISSING_MANDATORY, __func__, __LINE__, ccnxCodecTlvEncoder_Position(packetEncoder)); + ccnxCodecTlvEncoder_SetError(packetEncoder, error); + ccnxCodecError_Release(&error); + } + + return innerLength; +} + +static ssize_t +_encodeValidationAlg(CCNxCodecTlvEncoder *encoder, CCNxTlvDictionary *packetDictionary) +{ + ssize_t innerLength = 0; + + // There must be a CryptoSuite in the packet to sign it. + // Temporary exception for Content Objects, which are all signed if the codec has a signer. + if (ccnxValidationFacadeV1_HasCryptoSuite(packetDictionary) || ccnxTlvDictionary_IsContentObject(packetDictionary)) { + ssize_t startPosition = ccnxCodecTlvEncoder_Position(encoder); + + ccnxCodecTlvEncoder_AppendContainer(encoder, CCNxCodecSchemaV1Types_MessageType_ValidationAlg, 0); + innerLength = ccnxCodecSchemaV1ValidationEncoder_EncodeAlg(encoder, packetDictionary); + + if (innerLength == 0) { + // backup and erase the container + ccnxCodecTlvEncoder_SetPosition(encoder, startPosition); + } else if (innerLength >= 0) { + ccnxCodecTlvEncoder_SetContainerLength(encoder, startPosition, innerLength); + ssize_t endPosition = ccnxCodecTlvEncoder_Position(encoder); + return endPosition - startPosition; + } + } + + return innerLength; +} + +static ssize_t +_encodeValidationPayload(CCNxCodecTlvEncoder *encoder, CCNxTlvDictionary *packetDictionary) +{ + ssize_t startPosition = ccnxCodecTlvEncoder_Position(encoder); + + ccnxCodecTlvEncoder_AppendContainer(encoder, CCNxCodecSchemaV1Types_MessageType_ValidationPayload, 0); + ssize_t innerLength = ccnxCodecSchemaV1ValidationEncoder_EncodePayload(encoder, packetDictionary); + + if (innerLength == 0) { + // backup and erase the container + ccnxCodecTlvEncoder_SetPosition(encoder, startPosition); + } else if (innerLength > 0) { + ccnxCodecTlvEncoder_SetContainerLength(encoder, startPosition, innerLength); + ssize_t endPosition = ccnxCodecTlvEncoder_Position(encoder); + return endPosition - startPosition; + } + + return innerLength; +} + +// ===================================================== +// Public API + +CCNxCodecNetworkBufferIoVec * +ccnxCodecSchemaV1PacketEncoder_DictionaryEncode(CCNxTlvDictionary *packetDictionary, PARCSigner *signer) +{ + CCNxCodecNetworkBufferIoVec *outputBuffer = NULL; + + CCNxCodecTlvEncoder *packetEncoder = ccnxCodecTlvEncoder_Create(); + + if (signer) { +// ccnxCodecTlvEncoder_SetSigner(packetEncoder, signer); + } + + ssize_t encodedLength = ccnxCodecSchemaV1PacketEncoder_Encode(packetEncoder, packetDictionary); + if (encodedLength > 0) { + ccnxCodecTlvEncoder_Finalize(packetEncoder); + outputBuffer = ccnxCodecTlvEncoder_CreateIoVec(packetEncoder); + } + + trapUnexpectedStateIf(encodedLength < 0 && !ccnxCodecTlvEncoder_HasError(packetEncoder), + "Got error length but no error set"); + + assertFalse(ccnxCodecTlvEncoder_HasError(packetEncoder), "ENCODING ERROR") + { + printf("ERROR: %s\n", ccnxCodecError_ToString(ccnxCodecTlvEncoder_GetError(packetEncoder))); + ccnxTlvDictionary_Display(packetDictionary, 3); + } + + ccnxCodecTlvEncoder_Destroy(&packetEncoder); + + // return a reference counted copy so it won't be destroyed by ccnxCodecTlvEncoder_Destroy + return outputBuffer; +} + +ssize_t +ccnxCodecSchemaV1PacketEncoder_Encode(CCNxCodecTlvEncoder *packetEncoder, CCNxTlvDictionary *packetDictionary) +{ + ssize_t length = -1; + + // We will need to go back and fixedup the headers + ssize_t fixedHeaderPosition = ccnxCodecTlvEncoder_Position(packetEncoder); + ssize_t fixedHeaderLength = _encodeFixedHeader(packetEncoder, packetDictionary, -1, 0, 0); + + ssize_t optionalHeadersLength = _encodeOptionalHeaders(packetEncoder, packetDictionary); + + if (optionalHeadersLength >= 0) { + ccnxCodecTlvEncoder_MarkSignatureStart(packetEncoder); + + CCNxCodecSchemaV1Types_PacketType messageType = -1; + + ssize_t messageLength = _encodeMessage(packetEncoder, packetDictionary, &messageType); + + if (messageLength >= 0) { + // validation is optional, so it's ok if its 0 length + ssize_t validationAlgLength = _encodeValidationAlg(packetEncoder, packetDictionary); + ssize_t validationPayloadLength = 0; + if (validationAlgLength > 0) { + ccnxCodecTlvEncoder_MarkSignatureEnd(packetEncoder); + + validationPayloadLength = _encodeValidationPayload(packetEncoder, packetDictionary); + } + + if (validationAlgLength >= 0 && validationPayloadLength >= 0) { + // now fix up the fixed header + size_t endPosition = ccnxCodecTlvEncoder_Position(packetEncoder); + + size_t headerLength = fixedHeaderLength + optionalHeadersLength; + size_t packetLength = headerLength + messageLength + validationAlgLength + validationPayloadLength; + + // Will this work for InterestReturn? As long as _encodeMessage returns InterestReturn it + // will be ok. + int packetType = messageType; + + ccnxCodecTlvEncoder_SetPosition(packetEncoder, fixedHeaderPosition); + _encodeFixedHeader(packetEncoder, packetDictionary, packetType, headerLength, packetLength); + ccnxCodecTlvEncoder_SetPosition(packetEncoder, endPosition); + length = endPosition - fixedHeaderPosition; + + trapUnexpectedStateIf(packetLength != length, "packet length %zu not equal to measured length %zd", packetLength, length); + } + } + } + + return length; +} diff --git a/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_PacketEncoder.h b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_PacketEncoder.h new file mode 100755 index 00000000..c83f165f --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_PacketEncoder.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file ccnxCodecSchemaV1_PacketEncoder.h + * @brief Encoder for the version 1 TLV Packet + * + * Example: + * @code + * { + * } + * @endcode + * + */ + +#ifndef CCNxCodecSchemaV1_PacketEncoder_h +#define CCNxCodecSchemaV1_PacketEncoder_h + +#include <parc/algol/parc_Buffer.h> +#include <parc/security/parc_Signer.h> + +#include <ccnx/common/internal/ccnx_TlvDictionary.h> +#include <ccnx/common/codec/ccnxCodec_TlvEncoder.h> +#include <ccnx/common/codec/ccnxCodec_EncodingBuffer.h> +#include <ccnx/common/codec/ccnxCodec_NetworkBuffer.h> + +/** + * Encode the packetDictionary to wire format + * + * Will only use the PacketType from FixedHeader in the dictionary, if provided. The packet Version is fixed at "1", + * the PayloadLength and HeaderLength are calculated. If the FixedHeaderDictionary is not provided, the + * PacketType is inferred from the type of CCNx message. + * + * The signer is not stored beyond the call to DictionaryEncode. + * If the dictionary already has a ValidationAlg and ValidationPayload, those are used, not the Signer. + * Otherwise, if the signer is not null, it is used to sign the wire format. + * + * @param [in] packetDictionary The dictionary representation of the packet to encode + * @param [in] signer If not NULL will be used to sign the wire format + * + * @retval non-null An IoVec that can be written to the network + * @retval null an error + * + * Example: + * @code + * <#example#> + * @endcode + */ +CCNxCodecNetworkBufferIoVec *ccnxCodecSchemaV1PacketEncoder_DictionaryEncode(CCNxTlvDictionary *packetDictionary, PARCSigner *signer); + +/** + * Encode a packetDictionary to wire format. + * + * Will only use the PacketType from FixedHeader in the dictionary, if provided. The packet Version is fixed at "1", + * the PayloadLength and HeaderLength are calculated. If the FixedHeaderDictionary is not provided, the + * PacketType is inferred from the type of CCNx message. + * + * You must use ccnxCodecTlvEncoder_SetSigner(signer) if you require a signature or MAC on the packet. + * + * @param [in] packetEncoder A TLV packet will be appended to the encoder + * @param [in] packetDictionary The dictionary representation of the packet to encode + * + * @retval non-negative The total bytes appended to the encode buffer + * @retval -1 An error + * + * Example: + * @code + * <#example#> + * @endcode + */ +ssize_t ccnxCodecSchemaV1PacketEncoder_Encode(CCNxCodecTlvEncoder *packetEncoder, CCNxTlvDictionary *packetDictionary); + +#endif // CCNxCodecSchemaV1_PacketEncoder_h diff --git a/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_TlvDictionary.c b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_TlvDictionary.c new file mode 100644 index 00000000..72e1ce3e --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_TlvDictionary.c @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +#include <config.h> +#include <stdio.h> +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Object.h> + +#include <LongBow/runtime.h> +#include <ccnx/common/ccnx_PayloadType.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_TlvDictionary.h> + +CCNxTlvDictionary * +ccnxCodecSchemaV1TlvDictionary_CreateInterest(void) +{ + CCNxTlvDictionary *dictionary = ccnxTlvDictionary_Create(CCNxCodecSchemaV1TlvDictionary_MessageFastArray_END, CCNxCodecSchemaV1TlvDictionary_Lists_END); + ccnxTlvDictionary_SetMessageType_Interest(dictionary, CCNxTlvDictionary_SchemaVersion_V1); + return dictionary; +} + +CCNxTlvDictionary * +ccnxCodecSchemaV1TlvDictionary_CreateContentObject(void) +{ + CCNxTlvDictionary *dictionary = ccnxTlvDictionary_Create(CCNxCodecSchemaV1TlvDictionary_MessageFastArray_END, CCNxCodecSchemaV1TlvDictionary_Lists_END); + ccnxTlvDictionary_SetMessageType_ContentObject(dictionary, CCNxTlvDictionary_SchemaVersion_V1); + return dictionary; +} + +CCNxTlvDictionary * +ccnxCodecSchemaV1TlvDictionary_CreateManifest(void) +{ + CCNxTlvDictionary *dictionary = ccnxTlvDictionary_Create(CCNxCodecSchemaV1TlvDictionary_MessageFastArray_END, CCNxCodecSchemaV1TlvDictionary_Lists_END); + ccnxTlvDictionary_SetMessageType_Manifest(dictionary, CCNxTlvDictionary_SchemaVersion_V1); + + return dictionary; +} + +CCNxTlvDictionary * +ccnxCodecSchemaV1TlvDictionary_CreateControl(void) +{ + CCNxTlvDictionary *dictionary = ccnxTlvDictionary_Create(CCNxCodecSchemaV1TlvDictionary_MessageFastArray_END, CCNxCodecSchemaV1TlvDictionary_Lists_END); + ccnxTlvDictionary_SetMessageType_Control(dictionary, CCNxTlvDictionary_SchemaVersion_V1); + return dictionary; +} + +CCNxTlvDictionary * +ccnxCodecSchemaV1TlvDictionary_CreateInterestReturn(void) +{ + CCNxTlvDictionary *dictionary = ccnxTlvDictionary_Create(CCNxCodecSchemaV1TlvDictionary_MessageFastArray_END, CCNxCodecSchemaV1TlvDictionary_Lists_END); + ccnxTlvDictionary_SetMessageType_InterestReturn(dictionary, CCNxTlvDictionary_SchemaVersion_V1); + return dictionary; +} + diff --git a/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_TlvDictionary.h b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_TlvDictionary.h new file mode 100755 index 00000000..e76492b6 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_TlvDictionary.h @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file ccnxCodecSchemaV1_TlvDictionary.h + * @brief <#Brief Description#> + * + * Used as keys to the CCNxTlvDictionary for the version 1 schema + * + */ + +#ifndef libccnx_ccnx_TlvDictionary_SchemaV1_h +#define libccnx_ccnx_TlvDictionary_SchemaV1_h + +#include <ccnx/common/internal/ccnx_TlvDictionary.h> + +/** + * @typedef CCNxCodecSchemaV1TlvDictionary_CryptoSuite + * @abstract The ValidationAlgorithm Type. + * @constant <#name#> <#description#> + * @discussion These are the wire-format values for the ValidationAlgorithm type. The values + * specified follow from the CCNx Messages RFC. + * + * It is not the same as the value stored in CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_CRYPTO_SUITE, + * which is of type PARCCryptoSuite. + */ +typedef enum rta_tlv_schema_v1_crypto_suite { + CCNxCodecSchemaV1TlvDictionary_CryptoSuite_CRC32C = 2, + CCNxCodecSchemaV1TlvDictionary_CryptoSuite_HmacSha256 = 4, + CCNxCodecSchemaV1TlvDictionary_CryptoSuite_RsaSha256 = 6, + CCNxCodecSchemaV1TlvDictionary_CryptoSuite_EcSecp256K1 = 7, +} CCNxCodecSchemaV1TlvDictionary_CryptoSuite; + +/** + * @typedef <#CCNBHeaderType#> + * @abstract <#Abstract#> + * @constant CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_WireFormat The off-the-wire packet or a pre-encoded packet + * @discussion + * The WireFormat header is a ficticious header for holding the off-the-wire packet received + * from the network or to send a pre-encoded packet down through the stack. + * + * The Forwarder header is a ficticious header for holding special forwarder control block. The + * forwarder control block, on ingress, contains information about where a packet arrived. On + * egress, it contains information about how the packet should be transmitted, such as restricting + * it to a specific egress interface. + * + * The protected region extent is used to determine they byte range used for verification. + */ + + +typedef enum rta_tlv_schema_v1_headers_fastarray { + CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_TransportStack = 0, /**< Array element 0 is used by RTA Transport stack */ + CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_FixedHeader = 1, + CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_INTFRAG = 2, + CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_OBJFRAG = 3, + CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_WireFormat = 4, + CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_Forwarder = 5, + CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_InterestLifetime = 6, + CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_RecommendedCacheTime = 7, + CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_ProtectedStart = 8, /**< Fictitious header for Protected Region Extent */ + CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_ProtectedLength = 9, /**< Fictitious header for Protected Region length */ + CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_ContentObjectHashRegionStart = 10, /**< Fictitious header for CO Hash Region Extent */ + CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_ContentObjectHashRegionLength = 11, /**< Fictitious header for CO Hash Region length */ + CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_InterestReturnCode = 12, + CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_PathLabel = 13, + CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_END = 14 +} CCNxCodecSchemaV1TlvDictionary_HeadersFastArray; + +/** + * The ValidationFastArray are fields that may appear in the Validation Algorithm and the Validation Payload field. + * + * Note that the ValidationFastArray_CRYPTO_SUITE is always expressed in terms of PARCCryptoSuite. + */ +typedef enum rta_tlv_schema_v1_validation_fastarray { + CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_KEYID = CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_END + 0, + CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_CRYPTO_SUITE = CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_END + 1, // PARCCryptoSuite value + CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_KEY = CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_END + 2, + CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_CERT = CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_END + 3, + CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_KEYNAME_NAME = CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_END + 4, + CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_KEYNAME_KEYID = CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_END + 5, + CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_KEYNAME_OBJHASH = CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_END + 6, + CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_PAYLOAD = CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_END + 7, + CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_SIGNTIME = CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_END + 8, + CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_END = CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_END + 9 +} CCNxCodecSchemaV1TlvDictionary_ValidationFastArray; + + +/** + * The MessageFastArray are fields that may appear in the body of a CCNx message (Interest, Content Object, Control). + * + * The Hop Limit is part of the MessageFastArray even though it appears in the FixedHeader. It is treated like a property + * of the Interest. + */ +typedef enum rta_tlv_schema_v1_message_fastarray { + CCNxCodecSchemaV1TlvDictionary_MessageFastArray_NAME = CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_END + 0, + CCNxCodecSchemaV1TlvDictionary_MessageFastArray_KEYID_RESTRICTION = CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_END + 1, + CCNxCodecSchemaV1TlvDictionary_MessageFastArray_OBJHASH_RESTRICTION = CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_END + 2, + CCNxCodecSchemaV1TlvDictionary_MessageFastArray_PAYLOAD = CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_END + 4, + CCNxCodecSchemaV1TlvDictionary_MessageFastArray_HOPLIMIT = CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_END + 5, /***< Virtual field */ + CCNxCodecSchemaV1TlvDictionary_MessageFastArray_PAYLOADTYPE = CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_END + 6, + CCNxCodecSchemaV1TlvDictionary_MessageFastArray_EXPIRY_TIME = CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_END + 7, + CCNxCodecSchemaV1TlvDictionary_MessageFastArray_ENDSEGMENT = CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_END + 8, + CCNxCodecSchemaV1TlvDictionary_MessageFastArray_HASH_GROUP = CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_END + 9, + CCNxCodecSchemaV1TlvDictionary_MessageFastArray_DATA_POINTER = CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_END + 10, + CCNxCodecSchemaV1TlvDictionary_MessageFastArray_MANIFEST_POINTER = CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_END + 11, + CCNxCodecSchemaV1TlvDictionary_MessageFastArray_END = CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_END + 12 +} CCNxCodecSchemaV1TlvDictionary_MessageFastArray; + +//const int CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_PathLabel = CCNxCodecSchemaV1TlvDictionary_MessageFastArray_END + 1; + +/** + * Each TLV container can have custom types in it, so each container has a "list" + * Organization Extensions go here. + */ +typedef enum rta_tlv_schema_v1_lists { + CCNxCodecSchemaV1TlvDictionary_Lists_HEADERS = 0, + CCNxCodecSchemaV1TlvDictionary_Lists_MESSAGE_LIST = 1, + CCNxCodecSchemaV1TlvDictionary_Lists_VALIDATION_ALG_LIST = 4, + CCNxCodecSchemaV1TlvDictionary_Lists_VALIDATION_PAYLOAD_LIST = 5, + CCNxCodecSchemaV1TlvDictionary_Lists_HASH_GROUP_LIST = 6, + CCNxCodecSchemaV1TlvDictionary_Lists_END = 7 +} CCNxCodecSchemaV1TlvDictionary_Lists; + +/** + * Creates an empty Interest dictionary + * + * The dictionary schema will be V1 and the dictionary type will be Interest. No other + * fields are pre-populated. + * + * @retval non-null An allocated Dictionary of type Interest + * @retval null An error (likely no memory) + * + * Example: + * @code + * { + * // in a decoder + * if (messageType == _DecoderTlvType_Interest) { + * CCNxTlvDictionary *interest = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + * // decode the rest of the packet + * return interest; + * } + * } + * @endcode + */ +CCNxTlvDictionary *ccnxCodecSchemaV1TlvDictionary_CreateInterest(void); + +/** + * Creates an empty Content Object dictionary + * + * The dictionary schema will be V1 and the dictionary type will be Content Object. No other + * fields are pre-populated. + * + * @retval non-null An allocated Dictionary of type Content Object + * @retval null An error (likely no memory) + * + * Example: + * @code + * { + * // in a decoder + * if (messageType == _DecoderTlvType_ContentObject) { + * CCNxTlvDictionary *object = ccnxCodecSchemaV1TlvDictionary_CreateContentObject(); + * // decode the rest of the packet + * return object; + * } + * } + * @endcode + */ +CCNxTlvDictionary *ccnxCodecSchemaV1TlvDictionary_CreateContentObject(void); + +/** + * Creates an empty Manifest dictionary + * + * The dictionary schema will be V1 and the dictionary type will be Content Object. The + * PayloadType will be set to CCNxPayloadType_MANIFEST. No other fields are pre-populated. + * + * @retval non-null An allocated Dictionary of type Manifest + * @retval null An error (likely no memory) + * + * Example: + * @code + * { + * // in a decoder + * if (messageType == _DecoderTlvType_Manifest) { + * CCNxTlvDictionary *object = ccnxCodecSchemaV1TlvDictionary_Manifest(); + * // decode the rest of the packet + * return object; + * } + * } + * @endcode + */ +CCNxTlvDictionary *ccnxCodecSchemaV1TlvDictionary_CreateManifest(void); + +/** + * Creates an empty Control dictionary + * + * The dictionary schema will be V1 and the dictionary type will be Control. No other + * fields are pre-populated. + * + * @retval non-null An allocated Dictionary of type Control + * @retval null An error (likely no memory) + * + * Example: + * @code + * { + * // in a decoder + * if (messageType == _DecoderTlvType_Control) { + * CCNxTlvDictionary *control = ccnxCodecSchemaV1TlvDictionary_CreateControl(); + * // decode the rest of the packet + * return control; + * } + * } + * @endcode + */ +CCNxTlvDictionary *ccnxCodecSchemaV1TlvDictionary_CreateControl(void); + +/** + * Creates an empty InterestReturn dictionary + * + * The dictionary schema will be V1 and the dictionary type will be InterestReturn. No other + * fields are pre-populated. + * + * @retval non-null An allocated Dictionary of type InterestReturn + * @retval null An error (likely no memory) + * + * Example: + * @code + * { + * // in a decoder + * if (messageType == _DecoderTlvType_InterestReturn) { + * CCNxTlvDictionary *interestReturn = ccnxCodecSchemaV1TlvDictionary_CreateInterestReturn(); + * // decode the rest of the packet + * return interestReturn; + * } + * } + * @endcode + */ +CCNxTlvDictionary *ccnxCodecSchemaV1TlvDictionary_CreateInterestReturn(void); +#endif // libccnx_ccnx_TlvDictionary_SchemaV1_h diff --git a/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_Types.h b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_Types.h new file mode 100755 index 00000000..1c50eebc --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_Types.h @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file ccnxCodecSchemaV1_Types.h + * @brief Common definitions for Schema version 1 + * + * Defines the TLV "type" values for each field + * + */ + +#ifndef TransportRTA_ccnxCodecSchemaV1_Types_h +#define TransportRTA_ccnxCodecSchemaV1_Types_h + +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_TlvDictionary.h> + +/** + * @typedef CCNxCodecSchemaV1Types_PacketType + * @abstract The values used in the PacketType field of the Fixed Header + * @constant <#name#> <#description#> + * @discussion <#Discussion#> + */ +typedef enum rta_tlv_schema_v1_packet_type { + CCNxCodecSchemaV1Types_PacketType_Interest = 0x00, + CCNxCodecSchemaV1Types_PacketType_ContentObject = 0x01, + CCNxCodecSchemaV1Types_PacketType_InterestReturn = 0x02, + CCNxCodecSchemaV1Types_PacketType_Control = 0xA4, +} CCNxCodecSchemaV1Types_PacketType; + +/** + * @typedef CCNxCodecSchemaV1Types_MessageType + * @abstract The values used in the MessageType field of the CCNx Message body + * @constant <#name#> <#description#> + * @discussion <#Discussion#> + */ +typedef enum rta_tlv_schema_v1_message_type { + CCNxCodecSchemaV1Types_MessageType_Interest = 0x0001, + CCNxCodecSchemaV1Types_MessageType_ContentObject = 0x0002, + CCNxCodecSchemaV1Types_MessageType_ValidationAlg = 0x0003, + CCNxCodecSchemaV1Types_MessageType_ValidationPayload = 0x0004, + CCNxCodecSchemaV1Types_MessageType_Manifest = 0x0006, + CCNxCodecSchemaV1Types_MessageType_Control = 0xBEEF, +} CCNxCodecSchemaV1Types_MessageType; + +/** + * @typedef CCNxCodecSchemaV1Types_OptionalHeadersTypes + * @abstract The well-known keys for the hop-by-hop headers + * @constant <#name#> <#description#> + * @discussion <#Discussion#> + */ +typedef enum rta_tlv_schema_v1_optional_headers_types { + CCNxCodecSchemaV1Types_OptionalHeaders_InterestLifetime = 0x0001, + CCNxCodecSchemaV1Types_OptionalHeaders_RecommendedCacheTime = 0x0002, + CCNxCodecSchemaV1Types_OptionalHeaders_PathLabel = 0x0003, + CCNxCodecSchemaV1Types_OptionalHeaders_InterestFragment = 0x0004, + CCNxCodecSchemaV1Types_OptionalHeaders_ContentObjectFragment = 0x0005, +} CCNxCodecSchemaV1Types_OptionalHeaders; + +/** + * @typedef CCNxCodecSchemaV1Types_PayloadType + * @abstract The values of the PayloadType field + * @constant <#name#> <#description#> + * @discussion <#Discussion#> + */ +typedef enum rta_tlv_schema_v1_payloadtype_types { + CCNxCodecSchemaV1Types_PayloadType_Data = 0, + CCNxCodecSchemaV1Types_PayloadType_Key = 1, + CCNxCodecSchemaV1Types_PayloadType_Link = 2, +} CCNxCodecSchemaV1Types_PayloadType; + +// ================================================== +// Fields in a Message Object + +/** + * @typedef CCNxCodecSchemaV1Types_MessageTypes + * @abstract The well-known types inside the CCNxMessage + * @constant <#name#> <#description#> + * @discussion <#Discussion#> + */ +typedef enum rta_tlv_schema_v1_ccnxmessage_types { + CCNxCodecSchemaV1Types_CCNxMessage_Name = 0x0000, + CCNxCodecSchemaV1Types_CCNxMessage_Payload = 0x0001, + CCNxCodecSchemaV1Types_CCNxMessage_KeyIdRestriction = 0x0002, + CCNxCodecSchemaV1Types_CCNxMessage_ContentObjectHashRestriction = 0x0003, + CCNxCodecSchemaV1Types_CCNxMessage_PayloadType = 0x0005, + CCNxCodecSchemaV1Types_CCNxMessage_ExpiryTime = 0x0006, + CCNxCodecSchemaV1Types_CCNxMessage_HashGroup = 0x0007, + CCNxCodecSchemaV1Types_CCNxMessage_EndChunkNumber = 0x0019, +} CCNxCodecSchemaV1Types_CCNxMessage; + +typedef enum rta_tlv_schema_v1_ccnxmanifest_hashgroup_types { + CCNxCodecSchemaV1Types_CCNxManifestHashGroup_Metadata = 0x0001, + CCNxCodecSchemaV1Types_CCNxManifestHashGroup_DataPointer = 0x0002, + CCNxCodecSchemaV1Types_CCNxManifestHashGroup_ManifestPointer = 0x0003, +} CCNxCodecSchemaV1Types_CCNxManifestHashGroup; + +typedef enum rta_tlv_schema_v1_ccnxmanifest_hashgroup_metadata_types { + CCNxCodecSchemaV1Types_CCNxManifestHashGroupMetadata_Locator = 0x0000, + CCNxCodecSchemaV1Types_CCNxManifestHashGroupMetadata_DataSize = 0x0001, + CCNxCodecSchemaV1Types_CCNxManifestHashGroupMetadata_BlockSize = 0x0002, + CCNxCodecSchemaV1Types_CCNxManifestHashGroupMetadata_EntrySize = 0x0003, + CCNxCodecSchemaV1Types_CCNxManifestHashGroupMetadata_TreeHeight = 0x0004, + CCNxCodecSchemaV1Types_CCNxManifestHashGroupMetadata_OverallDataSha256 = 0x0005, +} CCNxCodecSchemaV1Types_CCNxManifestHashGroupMetadata; + +// ================================================== +// Fields in a Validation Algorithm + +/** + * @typedef CCNxCodecSchemaV1Types_ValidationAlg + * @abstract The well-known keys for the ContentObject + * @constant <#name#> <#description#> + * @discussion the CCNxCodecSchemaV1Types_ValidationAlg values for the crypto suites must be the same as CCNxCodecSchemaV1Types_CryptoSuiteType + */ +typedef enum rta_tlv_schema_v1_validation_alg { + CCNxCodecSchemaV1Types_ValidationAlg_CRC32C = CCNxCodecSchemaV1TlvDictionary_CryptoSuite_CRC32C, // 0x0002 + CCNxCodecSchemaV1Types_ValidationAlg_HMAC_SHA256 = CCNxCodecSchemaV1TlvDictionary_CryptoSuite_HmacSha256, // 0x0004 + CCNxCodecSchemaV1Types_ValidationAlg_RSA_SHA256 = CCNxCodecSchemaV1TlvDictionary_CryptoSuite_RsaSha256, // 0x0006 + CCNxCodecSchemaV1Types_ValidationAlg_EC_SECP_256K1 = CCNxCodecSchemaV1TlvDictionary_CryptoSuite_EcSecp256K1, // 0x0007 + + CCNxCodecSchemaV1Types_ValidationAlg_KeyId = 0x0009, + CCNxCodecSchemaV1Types_ValidationAlg_PublicKey = 0x000B, + CCNxCodecSchemaV1Types_ValidationAlg_Cert = 0x000C, + CCNxCodecSchemaV1Types_ValidationAlg_KeyName = 0x000E, + CCNxCodecSchemaV1Types_ValidationAlg_SigTime = 0x000F, +} CCNxCodecSchemaV1Types_ValidationAlg; + +/** + * @typedef CCNxCodecSchemaV1Types_Link + * @abstract The well-known keys for the LINK body + * @constant <#name#> <#description#> + * @discussion <#Discussion#> + */ +typedef enum rta_tlv_schema_v1_link_types { + CCNxCodecSchemaV1Types_Link_Name = 0x0000, + CCNxCodecSchemaV1Types_Link_KeyIdRestriction = 0x0001, + CCNxCodecSchemaV1Types_Link_ContentObjectHashRestriction = 0x0002, +} CCNxCodecSchemaV1Types_Link; + +/** + * @typedef CCNxCodecSchemaV1Types_HashGroup + * @abstract The well-known keys for the Manifest HashGroup. + * @constant <#name#> <#description#> + * @discussion <#Discussion#> + */ +typedef enum rta_tlv_schema_v1_link_hash_group { + CCNxCodecSchemaV1Types_HashGroup_Metadata = 0x0001, + CCNxCodecSchemaV1Types_HashGroup_DataPointer = 0x0002, + CCNxCodecSchemaV1Types_HashGroup_ManifestPointer = 0x0003 +} CCNxCodecSchemaV1Types_HashGroup; + +/** + * @typedef CCNxCodecSchemaV1Types_HashGroup + * @abstract The well-known keys for the Manifest HashGroup. + * @constant <#name#> <#description#> + * @discussion <#Discussion#> + */ +typedef enum rta_tlv_schema_v1_link_hash_group_metadata { + CCNxCodecSchemaV1Types_HashGroup_Locator = 0x0000, + CCNxCodecSchemaV1Types_HashGroup_ExternalMetadata = 0x0001, + CCNxCodecSchemaV1Types_HashGroup_BlockSize = 0x0002, + CCNxCodecSchemaV1Types_HashGroup_OverallDataSize = 0x0003, + CCNxCodecSchemaV1Types_HashGroup_OverallDataSha256 = 0x0004, +} CCNxCodecSchemaV1Types_HashGroupMetadata; + +// ================================================== +// Interest Return + +/** + * @typedef CCNxCodecSchemaV1Types_InterestReturnCode + * @abstract The values of the InterestReturn ReturnCode field + * @constant <#name#> <#description#> + * @discussion <#Discussion#> + */ +typedef enum rta_tlv_schema_v1_interestreturncode_types { + CCNxCodecSchemaV1Types_InterestReturnCode_NoRoute = 0x01, + CCNxCodecSchemaV1Types_InterestReturnCode_HopLimitExceeded = 0x02, + CCNxCodecSchemaV1Types_InterestReturnCode_NoResources = 0x03, + CCNxCodecSchemaV1Types_InterestReturnCode_PathError = 0x04, + CCNxCodecSchemaV1Types_InterestReturnCode_Prohibited = 0x05, + CCNxCodecSchemaV1Types_InterestReturnCode_Congestion = 0x06, + CCNxCodecSchemaV1Types_InterestReturnCode_MTUTooLarge = 0x07, +} CCNxCodecSchemaV1Types_InterestReturnCode; + +// ================================================== +// Hash function types +/** + * @typedef CCNxCodecSchemaV1Types_HashTypes + * @abstract The values of the InterestReturn ReturnCode field + * @constant <#name#> <#description#> + * @discussion <#Discussion#> + */ +typedef enum rta_tlv_schema_v1_hash_types { + CCNxCodecSchemaV1Types_HashType_SHA256 = 0x01, + CCNxCodecSchemaV1Types_HashType_SHA512 = 0x02, + CCNxCodecSchemaV1Types_HashType_App +} CCNxCodecSchemaV1Types_HashType; + +#endif //TransportRTA_ccnxCodecSchemaV1_Types_h diff --git a/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_ValidationDecoder.c b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_ValidationDecoder.c new file mode 100755 index 00000000..0524bcfa --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_ValidationDecoder.c @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> + +#include <LongBow/runtime.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Buffer.h> + +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_ValidationDecoder.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_Types.h> +#include <ccnx/common/codec/ccnxCodec_TlvUtilities.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_LinkCodec.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_CryptoSuite.h> + +static bool +_decodeKeyName(CCNxCodecTlvDecoder *decoder, CCNxTlvDictionary *packetDictionary, uint16_t type, uint16_t length) +{ + // At this point, the decoder should point to the 1st byte of the "value" of the (type, length) continer. + // This is defined as a CCNxLink + + bool success = false; + + // this will set the decoder error if it fails. + CCNxLink *link = ccnxCodecSchemaV1LinkCodec_DecodeValue(decoder, length); + if (link != NULL) { + const CCNxName *name = ccnxLink_GetName(link); + if (name != NULL) { + success = ccnxTlvDictionary_PutName(packetDictionary, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_KEYNAME_NAME, name); + + if (success) { + PARCBuffer *keyid = ccnxLink_GetKeyID(link); + if (keyid) { + ccnxTlvDictionary_PutBuffer(packetDictionary, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_KEYNAME_KEYID, keyid); + } + + PARCBuffer *hash = ccnxLink_GetContentObjectHash(link); + if (hash) { + ccnxTlvDictionary_PutBuffer(packetDictionary, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_KEYNAME_OBJHASH, hash); + } + } + } + + if (!success) { + CCNxCodecError *error = ccnxCodecError_Create(TLV_ERR_DECODE, __func__, __LINE__, ccnxCodecTlvDecoder_Position(decoder)); + ccnxCodecTlvDecoder_SetError(decoder, error); + ccnxCodecError_Release(&error); + } + + ccnxLink_Release(&link); + } + + return success; +} + +static bool +_decodeAlgParametersType(CCNxCodecTlvDecoder *decoder, CCNxTlvDictionary *packetDictionary, uint16_t type, uint16_t length) +{ + bool success = false; + switch (type) { + case CCNxCodecSchemaV1Types_ValidationAlg_Cert: + success = ccnxCodecTlvUtilities_PutAsBuffer(decoder, packetDictionary, type, length, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_CERT); + break; + + case CCNxCodecSchemaV1Types_ValidationAlg_KeyId: + success = ccnxCodecTlvUtilities_PutAsBuffer(decoder, packetDictionary, type, length, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_KEYID); + break; + + case CCNxCodecSchemaV1Types_ValidationAlg_KeyName: + // The "value" is a link + success = _decodeKeyName(decoder, packetDictionary, type, length); + break; + + case CCNxCodecSchemaV1Types_ValidationAlg_SigTime: + // This is a time, so put it as an Integer + success = ccnxCodecTlvUtilities_PutAsInteger(decoder, packetDictionary, type, length, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_SIGNTIME); + break; + + case CCNxCodecSchemaV1Types_ValidationAlg_PublicKey: + success = ccnxCodecTlvUtilities_PutAsBuffer(decoder, packetDictionary, type, length, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_KEY); + break; + + default: { + // if we do not know the TLV type, put it in this container's unknown list + success = ccnxCodecTlvUtilities_PutAsListBuffer(decoder, packetDictionary, type, length, CCNxCodecSchemaV1TlvDictionary_Lists_VALIDATION_ALG_LIST); + } + break; + } + + if (!success) { + CCNxCodecError *error = ccnxCodecError_Create(TLV_ERR_DECODE, __func__, __LINE__, ccnxCodecTlvDecoder_Position(decoder)); + ccnxCodecTlvDecoder_SetError(decoder, error); + ccnxCodecError_Release(&error); + } + return success; +} + +/** + * Called by _decodeAlgType() via ccnxCodecTlvUtilities_DecodeSubcontainer() to decode the + * algorithm specific parameters + */ +static bool +_decodeAlgParameters(CCNxCodecTlvDecoder *decoder, CCNxTlvDictionary *packetDictionary) +{ + return ccnxCodecTlvUtilities_DecodeContainer(decoder, packetDictionary, _decodeAlgParametersType); +} + +static bool +_decodeAlgType(CCNxCodecTlvDecoder *decoder, CCNxTlvDictionary *packetDictionary, uint16_t type, uint16_t length) +{ + bool success = false; + + PARCCryptoSuite parcSuite; + bool match = ccnxCodecSchemaV1CryptoSuite_TlvToParc((CCNxCodecSchemaV1TlvDictionary_CryptoSuite) type, &parcSuite); + + if (match) { + success = ccnxTlvDictionary_PutInteger(packetDictionary, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_CRYPTO_SUITE, parcSuite); + + if (success) { + success = ccnxCodecTlvUtilities_DecodeSubcontainer(decoder, packetDictionary, type, length, _decodeAlgParameters); + } + } else { + // if we do not know the TLV type, put it in this container's unknown list + success = ccnxCodecTlvUtilities_PutAsListBuffer(decoder, packetDictionary, type, length, CCNxCodecSchemaV1TlvDictionary_Lists_VALIDATION_ALG_LIST); + } + + if (!success) { + CCNxCodecError *error = ccnxCodecError_Create(TLV_ERR_DECODE, __func__, __LINE__, ccnxCodecTlvDecoder_Position(decoder)); + ccnxCodecTlvDecoder_SetError(decoder, error); + ccnxCodecError_Release(&error); + } + return success; +} + +// ================== +// Public API + +bool +ccnxCodecSchemaV1ValidationDecoder_DecodeAlg(CCNxCodecTlvDecoder *decoder, CCNxTlvDictionary *packetDictionary) +{ + return ccnxCodecTlvUtilities_DecodeContainer(decoder, packetDictionary, _decodeAlgType); +} + + +bool +ccnxCodecSchemaV1ValidationDecoder_DecodePayload(CCNxCodecTlvDecoder *decoder, CCNxTlvDictionary *packetDictionary) +{ + bool success = false; + // A 0-length payload is treaded like an error + size_t remaining = ccnxCodecTlvDecoder_Remaining(decoder); + if (remaining > 0) { + PARCBuffer *payload = ccnxCodecTlvDecoder_GetValue(decoder, remaining); + success = ccnxTlvDictionary_PutBuffer(packetDictionary, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_PAYLOAD, payload); + parcBuffer_Release(&payload); + } + return success; +} diff --git a/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_ValidationDecoder.h b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_ValidationDecoder.h new file mode 100755 index 00000000..c5d5a42c --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_ValidationDecoder.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file ccnxCodecSchemaV1_ValidationDecoder.h + * @brief Decode the validation algorithm and payload + * + * <#Detailed Description#> + * + */ + +#ifndef __CCNx_Common__ccnxCodecSchemaV1_ValidationDecoder__ +#define __CCNx_Common__ccnxCodecSchemaV1_ValidationDecoder__ + +#include <stdbool.h> + +#include <ccnx/common/internal/ccnx_TlvDictionary.h> +#include <ccnx/common/codec/ccnxCodec_TlvDecoder.h> + +/** + * The decode the validation algorithm + * + * The decoder should point to byte 0 of the valdiation algorithm "value" + * + * The results are put in the provided dictionary. + * It is an error if the "value" does not extend to the end of + * the decoder. + * + * @param [in] decoder The decoder to parse + * @param [in] packetDictionary The results go directly in to the provided dictionary. + * + * @return true Fully parsed, no errors + * @return false Error decoding, decoder is left pointing to the first byte of the error + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool ccnxCodecSchemaV1ValidationDecoder_DecodeAlg(CCNxCodecTlvDecoder *decoder, CCNxTlvDictionary *packetDictionary); + +/** + * The decode the validation payload + * + * The decoder should point to byte 0 of the valdiation payload "value" + * The payload is an opaque block, so this function will just put the "value" in to the proper + * dictionary location. There's no real parsing. + * + * @param [in] decoder The decoder to parse + * @param [in] packetDictionary The results go directly in to the provided dictionary. + * + * @return true Fully parsed, no errors + * @return false Error decoding, decoder is left pointing to the first byte of the error + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool ccnxCodecSchemaV1ValidationDecoder_DecodePayload(CCNxCodecTlvDecoder *decoder, CCNxTlvDictionary *packetDictionary); + +#endif /* defined(__CCNx_Common__ccnxCodecSchemaV1_ValidationDecoder__) */ diff --git a/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_ValidationEncoder.c b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_ValidationEncoder.c new file mode 100755 index 00000000..f7ad00aa --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_ValidationEncoder.c @@ -0,0 +1,253 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +#include <config.h> +#include <sys/time.h> +#include <ccnx/common/internal/ccnx_TlvDictionary.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_TlvDictionary.h> + +#include <ccnx/common/codec/ccnxCodec_TlvUtilities.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_Types.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_ValidationEncoder.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_LinkCodec.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_CryptoSuite.h> + +static ssize_t +_encodeKeyId(CCNxCodecTlvEncoder *encoder, CCNxTlvDictionary *packetDictionary) +{ + ssize_t length = 0; + PARCBuffer *keyid = ccnxTlvDictionary_GetBuffer(packetDictionary, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_KEYID); + if (keyid) { + length = ccnxCodecTlvEncoder_AppendBuffer(encoder, CCNxCodecSchemaV1Types_ValidationAlg_KeyId, keyid); + } + return length; +} + +static ssize_t +_encodePublicKey(CCNxCodecTlvEncoder *encoder, CCNxTlvDictionary *packetDictionary) +{ + ssize_t length = 0; + PARCBuffer *key = ccnxTlvDictionary_GetBuffer(packetDictionary, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_KEY); + if (key) { + length = ccnxCodecTlvEncoder_AppendBuffer(encoder, CCNxCodecSchemaV1Types_ValidationAlg_PublicKey, key); + } + return length; +} + +static ssize_t +_encodeCertificate(CCNxCodecTlvEncoder *encoder, CCNxTlvDictionary *packetDictionary) +{ + ssize_t length = 0; + PARCBuffer *cert = ccnxTlvDictionary_GetBuffer(packetDictionary, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_CERT); + if (cert) { + length = ccnxCodecTlvEncoder_AppendBuffer(encoder, CCNxCodecSchemaV1Types_ValidationAlg_Cert, cert); + } + return length; +} + +/** + * If there is a CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_KEYNAME_NAME entry in the dictionary, look up the + * optional keyid and hash restrictions, create a CCNxLink, then encode the Link. + */ +static ssize_t +_encodeKeyName(CCNxCodecTlvEncoder *encoder, CCNxTlvDictionary *packetDictionary) +{ + ssize_t length = 0; + CCNxName *keyname = ccnxTlvDictionary_GetName(packetDictionary, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_KEYNAME_NAME); + if (keyname) { + PARCBuffer *keyid = ccnxTlvDictionary_GetBuffer(packetDictionary, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_KEYNAME_KEYID); + PARCBuffer *hash = ccnxTlvDictionary_GetBuffer(packetDictionary, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_KEYNAME_OBJHASH); + CCNxLink *link = ccnxLink_Create(keyname, keyid, hash); + + size_t startPosition = ccnxCodecTlvEncoder_Position(encoder); + ccnxCodecTlvEncoder_AppendContainer(encoder, CCNxCodecSchemaV1Types_ValidationAlg_KeyName, 0); + ssize_t innerLength = ccnxCodecSchemaV1LinkCodec_Encode(encoder, link); + + if (innerLength == 0) { + // backup and erase the container + ccnxCodecTlvEncoder_SetPosition(encoder, startPosition); + } else if (innerLength > 0) { + ccnxCodecTlvEncoder_SetContainerLength(encoder, startPosition, innerLength); + ssize_t endPosition = ccnxCodecTlvEncoder_Position(encoder); + length = endPosition - startPosition; + } else { + // an error signal + length = innerLength; + } + + ccnxLink_Release(&link); + } + return length; +} + +/** + * If a time is not provided, use the current time + */ +static ssize_t +_encodeSignatureTime(CCNxCodecTlvEncoder *encoder, CCNxTlvDictionary *packetDictionary) +{ + ssize_t length = 0; + + bool haveSignTime = false; + uint64_t signTime = 0; + if (ccnxTlvDictionary_IsValueInteger(packetDictionary, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_SIGNTIME)) { + haveSignTime = true; + signTime = ccnxTlvDictionary_GetInteger(packetDictionary, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_SIGNTIME); + } else { + // if we have a signer and with a signature algorithm, create the signing time + PARCSigner *signer = ccnxCodecTlvEncoder_GetSigner(encoder); + if (signer) { + PARCSigningAlgorithm alg = parcSigner_GetSigningAlgorithm(signer); + if (alg != PARCSigningAlgortihm_NULL && alg != PARCSigningAlgorithm_UNKNOWN) { + // We will generate a signature, so generate a signing time + + struct timeval tv; + gettimeofday(&tv, NULL); + + // convert to milli-seconds + signTime = tv.tv_sec * 1000 + tv.tv_usec / 1000; + haveSignTime = true; + } + } + } + + if (haveSignTime) { + length = ccnxCodecTlvEncoder_AppendUint64(encoder, CCNxCodecSchemaV1Types_ValidationAlg_SigTime, signTime); + } + + return length; +} + +static ssize_t +_encodeAlgParameters(CCNxCodecTlvEncoder *encoder, CCNxTlvDictionary *packetDictionary) +{ + ssize_t length = 0; + ssize_t result; + + result = _encodeKeyId(encoder, packetDictionary); + if (result < 0) { + return result; + } + length += result; + + result = _encodePublicKey(encoder, packetDictionary); + if (result < 0) { + return result; + } + length += result; + + result = _encodeCertificate(encoder, packetDictionary); + if (result < 0) { + return result; + } + length += result; + + result = _encodeKeyName(encoder, packetDictionary); + if (result < 0) { + return result; + } + length += result; + + result = _encodeSignatureTime(encoder, packetDictionary); + if (result < 0) { + return result; + } + length += result; + + return length; +} + +ssize_t +ccnxCodecSchemaV1ValidationEncoder_EncodeAlg(CCNxCodecTlvEncoder *encoder, CCNxTlvDictionary *packetDictionary) +{ + ssize_t length = 0; + bool haveCryptoSuite = false; + CCNxCodecSchemaV1TlvDictionary_CryptoSuite suite; + + if (ccnxTlvDictionary_IsValueInteger(packetDictionary, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_CRYPTO_SUITE)) { + // try from dictionary + + PARCCryptoSuite parcSuite = (PARCCryptoSuite) ccnxTlvDictionary_GetInteger(packetDictionary, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_CRYPTO_SUITE); + + haveCryptoSuite = ccnxCodecSchemaV1CryptoSuite_ParcToTlv(parcSuite, &suite); + } else if (ccnxTlvDictionary_IsContentObject(packetDictionary)) { + // deduce from the signer + + PARCSigner *signer = ccnxCodecTlvEncoder_GetSigner(encoder); + if (signer != NULL) { + PARCCryptoHashType hashType = parcSigner_GetCryptoHashType(signer); + PARCSigningAlgorithm signAlg = parcSigner_GetSigningAlgorithm(signer); + + if (ccnxCodecSchemaV1CryptoSuite_SignAndHashToTlv(signAlg, hashType, &suite)) { + haveCryptoSuite = true; + } + } + } + + if (haveCryptoSuite) { + // write the TL container then encode any enclosed TLVs + ssize_t startPosition = ccnxCodecTlvEncoder_Position(encoder); + + ccnxCodecTlvEncoder_AppendContainer(encoder, suite, 0); + ssize_t innerLength = _encodeAlgParameters(encoder, packetDictionary); + + // 0 inner length is acceptable + if (innerLength >= 0) { + ccnxCodecTlvEncoder_SetContainerLength(encoder, startPosition, innerLength); + ssize_t endPosition = ccnxCodecTlvEncoder_Position(encoder); + length = endPosition - startPosition; + } else { + // an error signal + length = innerLength; + } + } + return length; +} + +ssize_t +ccnxCodecSchemaV1ValidationEncoder_EncodePayload(CCNxCodecTlvEncoder *encoder, CCNxTlvDictionary *packetDictionary) +{ + ssize_t length = 0; + + if (!ccnxTlvDictionary_IsValueBuffer(packetDictionary, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_PAYLOAD)) { + // try to compute a signature + + // If signer is NULL, then no signature is genearted + PARCSigner *signer = ccnxCodecTlvEncoder_GetSigner(encoder); + if (signer != NULL) { + // user did not give us one, so fill it in + PARCSignature *signature = ccnxCodecTlvEncoder_ComputeSignature(encoder); + PARCBuffer *sigbits = parcSignature_GetSignature(signature); + + // this creates its own reference to sigbits + ccnxTlvDictionary_PutBuffer(packetDictionary, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_PAYLOAD, sigbits); + + // this will release our hold on sigbitsElastic and sigbits. + parcSignature_Release(&signature); + } + } + + PARCBuffer *sigbits = ccnxTlvDictionary_GetBuffer(packetDictionary, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_PAYLOAD); + if (sigbits) { + size_t remaining = parcBuffer_Remaining(sigbits); + uint8_t *overlay = parcBuffer_Overlay(sigbits, 0); + length = ccnxCodecTlvEncoder_AppendRawArray(encoder, remaining, overlay); + } + + return length; +} diff --git a/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_ValidationEncoder.h b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_ValidationEncoder.h new file mode 100755 index 00000000..3897a053 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_ValidationEncoder.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file ccnxCodecSchemaV1_ValidationEncoder.h + * @brief Encode the Validation Algorithm and Payload + * + * Encodes the validation algorithm and payload from the dictionary. Optionally computes + * a signature if one is not specified in the dictionary and the encoder has a signer. + * + */ + +#ifndef __CCNx_Common__ccnxCodecSchemaV1_ValidationEncoder__ +#define __CCNx_Common__ccnxCodecSchemaV1_ValidationEncoder__ + +#include <ccnx/common/internal/ccnx_TlvDictionary.h> +#include <ccnx/common/codec/ccnxCodec_TlvEncoder.h> + +/** + * Appends the Validation Algorithm to the packet encoder + * + * If the dictionary has a CryptoSuite specified, we will create a ValidationAlgorithm section + * and fill it in as per the CryptoSuite and supplied validation algorithm arguments, such as + * a KeyId, KeyName, Cert, etc. For most signatures, only the KeyId is mandatory, the other + * fields will only be specified if the user put something in the dictionary. + * + * the caller is responsible for writing the ValidationAlgorithm TL container. + * + * @param [in] encoder An allocated encoder to append to + * @param [in] packetDictionary The dictionary containing the optional headers + * + * @return non-negative Total bytes appended to encoder + * @return -1 An error + * + * Example: + * @code + * <#example#> + * @endcode + */ +ssize_t ccnxCodecSchemaV1ValidationEncoder_EncodeAlg(CCNxCodecTlvEncoder *encoder, CCNxTlvDictionary *packetDictionary); + +/** + * Appends the Validation Payload to the packet encoder + * + * This will append the Valdiation Payload from the dictionary, or if it is missing and the + * encoder has a signer, will create a signature. + * + * the caller is responsible for writing the ValidationPayload TL container. + * + * To create the signature, the caller must have used ccnxCodecTlvEncoder_MarkSignatureStart() and + * ccnxCodecTlvEncoder_MarkSignatureEnd() functions to specify the byte locations of the start and + * stop of the protected region. + * + * @param [in] encoder An allocated encoder to append to + * @param [in] packetDictionary The dictionary containing the optional headers + * + * @return non-negative Total bytes appended to encoder + * @return -1 An error + * + * Example: + * @code + * <#example#> + * @endcode + */ +ssize_t ccnxCodecSchemaV1ValidationEncoder_EncodePayload(CCNxCodecTlvEncoder *encoder, CCNxTlvDictionary *packetDictionary); + +#endif /* defined(__CCNx_Common__ccnxCodecSchemaV1_ValidationEncoder__) */ diff --git a/libccnx-common/ccnx/common/codec/schema_v1/test/.gitignore b/libccnx-common/ccnx/common/codec/schema_v1/test/.gitignore new file mode 100644 index 00000000..ffeaadf8 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/test/.gitignore @@ -0,0 +1,19 @@ +*.gcno +*.log +*.trs +*.o +test_ccnxCodecSchemaV1_CryptoSuite +test_ccnxCodecSchemaV1_FixedHeaderDecoder +test_ccnxCodecSchemaV1_FixedHeaderEncoder +test_ccnxCodecSchemaV1_LinkCodec +test_ccnxCodecSchemaV1_MessageDecoder +test_ccnxCodecSchemaV1_MessageEncoder +test_ccnxCodecSchemaV1_NameCodec +test_ccnxCodecSchemaV1_NameSegmentCodec +test_ccnxCodecSchemaV1_OptionalHeadersDecoder +test_ccnxCodecSchemaV1_OptionalHeadersEncoder +test_ccnxCodecSchemaV1_PacketDecoder +test_ccnxCodecSchemaV1_PacketEncoder +test_ccnxCodecSchemaV1_TlvDictionary +test_ccnxCodecSchemaV1_ValidationDecoder +test_ccnxCodecSchemaV1_ValidationEncoder diff --git a/libccnx-common/ccnx/common/codec/schema_v1/test/CMakeLists.txt b/libccnx-common/ccnx/common/codec/schema_v1/test/CMakeLists.txt new file mode 100644 index 00000000..c827a36e --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/test/CMakeLists.txt @@ -0,0 +1,29 @@ +# Enable gcov output for the tests +add_definitions(--coverage) +set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} " --coverage") + +set(TestsExpectedToPass + test_ccnxCodecSchemaV1_CryptoSuite + test_ccnxCodecSchemaV1_FixedHeaderDecoder + test_ccnxCodecSchemaV1_FixedHeaderEncoder + test_ccnxCodecSchemaV1_HashCodec + test_ccnxCodecSchemaV1_LinkCodec + test_ccnxCodecSchemaV1_ManifestDecoder + test_ccnxCodecSchemaV1_ManifestEncoder + test_ccnxCodecSchemaV1_MessageDecoder + test_ccnxCodecSchemaV1_MessageEncoder + test_ccnxCodecSchemaV1_NameCodec + test_ccnxCodecSchemaV1_NameSegmentCodec + test_ccnxCodecSchemaV1_OptionalHeadersDecoder + test_ccnxCodecSchemaV1_OptionalHeadersEncoder + test_ccnxCodecSchemaV1_PacketDecoder + test_ccnxCodecSchemaV1_PacketEncoder + test_ccnxCodecSchemaV1_TlvDictionary + test_ccnxCodecSchemaV1_ValidationDecoder + test_ccnxCodecSchemaV1_ValidationEncoder +) + + +foreach(test ${TestsExpectedToPass}) + AddTest(${test}) +endforeach() diff --git a/libccnx-common/ccnx/common/codec/schema_v1/test/test_ccnxCodecSchemaV1_CryptoSuite.c b/libccnx-common/ccnx/common/codec/schema_v1/test/test_ccnxCodecSchemaV1_CryptoSuite.c new file mode 100644 index 00000000..10bc24d2 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/test/test_ccnxCodecSchemaV1_CryptoSuite.c @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../ccnxCodecSchemaV1_CryptoSuite.c" +#include <parc/algol/parc_SafeMemory.h> +#include <LongBow/unit-test.h> + +#include <stdio.h> + +LONGBOW_TEST_RUNNER(ccnxCodecSchemaV1_CryptoSuite) +{ + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(ccnxCodecSchemaV1_CryptoSuite) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(ccnxCodecSchemaV1_CryptoSuite) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecSchemaV1CryptoSuite_ParcToTlv); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecSchemaV1CryptoSuite_TlvToParc); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecSchemaV1CryptoSuite_SignAndHashToTlv); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + if (parcSafeMemory_ReportAllocation(STDOUT_FILENO) != 0) { + printf("('%s' leaks memory by %d (allocs - frees)) ", longBowTestCase_GetName(testCase), parcMemory_Outstanding()); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, ccnxCodecSchemaV1CryptoSuite_ParcToTlv) +{ + struct test_vector { + PARCCryptoSuite input; + CCNxCodecSchemaV1TlvDictionary_CryptoSuite output; + bool success; + bool sentinel; + } vectors[] = { + { .input = PARCCryptoSuite_RSA_SHA256, .output = CCNxCodecSchemaV1TlvDictionary_CryptoSuite_RsaSha256, .success = true, .sentinel = false }, + { .input = PARCCryptoSuite_HMAC_SHA256, .output = CCNxCodecSchemaV1TlvDictionary_CryptoSuite_HmacSha256, .success = true, .sentinel = false }, + { .input = PARCCryptoSuite_NULL_CRC32C, .output = CCNxCodecSchemaV1TlvDictionary_CryptoSuite_CRC32C, .success = true, .sentinel = false }, + { .input = PARCCryptoSuite_DSA_SHA256, .output = 0, .success = false, .sentinel = false }, + { .input = PARCCryptoSuite_RSA_SHA512, .output = 0, .success = false, .sentinel = false }, + { .input = PARCCryptoSuite_HMAC_SHA512, .output = 0, .success = false, .sentinel = false }, + { .input = 13579, .output = 0, .success = false, .sentinel = false }, + { .input = 0, .output = 0, .success = false, .sentinel = true }, + }; + + for (int i = 0; !vectors[i].sentinel; i++) { + unsigned output; + bool success = ccnxCodecSchemaV1CryptoSuite_ParcToTlv(vectors[i].input, &output); + assertTrue(success == vectors[i].success, "Wrong return value, index %d expected %d got %d", i, vectors[i].success, success); + if (success) { + assertTrue(output == vectors[i].output, "Wrong output, index %d expected %u got %u", i, vectors[i].output, output); + } + } +} + +LONGBOW_TEST_CASE(Global, ccnxCodecSchemaV1CryptoSuite_TlvToParc) +{ + struct test_vector { + PARCCryptoSuite output; + CCNxCodecSchemaV1TlvDictionary_CryptoSuite input; + bool success; + bool sentinel; + } vectors[] = { + { .input = CCNxCodecSchemaV1TlvDictionary_CryptoSuite_RsaSha256, .output = PARCCryptoSuite_RSA_SHA256, .success = true, .sentinel = false }, + { .input = CCNxCodecSchemaV1TlvDictionary_CryptoSuite_HmacSha256, .output = PARCCryptoSuite_HMAC_SHA256, .success = true, .sentinel = false }, + { .input = CCNxCodecSchemaV1TlvDictionary_CryptoSuite_CRC32C, .output = PARCCryptoSuite_NULL_CRC32C, .success = true, .sentinel = false }, + { .input = CCNxCodecSchemaV1TlvDictionary_CryptoSuite_EcSecp256K1, .output = 0, .success = false, .sentinel = false }, + { .input = 13579, .output = 0, .success = false, .sentinel = false }, + { .input = 0, .output = 0, .success = false, .sentinel = true }, + }; + + for (int i = 0; !vectors[i].sentinel; i++) { + unsigned output; + bool success = ccnxCodecSchemaV1CryptoSuite_TlvToParc(vectors[i].input, &output); + assertTrue(success == vectors[i].success, "Wrong return value, index %d expected %d got %d", i, vectors[i].success, success); + if (success) { + assertTrue(output == vectors[i].output, "Wrong output, index %d expected %u got %u", i, vectors[i].output, output); + } + } +} + +LONGBOW_TEST_CASE(Global, ccnxCodecSchemaV1CryptoSuite_SignAndHashToTlv) +{ + struct test_vector { + PARCSigningAlgorithm signAlg; + PARCCryptoHashType hashType; + CCNxCodecSchemaV1TlvDictionary_CryptoSuite output; + bool success; + bool sentinel; + } vectors[] = { + { .signAlg = PARCSigningAlgorithm_RSA, .hashType = PARCCryptoHashType_SHA256, .output = CCNxCodecSchemaV1TlvDictionary_CryptoSuite_RsaSha256, .success = true, .sentinel = false }, + { .signAlg = PARCSigningAlgorithm_HMAC, .hashType = PARCCryptoHashType_SHA256, .output = CCNxCodecSchemaV1TlvDictionary_CryptoSuite_HmacSha256, .success = true, .sentinel = false }, + { .signAlg = PARCSigningAlgortihm_NULL, .hashType = PARCCryptoHashType_CRC32C, .output = CCNxCodecSchemaV1TlvDictionary_CryptoSuite_CRC32C, .success = true, .sentinel = false }, + { .signAlg = PARCSigningAlgorithm_RSA, .hashType = 12345, .output = 0, .success = false, .sentinel = false }, + { .signAlg = PARCSigningAlgorithm_HMAC, .hashType = 12345, .output = 0, .success = false, .sentinel = false }, + { .signAlg = PARCSigningAlgortihm_NULL, .hashType = 12345, .output = 0, .success = false, .sentinel = false }, + { .signAlg = 12345, .hashType = 12345, .output = 0, .success = false, .sentinel = false }, + { .signAlg = 0, .hashType = 0, .output = 0, .success = false, .sentinel = true }, + }; + + for (int i = 0; !vectors[i].sentinel; i++) { + unsigned output; + bool success = ccnxCodecSchemaV1CryptoSuite_SignAndHashToTlv(vectors[i].signAlg, vectors[i].hashType, &output); + assertTrue(success == vectors[i].success, "Wrong return value, index %d expected %d got %d", i, vectors[i].success, success); + if (success) { + assertTrue(output == vectors[i].output, "Wrong output, index %d expected %u got %u", i, vectors[i].output, output); + } + } +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(ccnxCodecSchemaV1_CryptoSuite); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-common/ccnx/common/codec/schema_v1/test/test_ccnxCodecSchemaV1_FixedHeaderDecoder.c b/libccnx-common/ccnx/common/codec/schema_v1/test/test_ccnxCodecSchemaV1_FixedHeaderDecoder.c new file mode 100644 index 00000000..7a889b58 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/test/test_ccnxCodecSchemaV1_FixedHeaderDecoder.c @@ -0,0 +1,350 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../ccnxCodecSchemaV1_FixedHeaderDecoder.c" +#include <parc/algol/parc_SafeMemory.h> + +#include <LongBow/unit-test.h> + +typedef struct test_data { + uint8_t *packet; + PARCBuffer *fixedHeader; + CCNxCodecTlvDecoder *decoder; + CCNxTlvDictionary *dictionary; + + // truth table + uint8_t version; + uint8_t packetType; + uint16_t packetLength; + uint8_t hopLimit; + uint8_t returnCode; + uint8_t flags; + uint8_t headerLength; +} TestData; + +static TestData * +_commonSetup(void) +{ + TestData *data = parcMemory_AllocateAndClear(sizeof(TestData)); + assertNotNull(data, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(TestData)); + + data->packet = parcMemory_Allocate(8); + assertNotNull(data->packet, "parcMemory_Allocate(%d) returned NULL", 8); + memcpy(data->packet, &((uint8_t[]) { 0x00, 0x01, 0x01, 0x02, 0x03, 0x04, 0x05, 0x08 }), 8); + + data->fixedHeader = parcBuffer_Wrap(data->packet, 8, 0, 8); + data->version = 0; + data->packetType = 1; + data->packetLength = 0x0102; + data->hopLimit = 3; + data->returnCode = 4; + data->flags = 5; + data->headerLength = 8; + data->decoder = ccnxCodecTlvDecoder_Create(data->fixedHeader); + data->dictionary = ccnxTlvDictionary_Create(CCNxCodecSchemaV1TlvDictionary_MessageFastArray_END, + CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_END); + + return data; +} + +static void +_commonTeardown(TestData *data) +{ +// TestData *data = longBowTestCase_GetClipBoardData(testCase); + + ccnxTlvDictionary_Release(&data->dictionary); + ccnxCodecTlvDecoder_Destroy(&data->decoder); + parcBuffer_Release(&data->fixedHeader); + parcMemory_Deallocate((void **) &(data->packet)); + parcMemory_Deallocate((void **) &data); +} + +LONGBOW_TEST_RUNNER(ccnxCodecSchemaV1_FixedHeaderDecoder) +{ + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +LONGBOW_TEST_RUNNER_SETUP(ccnxCodecSchemaV1_FixedHeaderDecoder) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_RUNNER_TEARDOWN(ccnxCodecSchemaV1_FixedHeaderDecoder) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecSchemaV1FixedHeaderDecoder_Decode_Underrun); + + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecSchemaV1FixedHeaderDecoder_GetHeaderLength); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecSchemaV1FixedHeaderDecoder_GetPacketType); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecSchemaV1FixedHeaderDecoder_GetPacketLength); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecSchemaV1FixedHeaderDecoder_GetVersion); + + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecSchemaV1FixedHeaderDecoder_GetHopLimit); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecSchemaV1FixedHeaderDecoder_GetReturnCode); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecSchemaV1FixedHeaderDecoder_GetFlags); + + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecSchemaV1FixedHeaderDecoder_GetHeaderLength_Missing); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecSchemaV1FixedHeaderDecoder_GetPacketType_Missing); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecSchemaV1FixedHeaderDecoder_GetPacketLength_Missing); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecSchemaV1FixedHeaderDecoder_GetVersion_Missing); + + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecSchemaV1FixedHeaderDecoder_PacketLengthTooShort); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecSchemaV1FixedHeaderDecoder_HeaderLengthTooShort); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecSchemaV1FixedHeaderDecoder_PacketLengthLessHeaderLength); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + longBowTestCase_SetClipBoardData(testCase, _commonSetup()); + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + _commonTeardown(longBowTestCase_GetClipBoardData(testCase)); + + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +/** + * Successful decode is tested in all the _GetX functions. We only test + * when the buffer is too small here + */ +LONGBOW_TEST_CASE(Global, ccnxCodecSchemaV1FixedHeaderDecoder_Decode_Underrun) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + // advance the buffer so its too small + ccnxCodecTlvDecoder_Advance(data->decoder, 1); + + size_t beforePosition = ccnxCodecTlvDecoder_Position(data->decoder); + bool success = ccnxCodecSchemaV1FixedHeaderDecoder_Decode(data->decoder, data->dictionary); + size_t afterPosition = ccnxCodecTlvDecoder_Position(data->decoder); + + assertFalse(success, "Should have failed with too small a buffer"); + assertTrue(beforePosition == afterPosition, "Wrong postion, got %zu expected %zu", afterPosition, beforePosition); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecSchemaV1FixedHeaderDecoder_GetHeaderLength) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + ccnxCodecSchemaV1FixedHeaderDecoder_Decode(data->decoder, data->dictionary); + int headerLength = ccnxCodecSchemaV1FixedHeaderDecoder_GetHeaderLength(data->dictionary); + assertTrue(headerLength == data->headerLength, "Wrong headerLength, got %d expected %d", headerLength, + data->headerLength); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecSchemaV1FixedHeaderDecoder_GetPacketType) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + ccnxCodecSchemaV1FixedHeaderDecoder_Decode(data->decoder, data->dictionary); + int packetType = ccnxCodecSchemaV1FixedHeaderDecoder_GetPacketType(data->dictionary); + assertTrue(packetType == data->packetType, "Wrong packetType, got %d expected %d", packetType, data->packetType); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecSchemaV1FixedHeaderDecoder_GetPacketLength) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + ccnxCodecSchemaV1FixedHeaderDecoder_Decode(data->decoder, data->dictionary); + int packetLength = ccnxCodecSchemaV1FixedHeaderDecoder_GetPacketLength(data->dictionary); + assertTrue(packetLength == data->packetLength, "Wrong payloadLength, got %d expected %d", packetLength, + data->packetLength); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecSchemaV1FixedHeaderDecoder_GetVersion) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + ccnxCodecSchemaV1FixedHeaderDecoder_Decode(data->decoder, data->dictionary); + int version = ccnxCodecSchemaV1FixedHeaderDecoder_GetVersion(data->dictionary); + assertTrue(version == data->version, "Wrong version, got %d expected %d", version, data->version); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecSchemaV1FixedHeaderDecoder_GetHopLimit) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + ccnxCodecSchemaV1FixedHeaderDecoder_Decode(data->decoder, data->dictionary); + int hopLimit = ccnxCodecSchemaV1FixedHeaderDecoder_GetHopLimit(data->dictionary); + assertTrue(hopLimit == data->hopLimit, "Wrong hopLimit, got %d expected %d", hopLimit, data->hopLimit); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecSchemaV1FixedHeaderDecoder_GetReturnCode) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + ccnxCodecSchemaV1FixedHeaderDecoder_Decode(data->decoder, data->dictionary); + int returnCode = ccnxCodecSchemaV1FixedHeaderDecoder_GetReturnCode(data->dictionary); + assertTrue(returnCode == data->returnCode, "Wrong returnCode, got %d expected %d", returnCode, data->returnCode); + + // Check that the InterestReturnCode was set in the fast array, too. + uint8_t test = + ccnxTlvDictionary_GetInteger(data->dictionary, + CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_InterestReturnCode); + assertTrue(test == data->returnCode, "Expected the dictionary to have the return code set"); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecSchemaV1FixedHeaderDecoder_GetFlags) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + ccnxCodecSchemaV1FixedHeaderDecoder_Decode(data->decoder, data->dictionary); + int flags = ccnxCodecSchemaV1FixedHeaderDecoder_GetFlags(data->dictionary); + assertTrue(flags == data->flags, "Wrong flags, got %d expected %d", flags, data->flags); +} + + +// ============================== +// Tests for missing values + +LONGBOW_TEST_CASE(Global, ccnxCodecSchemaV1FixedHeaderDecoder_GetHeaderLength_Missing) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + // don't decode the buffer, so we're operating with an empty dictionary + int version = ccnxCodecSchemaV1FixedHeaderDecoder_GetHeaderLength(data->dictionary); + assertTrue(version == -1, "Wrong HeaderLength, got %d expected %d", version, -1); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecSchemaV1FixedHeaderDecoder_GetPacketType_Missing) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + // don't decode the buffer, so we're operating with an empty dictionary + int version = ccnxCodecSchemaV1FixedHeaderDecoder_GetPacketType(data->dictionary); + assertTrue(version == -1, "Wrong PacketType, got %d expected %d", version, -1); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecSchemaV1FixedHeaderDecoder_GetPacketLength_Missing) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + // don't decode the buffer, so we're operating with an empty dictionary + int version = ccnxCodecSchemaV1FixedHeaderDecoder_GetPacketLength(data->dictionary); + assertTrue(version == -1, "Wrong payloadLength, got %d expected %d", version, -1); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecSchemaV1FixedHeaderDecoder_GetVersion_Missing) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + // don't decode the buffer, so we're operating with an empty dictionary + int version = ccnxCodecSchemaV1FixedHeaderDecoder_GetVersion(data->dictionary); + assertTrue(version == -1, "Wrong version, got %d expected %d", version, -1); +} + + +/** + * Packet length must be at least 8 bytes + */ +LONGBOW_TEST_CASE(Global, ccnxCodecSchemaV1FixedHeaderDecoder_PacketLengthTooShort) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + CCNxCodecSchemaV1InterestHeader header = { + .version = 1, + .packetType = CCNxCodecSchemaV1Types_PacketType_Interest, + .packetLength = htons(3), + .hopLimit = 4, + .returnCode = 7, + .flags = 8, + .headerLength = 9, + }; + + PARCBuffer *fixedHeader = parcBuffer_Wrap(&header, 8, 0, 8); + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(fixedHeader); + + bool success = ccnxCodecSchemaV1FixedHeaderDecoder_Decode(decoder, data->dictionary); + assertFalse(success, "Did not fail on packet length too short"); + + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&fixedHeader); +} + +/** + * Header length must be at least 8 bytes + */ +LONGBOW_TEST_CASE(Global, ccnxCodecSchemaV1FixedHeaderDecoder_HeaderLengthTooShort) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + CCNxCodecSchemaV1InterestHeader header = { + .version = 1, + .packetType = CCNxCodecSchemaV1Types_PacketType_Interest, + .packetLength = htons(12), + .hopLimit = 4, + .returnCode = 7, + .flags = 8, + .headerLength = 6, + }; + + PARCBuffer *fixedHeader = parcBuffer_Wrap(&header, 8, 0, 8); + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(fixedHeader); + + bool success = ccnxCodecSchemaV1FixedHeaderDecoder_Decode(decoder, data->dictionary); + assertFalse(success, "Did not fail on header length too short"); + + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&fixedHeader); +} + +/** + * Packet length must be no less than Header Length + */ +LONGBOW_TEST_CASE(Global, ccnxCodecSchemaV1FixedHeaderDecoder_PacketLengthLessHeaderLength) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + CCNxCodecSchemaV1InterestHeader header = { + .version = 1, + .packetType = CCNxCodecSchemaV1Types_PacketType_Interest, + .packetLength = htons(12), + .hopLimit = 4, + .returnCode = 7, + .flags = 8, + .headerLength = 18, + }; + + PARCBuffer *fixedHeader = parcBuffer_Wrap(&header, 8, 0, 8); + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(fixedHeader); + + bool success = ccnxCodecSchemaV1FixedHeaderDecoder_Decode(decoder, data->dictionary); + assertFalse(success, "Did not fail on packet length less than header length"); + + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&fixedHeader); +} + +// ====================== + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(ccnxCodecSchemaV1_FixedHeaderDecoder); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-common/ccnx/common/codec/schema_v1/test/test_ccnxCodecSchemaV1_FixedHeaderEncoder.c b/libccnx-common/ccnx/common/codec/schema_v1/test/test_ccnxCodecSchemaV1_FixedHeaderEncoder.c new file mode 100755 index 00000000..4f479090 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/test/test_ccnxCodecSchemaV1_FixedHeaderEncoder.c @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../ccnxCodecSchemaV1_FixedHeaderEncoder.c" +#include <parc/algol/parc_SafeMemory.h> +#include <LongBow/unit-test.h> + +#include <ccnx/common/codec/test/testrig_Compare.c> + +LONGBOW_TEST_RUNNER(ccnxCodecSchemaV0_FixedHeaderEncoder) +{ + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(ccnxCodecSchemaV0_FixedHeaderEncoder) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(ccnxCodecSchemaV0_FixedHeaderEncoder) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecSchemaV1FixedHeaderEncoder_EncodeInterest); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecSchemaV1FixedHeaderEncoder_EncodeContentObject); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecSchemaV1FixedHeaderEncoder_EncodeInterestReturn); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, ccnxCodecSchemaV1FixedHeaderEncoder_EncodeInterest) +{ + uint16_t packetLength = 0x0102; + + CCNxCodecSchemaV1InterestHeader header = { + .version = 1, + .packetType = CCNxCodecSchemaV1Types_PacketType_Interest, + .packetLength = packetLength, + .hopLimit = 4, + .returnCode = 7, // will be set to 0 + .flags = 8, + .headerLength = 9, + }; + + CCNxCodecSchemaV1InterestHeader truth = { + .version = 1, + .packetType = CCNxCodecSchemaV1Types_PacketType_Interest, + .packetLength = htons(packetLength), + .hopLimit = 4, + .returnCode = 0, + .flags = 8, + .headerLength = 9, + }; + + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + + ssize_t length = ccnxCodecSchemaV1FixedHeaderEncoder_EncodeHeader(encoder, (CCNxCodecSchemaV1FixedHeader *) &header); + assertTrue(length == 8, "Wrong length, got %zd expected %d", length, 8); + + testCompareEncoderToLinearMemory(encoder, length, (uint8_t *) &truth); + + ccnxCodecTlvEncoder_Destroy(&encoder); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecSchemaV1FixedHeaderEncoder_EncodeContentObject) +{ + uint16_t packetLength = 0x0102; + + CCNxCodecSchemaV1InterestHeader header = { + .version = 1, + .packetType = CCNxCodecSchemaV1Types_PacketType_ContentObject, + .packetLength = packetLength, + .hopLimit = 4, // will be set to 0 + .returnCode = 7, // will be set to 0 + .flags = 8, // will be set to 0 + .headerLength = 9, + }; + + CCNxCodecSchemaV1InterestHeader truth = { + .version = 1, + .packetType = CCNxCodecSchemaV1Types_PacketType_ContentObject, + .packetLength = htons(packetLength), + .hopLimit = 0, + .returnCode = 0, + .flags = 0, + .headerLength = 9, + }; + + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + + ssize_t length = ccnxCodecSchemaV1FixedHeaderEncoder_EncodeHeader(encoder, (CCNxCodecSchemaV1FixedHeader *) &header); + assertTrue(length == 8, "Wrong length, got %zd expected %d", length, 8); + + testCompareEncoderToLinearMemory(encoder, length, (uint8_t *) &truth); + + ccnxCodecTlvEncoder_Destroy(&encoder); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecSchemaV1FixedHeaderEncoder_EncodeInterestReturn) +{ + uint16_t packetLength = 0x0102; + + CCNxCodecSchemaV1InterestHeader header = { + .version = 1, + .packetType = CCNxCodecSchemaV1Types_PacketType_InterestReturn, + .packetLength = packetLength, + .hopLimit = 4, + .returnCode = 7, + .flags = 8, + .headerLength = 9, + }; + + CCNxCodecSchemaV1InterestHeader truth = { + .version = 1, + .packetType = CCNxCodecSchemaV1Types_PacketType_InterestReturn, + .packetLength = htons(packetLength), + .hopLimit = 4, + .returnCode = 7, + .flags = 8, + .headerLength = 9, + }; + + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + + ssize_t length = ccnxCodecSchemaV1FixedHeaderEncoder_EncodeHeader(encoder, (CCNxCodecSchemaV1FixedHeader *) &header); + assertTrue(length == 8, "Wrong length, got %zd expected %d", length, 8); + + testCompareEncoderToLinearMemory(encoder, length, (uint8_t *) &truth); + + ccnxCodecTlvEncoder_Destroy(&encoder); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(ccnxCodecSchemaV0_FixedHeaderEncoder); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-common/ccnx/common/codec/schema_v1/test/test_ccnxCodecSchemaV1_HashCodec.c b/libccnx-common/ccnx/common/codec/schema_v1/test/test_ccnxCodecSchemaV1_HashCodec.c new file mode 100644 index 00000000..9d03ca2e --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/test/test_ccnxCodecSchemaV1_HashCodec.c @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../ccnxCodecSchemaV1_HashCodec.c" + +#include <stdio.h> + +#include <parc/algol/parc_SafeMemory.h> +#include <LongBow/unit-test.h> + +#include <ccnx/common/codec/ccnxCodec_Error.h> + +LONGBOW_TEST_RUNNER(ccnxTlvCodec_Hash) +{ + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(ccnxTlvCodec_Hash) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(ccnxTlvCodec_Hash) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, ccnxTlvCodecHash_DecodeValue); + LONGBOW_RUN_TEST_CASE(Global, ccnxTlvCodecHash_DecodeValue_InvalidHash); + LONGBOW_RUN_TEST_CASE(Global, ccnxTlvCodecHash_DecodeValue_InvalidLength_SHA256); + LONGBOW_RUN_TEST_CASE(Global, ccnxTlvCodecHash_DecodeValue_InvalidLength_SHA512); + LONGBOW_RUN_TEST_CASE(Global, ccnxTlvCodecHash_DecodeValue_InvalidLength_App); + + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecSchemaV1HashCodec_Encode); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecSchemaV1HashCodec_Encode_InvalidLength); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + if (parcSafeMemory_ReportAllocation(STDOUT_FILENO) != 0) { + printf("('%s' leaks memory by %d (allocs - frees)) ", longBowTestCase_GetName(testCase), parcMemory_Outstanding()); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, ccnxTlvCodecHash_DecodeValue) +{ + // -- 32-byte hash + uint8_t encoded[] = { + 0x00, CCNxCodecSchemaV1Types_HashType_SHA256, 0x00, 0x20, + 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, + }; + + PARCBuffer *tlvBuffer = parcBuffer_Wrap(encoded, sizeof(encoded), 0, sizeof(encoded)); + PARCBuffer *payloadBuffer = parcBuffer_Wrap(encoded + 4, sizeof(encoded) - 4, 0, sizeof(encoded) - 4); + + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(tlvBuffer); + PARCCryptoHash *hash = ccnxCodecSchemaV1HashCodec_DecodeValue(decoder, sizeof(encoded)); + assertNotNull(hash, "got non-NULL hash when it should have been an error (null)"); + + assertTrue(parcCryptoHash_GetDigestType(hash) == PARCCryptoHashType_SHA256, "Expected to decode the correct hash type."); + assertTrue(parcBuffer_Equals(payloadBuffer, parcCryptoHash_GetDigest(hash)), "Expected the digest to match."); + + CCNxCodecError *error = ccnxCodecTlvDecoder_GetError(decoder); + assertNull(error, "Got null error when it should have been set"); + + parcCryptoHash_Release(&hash); + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&tlvBuffer); + parcBuffer_Release(&payloadBuffer); +} + +LONGBOW_TEST_CASE(Global, ccnxTlvCodecHash_DecodeValue_InvalidHash) +{ + // -- 32-byte hash + uint8_t encoded[] = { + 0x00, 0xFF, 0x00, 0x20, + 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, + }; + + PARCBuffer *tlvBuffer = parcBuffer_Wrap(encoded, sizeof(encoded), 0, sizeof(encoded)); + + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(tlvBuffer); + PARCCryptoHash *hash = ccnxCodecSchemaV1HashCodec_DecodeValue(decoder, sizeof(encoded)); + assertNull(hash, "Should not have decoded an incorrect hash digest"); + + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&tlvBuffer); +} + +LONGBOW_TEST_CASE(Global, ccnxTlvCodecHash_DecodeValue_InvalidLength_SHA256) +{ + // -- 32-byte hash + uint8_t encoded[] = { + 0x00, CCNxCodecSchemaV1Types_HashType_SHA256, 0x00, 0x18, + 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, + }; + + PARCBuffer *tlvBuffer = parcBuffer_Wrap(encoded, sizeof(encoded), 0, sizeof(encoded)); + + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(tlvBuffer); + PARCCryptoHash *hash = ccnxCodecSchemaV1HashCodec_DecodeValue(decoder, sizeof(encoded)); + assertNull(hash, "Should not have decoded a SHA256 hash digest with an incorrect length"); + + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&tlvBuffer); +} + +LONGBOW_TEST_CASE(Global, ccnxTlvCodecHash_DecodeValue_InvalidLength_SHA512) +{ + // -- 32-byte hash + uint8_t encoded[] = { + 0x00, CCNxCodecSchemaV1Types_HashType_SHA512, 0x00, 0x18, + 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, + }; + + PARCBuffer *tlvBuffer = parcBuffer_Wrap(encoded, sizeof(encoded), 0, sizeof(encoded)); + + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(tlvBuffer); + PARCCryptoHash *hash = ccnxCodecSchemaV1HashCodec_DecodeValue(decoder, sizeof(encoded)); + assertNull(hash, "Should not have decoded a SHA512 hash digest with an incorrect length"); + + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&tlvBuffer); +} + +LONGBOW_TEST_CASE(Global, ccnxTlvCodecHash_DecodeValue_InvalidLength_App) +{ + // -- 32-byte hash + uint8_t encoded[] = { + 0x00, CCNxCodecSchemaV1Types_HashType_App, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00,0x00,0x00, 0x00, 0x00 + }; + + PARCBuffer *tlvBuffer = parcBuffer_Wrap(encoded, sizeof(encoded), 0, sizeof(encoded)); + + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(tlvBuffer); + PARCCryptoHash *hash = ccnxCodecSchemaV1HashCodec_DecodeValue(decoder, sizeof(encoded)); + assertNotNull(hash, "Should have decoded an application hash digest with an arbitrary length"); + + parcCryptoHash_Release(&hash); + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&tlvBuffer); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecSchemaV1HashCodec_Encode) +{ + uint8_t encoded[] = { + 0x00, CCNxCodecSchemaV1Types_HashType_SHA256, 0x00, 0x20, + 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, + }; + + PARCBuffer *trueEncoding = parcBuffer_Wrap(encoded, sizeof(encoded), 0, sizeof(encoded)); + + // Create the hash + PARCBuffer *payloadBuffer = parcBuffer_Allocate(0x20); + PARCCryptoHash *expectedHash = parcCryptoHash_Create(PARCCryptoHashType_SHA256, payloadBuffer); + + // Encode it + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + ssize_t length = ccnxCodecSchemaV1HashCodec_Encode(encoder, expectedHash); + assertFalse(length < 0, "Got error on encode: %s", ccnxCodecError_ToString(ccnxCodecTlvEncoder_GetError(encoder))); + assertTrue(length == sizeof(encoded), "Wrong length, expected %zd got %zd", sizeof(encoded), length); + + // Check for equality + ccnxCodecTlvEncoder_Finalize(encoder); + PARCBuffer *testEncoding = ccnxCodecTlvEncoder_CreateBuffer(encoder); + assertTrue(parcBuffer_Equals(trueEncoding, testEncoding), "The hash was encoded incorrectly.") + { + printf("Expected\n"); + parcBuffer_Display(trueEncoding, 3); + printf("Got\n"); + parcBuffer_Display(testEncoding, 3); + } + + parcBuffer_Release(&testEncoding); + parcBuffer_Release(&trueEncoding); + + parcCryptoHash_Release(&expectedHash); + parcBuffer_Release(&payloadBuffer); + + ccnxCodecTlvEncoder_Destroy(&encoder); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecSchemaV1HashCodec_Encode_InvalidLength) +{ +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(ccnxTlvCodec_Hash); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-common/ccnx/common/codec/schema_v1/test/test_ccnxCodecSchemaV1_LinkCodec.c b/libccnx-common/ccnx/common/codec/schema_v1/test/test_ccnxCodecSchemaV1_LinkCodec.c new file mode 100755 index 00000000..599e3a5a --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/test/test_ccnxCodecSchemaV1_LinkCodec.c @@ -0,0 +1,450 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../ccnxCodecSchemaV1_LinkCodec.c" +#include <parc/algol/parc_SafeMemory.h> +#include <LongBow/unit-test.h> + +#include <ccnx/common/codec/ccnxCodec_Error.h> + +LONGBOW_TEST_RUNNER(ccnxTlvCodec_Name) +{ + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(ccnxTlvCodec_Name) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(ccnxTlvCodec_Name) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, ccnxTlvCodecLink_DecodeValue_NameOnly); + LONGBOW_RUN_TEST_CASE(Global, ccnxTlvCodecLink_DecodeValue_AllFields); + LONGBOW_RUN_TEST_CASE(Global, ccnxTlvCodecLink_DecodeValue_NoName); + LONGBOW_RUN_TEST_CASE(Global, ccnxTlvCodecLink_DecodeValue_ExtraField); + LONGBOW_RUN_TEST_CASE(Global, ccnxTlvCodecLink_DecodeValue_DupName); + LONGBOW_RUN_TEST_CASE(Global, ccnxTlvCodecLink_DecodeValue_DupKeyId); + LONGBOW_RUN_TEST_CASE(Global, ccnxTlvCodecLink_DecodeValue_DupHash); + LONGBOW_RUN_TEST_CASE(Global, ccnxTlvCodecLink_DecodeValue_FieldOverrun); + LONGBOW_RUN_TEST_CASE(Global, ccnxTlvCodecLink_DecodeValue_Underrun); + + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecSchemaV1LinkCodec_Encode); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + if (parcSafeMemory_ReportAllocation(STDOUT_FILENO) != 0) { + printf("('%s' leaks memory by %d (allocs - frees)) ", longBowTestCase_GetName(testCase), parcMemory_Outstanding()); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, ccnxTlvCodecLink_DecodeValue_NameOnly) +{ + CCNxName *truth = ccnxName_CreateFromCString("lci:/3=rope"); + + uint8_t encoded[] = { + 0x00, 0x00, 0x00, 8, + 0x00, 0x03, 0x00, 4, + 'r', 'o', 'p', 'e' + }; + + PARCBuffer *buffer = parcBuffer_Wrap(encoded, sizeof(encoded), 0, sizeof(encoded)); + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(buffer); + CCNxLink *link = ccnxCodecSchemaV1LinkCodec_DecodeValue(decoder, sizeof(encoded)); + assertNotNull(link, "got null link: %s", ccnxCodecError_ToString(ccnxCodecTlvDecoder_GetError(decoder))); + + const CCNxName *test = ccnxLink_GetName(link); + assertTrue(ccnxName_Equals(truth, test), "Wrong name") + { + printf("Expected\n"); + ccnxName_Display(truth, 3); + printf("Got\n"); + ccnxName_Display(test, 3); + } + + PARCBuffer *testKeyid = ccnxLink_GetKeyID(link); + assertNull(testKeyid, "Got a keyid without the wire encoding for it"); + + PARCBuffer *testHash = ccnxLink_GetContentObjectHash(link); + assertNull(testHash, "got a hash without the wire encoding for it"); + + ccnxLink_Release(&link); + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&buffer); + ccnxName_Release(&truth); +} + +LONGBOW_TEST_CASE(Global, ccnxTlvCodecLink_DecodeValue_AllFields) +{ + CCNxName *truth = ccnxName_CreateFromCString("lci:/3=rope"); + + uint8_t encoded[] = { + // -- name + 0x00, 0x00, 0x00, 8, + 0x00, 0x03, 0x00, 4, + 'r', 'o', 'p', 'e', + // -- keyid + 0x00, 0x01, 0x00, 8, + 0xa0, 0xa1, 0xa2, 0xa3, + 0xa4, 0xa5, 0xa6, 0xa7, + // -- hash + 0x00, 0x02, 0x00, 16, + 0xb0, 0xb1, 0xb2, 0xb3, + 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, + 0xbc, 0xbd, 0xbe, 0xbf, + }; + + PARCBuffer *buffer = parcBuffer_Wrap(encoded, sizeof(encoded), 0, sizeof(encoded)); + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(buffer); + CCNxLink *link = ccnxCodecSchemaV1LinkCodec_DecodeValue(decoder, sizeof(encoded)); + assertNotNull(link, "got null link: %s", ccnxCodecError_ToString(ccnxCodecTlvDecoder_GetError(decoder))); + + const CCNxName *test = ccnxLink_GetName(link); + assertTrue(ccnxName_Equals(truth, test), "Wrong name") + { + printf("Expected\n"); + ccnxName_Display(truth, 3); + printf("Got\n"); + ccnxName_Display(test, 3); + } + + PARCBuffer *keyid = parcBuffer_Wrap(encoded, sizeof(encoded), 16, 24); + PARCBuffer *testKeyid = ccnxLink_GetKeyID(link); + assertTrue(parcBuffer_Equals(keyid, testKeyid), "Wrong keyid") + { + printf("Expected\n"); + parcBuffer_Display(keyid, 3); + printf("Got\n"); + parcBuffer_Display(testKeyid, 3); + } + + PARCBuffer *hash = parcBuffer_Wrap(encoded, sizeof(encoded), 28, 44); + PARCBuffer *testHash = ccnxLink_GetContentObjectHash(link); + assertTrue(parcBuffer_Equals(hash, testHash), "Wrong hash") + { + printf("Expected\n"); + parcBuffer_Display(hash, 3); + printf("Got\n"); + parcBuffer_Display(testHash, 3); + } + + parcBuffer_Release(&hash); + parcBuffer_Release(&keyid); + + ccnxLink_Release(&link); + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&buffer); + ccnxName_Release(&truth); +} + +/* + * wire format missing name + */ +LONGBOW_TEST_CASE(Global, ccnxTlvCodecLink_DecodeValue_NoName) +{ + uint8_t encoded[] = { + // -- keyid + 0x00, 0x01, 0x00, 8, + 0xa0, 0xa1, 0xa2, 0xa3, + 0xa4, 0xa5, 0xa6, 0xa7, + // -- hash + 0x00, 0x02, 0x00, 16, + 0xb0, 0xb1, 0xb2, 0xb3, + 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, + 0xbc, 0xbd, 0xbe, 0xbf, + }; + + PARCBuffer *buffer = parcBuffer_Wrap(encoded, sizeof(encoded), 0, sizeof(encoded)); + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(buffer); + CCNxLink *link = ccnxCodecSchemaV1LinkCodec_DecodeValue(decoder, sizeof(encoded)); + assertNull(link, "got non-null link when it should have been an error (null)"); + + CCNxCodecError *error = ccnxCodecTlvDecoder_GetError(decoder); + assertNotNull(error, "Got null error when it should have been set"); + + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&buffer); +} + +/* + * Wire format has an extra TLV in it that's not part of the spec + */ +LONGBOW_TEST_CASE(Global, ccnxTlvCodecLink_DecodeValue_ExtraField) +{ + uint8_t encoded[] = { + // -- name + 0x00, 0x00, 0x00, 8, + 0x00, 0x03, 0x00, 4, + 'r', 'o', 'p', 'e', + // -- keyid + 0x00, 0x01, 0x00, 8, + 0xa0, 0xa1, 0xa2, 0xa3, + 0xa4, 0xa5, 0xa6, 0xa7, + // -- hash + 0x00, 0x02, 0x00, 16, + 0xb0, 0xb1, 0xb2, 0xb3, + 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, + 0xbc, 0xbd, 0xbe, 0xbf, + // -- extra + 0x00, 0xFF, 0x00, 4, + 0xc0, 0xc1, 0xc2, 0xc3, + }; + + PARCBuffer *buffer = parcBuffer_Wrap(encoded, sizeof(encoded), 0, sizeof(encoded)); + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(buffer); + CCNxLink *link = ccnxCodecSchemaV1LinkCodec_DecodeValue(decoder, sizeof(encoded)); + assertNull(link, "got non-null link when it should have been an error (null)"); + + CCNxCodecError *error = ccnxCodecTlvDecoder_GetError(decoder); + assertNotNull(error, "Got null error when it should have been set"); + + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, ccnxTlvCodecLink_DecodeValue_DupName) +{ + uint8_t encoded[] = { + // -- name + 0x00, 0x00, 0x00, 8, + 0x00, 0x03, 0x00, 4, + 'r', 'o', 'p', 'e', + // -- keyid + 0x00, 0x01, 0x00, 8, + 0xa0, 0xa1, 0xa2, 0xa3, + 0xa4, 0xa5, 0xa6, 0xa7, + // -- hash + 0x00, 0x02, 0x00, 16, + 0xb0, 0xb1, 0xb2, 0xb3, + 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, + 0xbc, 0xbd, 0xbe, 0xbf, + // -- name + 0x00, 0x00, 0x00, 8, + 0x00, 0x03, 0x00, 4, + 'r', 'o', 'p', 'e', + }; + + PARCBuffer *buffer = parcBuffer_Wrap(encoded, sizeof(encoded), 0, sizeof(encoded)); + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(buffer); + CCNxLink *link = ccnxCodecSchemaV1LinkCodec_DecodeValue(decoder, sizeof(encoded)); + assertNull(link, "got non-null link when it should have been an error (null)"); + + CCNxCodecError *error = ccnxCodecTlvDecoder_GetError(decoder); + assertNotNull(error, "Got null error when it should have been set"); + + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, ccnxTlvCodecLink_DecodeValue_DupKeyId) +{ + uint8_t encoded[] = { + // -- name + 0x00, 0x00, 0x00, 8, + 0x00, 0x03, 0x00, 4, + 'r', 'o', 'p', 'e', + // -- keyid + 0x00, 0x01, 0x00, 8, + 0xa0, 0xa1, 0xa2, 0xa3, + 0xa4, 0xa5, 0xa6, 0xa7, + // -- hash + 0x00, 0x02, 0x00, 16, + 0xb0, 0xb1, 0xb2, 0xb3, + 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, + 0xbc, 0xbd, 0xbe, 0xbf, + // -- keyid + 0x00, 0x01, 0x00, 8, + 0xa0, 0xa1, 0xa2, 0xa3, + 0xa4, 0xa5, 0xa6, 0xa7, + }; + + PARCBuffer *buffer = parcBuffer_Wrap(encoded, sizeof(encoded), 0, sizeof(encoded)); + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(buffer); + CCNxLink *link = ccnxCodecSchemaV1LinkCodec_DecodeValue(decoder, sizeof(encoded)); + assertNull(link, "got non-null link when it should have been an error (null)"); + + CCNxCodecError *error = ccnxCodecTlvDecoder_GetError(decoder); + assertNotNull(error, "Got null error when it should have been set"); + + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, ccnxTlvCodecLink_DecodeValue_DupHash) +{ + uint8_t encoded[] = { + // -- name + 0x00, 0x00, 0x00, 8, + 0x00, 0x03, 0x00, 4, + 'r', 'o', 'p', 'e', + // -- keyid + 0x00, 0x01, 0x00, 8, + 0xa0, 0xa1, 0xa2, 0xa3, + 0xa4, 0xa5, 0xa6, 0xa7, + // -- hash + 0x00, 0x02, 0x00, 16, + 0xb0, 0xb1, 0xb2, 0xb3, + 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, + 0xbc, 0xbd, 0xbe, 0xbf, + // -- hash + 0x00, 0x02, 0x00, 16, + 0xb0, 0xb1, 0xb2, 0xb3, + 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, + 0xbc, 0xbd, 0xbe, 0xbf, + }; + + PARCBuffer *buffer = parcBuffer_Wrap(encoded, sizeof(encoded), 0, sizeof(encoded)); + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(buffer); + CCNxLink *link = ccnxCodecSchemaV1LinkCodec_DecodeValue(decoder, sizeof(encoded)); + assertNull(link, "got non-null link when it should have been an error (null)"); + + CCNxCodecError *error = ccnxCodecTlvDecoder_GetError(decoder); + assertNotNull(error, "Got null error when it should have been set"); + + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, ccnxTlvCodecLink_DecodeValue_FieldOverrun) +{ + // name length is beyond end of fragment + uint8_t encoded[] = { + 0x00, 0x00, 0x00, 30, + 0x00, 0x03, 0x00, 4, + 'r', 'o', 'p', 'e' + }; + + PARCBuffer *buffer = parcBuffer_Wrap(encoded, sizeof(encoded), 0, sizeof(encoded)); + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(buffer); + CCNxLink *link = ccnxCodecSchemaV1LinkCodec_DecodeValue(decoder, sizeof(encoded)); + assertNull(link, "got non-null link when it should have been an error (null)"); + + CCNxCodecError *error = ccnxCodecTlvDecoder_GetError(decoder); + assertNotNull(error, "Got null error when it should have been set"); + + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, ccnxTlvCodecLink_DecodeValue_Underrun) +{ + // buffer is too short to even parse the T and L + uint8_t encoded[] = { + 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + // the "2" makes it too short to parse + PARCBuffer *buffer = parcBuffer_Wrap(encoded, sizeof(encoded), 0, 2); + + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(buffer); + CCNxLink *link = ccnxCodecSchemaV1LinkCodec_DecodeValue(decoder, sizeof(encoded)); + assertNull(link, "got non-null link when it should have been an error (null)"); + + CCNxCodecError *error = ccnxCodecTlvDecoder_GetError(decoder); + assertNotNull(error, "Got null error when it should have been set"); + + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&buffer); +} + +// ============ + +LONGBOW_TEST_CASE(Global, ccnxCodecSchemaV1LinkCodec_Encode) +{ + uint8_t encoded[] = { + // -- name + 0x00, 0x00, 0x00, 8, + 0x00, 0x03, 0x00, 4, + 'r', 'o', 'p', 'e', + // -- keyid + 0x00, 0x01, 0x00, 8, + 0xa0, 0xa1, 0xa2, 0xa3, + 0xa4, 0xa5, 0xa6, 0xa7, + // -- hash + 0x00, 0x02, 0x00, 16, + 0xb0, 0xb1, 0xb2, 0xb3, + 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, + 0xbc, 0xbd, 0xbe, 0xbf, + }; + + CCNxName *trueName = ccnxName_CreateFromCString("lci:/3=rope"); + PARCBuffer *trueKeyId = parcBuffer_Wrap(encoded, sizeof(encoded), 16, 24); + PARCBuffer *trueHash = parcBuffer_Wrap(encoded, sizeof(encoded), 28, 44); + PARCBuffer *trueEncoding = parcBuffer_Wrap(encoded, sizeof(encoded), 0, sizeof(encoded)); + + CCNxLink *link = ccnxLink_Create(trueName, trueKeyId, trueHash); + + // now encode it and compare the the trueEncoding + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + + ssize_t length = ccnxCodecSchemaV1LinkCodec_Encode(encoder, link); + assertFalse(length < 0, "Got error on encode: %s", ccnxCodecError_ToString(ccnxCodecTlvEncoder_GetError(encoder))); + assertTrue(length == sizeof(encoded), "Wrong length, expected %zd got %zd", sizeof(encoded), length); + + ccnxCodecTlvEncoder_Finalize(encoder); + PARCBuffer *testEncoding = ccnxCodecTlvEncoder_CreateBuffer(encoder); + assertTrue(parcBuffer_Equals(trueEncoding, testEncoding), "Wrong hash") + { + printf("Expected\n"); + parcBuffer_Display(trueEncoding, 3); + printf("Got\n"); + parcBuffer_Display(testEncoding, 3); + } + + parcBuffer_Release(&testEncoding); + parcBuffer_Release(&trueEncoding); + parcBuffer_Release(&trueHash); + parcBuffer_Release(&trueKeyId); + ccnxName_Release(&trueName); + + ccnxLink_Release(&link); + ccnxCodecTlvEncoder_Destroy(&encoder); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(ccnxTlvCodec_Name); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-common/ccnx/common/codec/schema_v1/test/test_ccnxCodecSchemaV1_ManifestDecoder.c b/libccnx-common/ccnx/common/codec/schema_v1/test/test_ccnxCodecSchemaV1_ManifestDecoder.c new file mode 100755 index 00000000..3c1609ab --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/test/test_ccnxCodecSchemaV1_ManifestDecoder.c @@ -0,0 +1,267 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../ccnxCodecSchemaV1_ManifestDecoder.c" +#include <parc/algol/parc_SafeMemory.h> +#include <LongBow/unit-test.h> + +#include "testrig_encoder.c" + +#include <ccnx/common/ccnx_Manifest.h> + +// ========================================================================= + +LONGBOW_TEST_RUNNER(ccnxCodecSchemaV1_ManifestDecoder) +{ + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(ccnxCodecSchemaV1_ManifestDecoder) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(ccnxCodecSchemaV1_ManifestDecoder) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + + +// ========================================================================= + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecSchemaV1ManifestDecoder_Decode); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecSchemaV1ManifestDecoder_DecodeType); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecSchemaV1ManifestDecoder_DecodeHashGroup); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecSchemaV1ManifestDecoder_DecodeHashGroupMetadata); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, ccnxCodecSchemaV1ManifestDecoder_Decode) +{ + uint8_t rawManifest[40] = { 0x00, 0x07, 0x00, 0x24, + 0x00, 0x02, 0x00, 0x20, + 0x46, 0x46, 0x46, 0x46, + 0x46, 0x46, 0x46, 0x46, + 0x46, 0x46, 0x46, 0x46, + 0x46, 0x46, 0x46, 0x46, + 0x46, 0x46, 0x46, 0x46, + 0x46, 0x46, 0x46, 0x46, + 0x46, 0x46, 0x46, 0x46, + 0x46, 0x46, 0x46, 0x46 }; + PARCBuffer *wireFormat = parcBuffer_Flip(parcBuffer_CreateFromArray(rawManifest, 40)); + + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(wireFormat); + CCNxTlvDictionary *dict = ccnxCodecSchemaV1TlvDictionary_CreateManifest(); + + bool result = ccnxCodecSchemaV1ManifestDecoder_Decode(decoder, dict); + assertTrue(result, "Expected the manifest to be decoded correctly"); + + ccnxTlvDictionary_Release(&dict); + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&wireFormat); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecSchemaV1ManifestDecoder_DecodeType) +{ + uint8_t rawManifest[40] = { 0x00, 0x07, 0x00, 0x24, + 0x00, 0x02, 0x00, 0x20, + 0x46, 0x46, 0x46, 0x46, + 0x46, 0x46, 0x46, 0x46, + 0x46, 0x46, 0x46, 0x46, + 0x46, 0x46, 0x46, 0x46, + 0x46, 0x46, 0x46, 0x46, + 0x46, 0x46, 0x46, 0x46, + 0x46, 0x46, 0x46, 0x46, + 0x46, 0x46, 0x46, 0x46 }; + PARCBuffer *wireFormat = parcBuffer_Flip(parcBuffer_CreateFromArray(rawManifest, 40)); + + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(wireFormat); + CCNxTlvDictionary *dict = ccnxCodecSchemaV1TlvDictionary_CreateManifest(); + + uint16_t type = ccnxCodecTlvDecoder_GetType(decoder); + uint16_t length = ccnxCodecTlvDecoder_GetLength(decoder); + + bool result = _decodeType(decoder, dict, type, length); + assertTrue(result, "Expected the manifest type to be correctly decoded at the top level"); + + ccnxManifest_Release(&dict); + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&wireFormat); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecSchemaV1ManifestDecoder_DecodeHashGroup) +{ + uint8_t rawManifest[40] = { 0x00, 0x07, 0x00, 0x24, + 0x00, 0x02, 0x00, 0x20, + 0x46, 0x46, 0x46, 0x46, + 0x46, 0x46, 0x46, 0x46, + 0x46, 0x46, 0x46, 0x46, + 0x46, 0x46, 0x46, 0x46, + 0x46, 0x46, 0x46, 0x46, + 0x46, 0x46, 0x46, 0x46, + 0x46, 0x46, 0x46, 0x46, + 0x46, 0x46, 0x46, 0x46 }; + PARCBuffer *wireFormat = parcBuffer_Flip(parcBuffer_CreateFromArray(rawManifest, 40)); + + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(wireFormat); + CCNxTlvDictionary *dict = ccnxCodecSchemaV1TlvDictionary_CreateManifest(); + + ccnxCodecTlvDecoder_GetType(decoder); // swallow type + uint16_t length = ccnxCodecTlvDecoder_GetLength(decoder); + + CCNxManifestHashGroup *group = ccnxManifestHashGroup_Create(); + bool result = _decodeHashGroup(decoder, dict, group, length); + assertTrue(result, "Expected hash group to be decoded correctly."); + + PARCBuffer *expectedPointer = parcBuffer_AllocateCString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"); + CCNxManifestHashGroupPointer *pointer = ccnxManifestHashGroup_GetPointerAtIndex(group, 0); + const PARCBuffer *actualPointer = ccnxManifestHashGroupPointer_GetDigest(pointer); + assertTrue(parcBuffer_Equals(expectedPointer, actualPointer), "Expected decoded pointer to equal %s, got %s", parcBuffer_ToHexString(expectedPointer), parcBuffer_ToHexString(actualPointer)); + + parcBuffer_Release(&expectedPointer); + + ccnxManifestHashGroup_Release(&group); + ccnxManifest_Release(&dict); + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&wireFormat); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecSchemaV1ManifestDecoder_DecodeHashGroupMetadata) +{ + // Re-build the expected metadata from the manifest + CCNxName *groupLocator = ccnxName_CreateFromCString("ccnx:/locator"); + PARCBuffer *digest = parcBuffer_Allocate(16); + for (size_t i = 0; i < parcBuffer_Limit(digest); i++) { + parcBuffer_PutUint8(digest, 0); + } + parcBuffer_Flip(digest); + size_t entrySize = 1; + size_t dataSize = 2; + size_t blockSize = 3; + size_t treeHeight = 4; + + // Compute the expected size of this metadata group. + size_t metadataSize = 4 * (4 + 8) + 4 + parcBuffer_Limit(digest) + 4 + strlen("ccnx:/locator"); + + // See test_ccnxCodecSchemaV1_ManifestEncoder.c for the packet construction details. + uint8_t rawMetadata[89] = { 0x00, CCNxCodecSchemaV1Types_CCNxManifestHashGroup_Metadata, + 0x00, metadataSize, + 0x00, CCNxCodecSchemaV1Types_CCNxManifestHashGroupMetadata_Locator, + 0x00, strlen("ccnx:/locator"), + 'c', 'c', + 'n', 'x', + ':', '/', + 'l', 'o', + 'c', 'a', + 't', 'o', + 'r', + 0x00, CCNxCodecSchemaV1Types_CCNxManifestHashGroupMetadata_DataSize, + 0x00, 0x08, + 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00, + 0x00, dataSize, + 0x00, CCNxCodecSchemaV1Types_CCNxManifestHashGroupMetadata_BlockSize, + 0x00, 0x08, + 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00, + 0x00, blockSize, + 0x00, CCNxCodecSchemaV1Types_CCNxManifestHashGroupMetadata_EntrySize, + 0x00, 0x08, + 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00, + 0x00, entrySize, + 0x00, CCNxCodecSchemaV1Types_CCNxManifestHashGroupMetadata_TreeHeight, + 0x00, 0x08, + 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00, + 0x00, treeHeight, + 0x00, CCNxCodecSchemaV1Types_CCNxManifestHashGroupMetadata_OverallDataSha256, + 0x00, parcBuffer_Remaining(digest), + 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00 }; + PARCBuffer *wireFormat = parcBuffer_Flip(parcBuffer_CreateFromArray(rawMetadata, sizeof(rawMetadata))); + + // Create the encoder and swallow the top level container + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(wireFormat); + ccnxCodecTlvDecoder_GetType(decoder); // swallow type + uint16_t length = ccnxCodecTlvDecoder_GetLength(decoder); + + // Decode the metadata + CCNxManifestHashGroup *group = ccnxManifestHashGroup_Create(); + bool result = _decodeHashGroupMetadata(decoder, group, length); + assertTrue(result, "Expected hash group metadata to be decoded correctly."); + + const CCNxName *actualLocator = ccnxManifestHashGroup_GetLocator(group); + size_t actualEntrySize = ccnxManifestHashGroup_GetEntrySize(group); + size_t actualDataSize = ccnxManifestHashGroup_GetDataSize(group); + size_t actualBlockSize = ccnxManifestHashGroup_GetBlockSize(group); + size_t actualTreeHeight = ccnxManifestHashGroup_GetTreeHeight(group); + const PARCBuffer *actualDigest = ccnxManifestHashGroup_GetOverallDataDigest(group); + + assertTrue(ccnxName_Equals(groupLocator, actualLocator), "Expected decoded locator to equal %s, got %s", ccnxName_ToString(groupLocator), ccnxName_ToString(actualLocator)); + assertTrue(entrySize == actualEntrySize, "Expected %zu entry size, got %zu", entrySize, actualEntrySize); + assertTrue(dataSize == actualDataSize, "Expected %zu data size, got %zu", dataSize, actualDataSize); + assertTrue(blockSize == actualBlockSize, "Expected %zu block size, got %zu", blockSize, actualBlockSize); + assertTrue(treeHeight == actualTreeHeight, "Expected %zu tree height, got %zu", treeHeight, actualTreeHeight); + assertTrue(parcBuffer_Equals(digest, actualDigest), "Expected %s digest, got %s", parcBuffer_ToHexString(digest), parcBuffer_ToHexString(actualDigest)); + + parcBuffer_Release(&digest); + ccnxName_Release(&groupLocator); + + ccnxManifestHashGroup_Release(&group); + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&wireFormat); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(ccnxCodecSchemaV1_ManifestDecoder); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-common/ccnx/common/codec/schema_v1/test/test_ccnxCodecSchemaV1_ManifestEncoder.c b/libccnx-common/ccnx/common/codec/schema_v1/test/test_ccnxCodecSchemaV1_ManifestEncoder.c new file mode 100755 index 00000000..a0e36abf --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/test/test_ccnxCodecSchemaV1_ManifestEncoder.c @@ -0,0 +1,306 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../ccnxCodecSchemaV1_ManifestEncoder.c" +#include <parc/algol/parc_SafeMemory.h> +#include <LongBow/unit-test.h> + +#include "testrig_encoder.c" + +#include <ccnx/common/ccnx_Manifest.h> + +// ========================================================================= + +LONGBOW_TEST_RUNNER(ccnxCodecSchemaV1_ManifestEncoder) +{ + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(ccnxCodecSchemaV1_ManifestEncoder) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(ccnxCodecSchemaV1_ManifestEncoder) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + + +// ========================================================================= + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecSchemaV1ManifestEncoder_EncodeEmpty); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecSchemaV1ManifestEncoder_EncodeSingleHashGroup); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecSchemaV1ManifestEncoder_EncodeSingleHashGroup_WithMetadata); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecSchemaV1ManifestEncoder_AddPointer); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, ccnxCodecSchemaV1ManifestEncoder_EncodeEmpty) +{ + CCNxName *locator = ccnxName_CreateFromCString("lci:/name"); + CCNxManifest *manifest = ccnxManifest_Create(locator); + + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + size_t result = ccnxCodecSchemaV1ManifestEncoder_Encode(encoder, manifest); + + assertTrue(result == 0, "Expected an empty Manifest to be encoded to size 0, got %zu", result); + + ccnxCodecTlvEncoder_Destroy(&encoder); + ccnxManifest_Release(&manifest); + ccnxName_Release(&locator); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecSchemaV1ManifestEncoder_AddPointer) +{ + CCNxName *locator = ccnxName_CreateFromCString("ccnx:/name"); + CCNxManifest *manifest = ccnxManifest_Create(locator); + + CCNxManifestHashGroup *group = ccnxManifestHashGroup_Create(); + PARCBuffer *pointer = parcBuffer_Flip(parcBuffer_ParseHexString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")); + ccnxManifestHashGroup_AppendPointer(group, CCNxManifestHashGroupPointerType_Data, pointer); + + ccnxManifest_AddHashGroup(manifest, group); + + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + size_t result = ccnxCodecSchemaV1ManifestEncoder_Encode(encoder, manifest); + size_t expected = 4 + 4 + parcBuffer_Remaining(pointer); // hash group TL, pointer TL, pointer V + + assertTrue(result == expected, "Expected an empty Manifest to be encoded to size %zu, got %zu", expected, result); + + CCNxCodecNetworkBufferIoVec *iovec = ccnxCodecTlvEncoder_CreateIoVec(encoder); + const struct iovec *vector = ccnxCodecNetworkBufferIoVec_GetArray(iovec); + + assertTrue(vector->iov_len == expected, "Expected the IO vector to contain the encoded manifest"); + assertTrue(memcmp(vector->iov_base + 8, parcBuffer_Overlay(pointer, parcBuffer_Remaining(pointer)), vector->iov_len - 8) == 0, "Expected the same pointer to be encoded"); + + uint16_t expectedType = CCNxCodecSchemaV1Types_CCNxManifestHashGroup_DataPointer; + uint8_t *base = (uint8_t *) vector->iov_base; + uint16_t actualType = (base[4] << 8) | base[5]; + assertTrue(expectedType == actualType, "Expected the type to be written correctly as CCNxCodecSchemaV1Types_CCNxManifestHashGroup_DataPointer"); + + ccnxCodecNetworkBufferIoVec_Release(&iovec); + + // Cleanup + parcBuffer_Release(&pointer); + ccnxManifestHashGroup_Release(&group); + + ccnxCodecTlvEncoder_Destroy(&encoder); + ccnxManifest_Release(&manifest); + ccnxName_Release(&locator); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecSchemaV1ManifestEncoder_EncodeSingleHashGroup) +{ + CCNxName *locator = ccnxName_CreateFromCString("ccnx:/name"); + CCNxManifest *manifest = ccnxManifest_Create(locator); + + CCNxManifestHashGroup *group = ccnxManifestHashGroup_Create(); + PARCBuffer *pointer = parcBuffer_Flip(parcBuffer_ParseHexString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")); + ccnxManifestHashGroup_AppendPointer(group, CCNxManifestHashGroupPointerType_Data, pointer); + + ccnxManifest_AddHashGroup(manifest, group); + + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + size_t result = ccnxCodecSchemaV1ManifestEncoder_Encode(encoder, manifest); + size_t expected = 4 + 4 + parcBuffer_Remaining(pointer); // hash group TL, pointer TL, pointer V + + assertTrue(result == expected, "Expected an empty Manifest to be encoded to size %zu, got %zu", expected, result); + + CCNxCodecNetworkBufferIoVec *iovec = ccnxCodecTlvEncoder_CreateIoVec(encoder); + const struct iovec *vector = ccnxCodecNetworkBufferIoVec_GetArray(iovec); + + uint8_t expectedVector[24] = { 0x00, 0x07, 0x00, 0x14, + 0x00, 0x02, 0x00, 0x10, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF }; + assertTrue(vector->iov_len == expected, "Expected the IO vector to contain the encoded manifest"); + assertTrue(memcmp(vector->iov_base, expectedVector, vector->iov_len) == 0, "Expected the same pointer to be encoded"); + + ccnxCodecNetworkBufferIoVec_Release(&iovec); + + // Cleanup + parcBuffer_Release(&pointer); + ccnxManifestHashGroup_Release(&group); + + ccnxCodecTlvEncoder_Destroy(&encoder); + ccnxManifest_Release(&manifest); + ccnxName_Release(&locator); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecSchemaV1ManifestEncoder_EncodeSingleHashGroup_WithMetadata) +{ + CCNxName *locator = ccnxName_CreateFromCString("ccnx:/name"); + CCNxManifest *manifest = ccnxManifest_Create(locator); + + CCNxManifestHashGroup *group = ccnxManifestHashGroup_Create(); + PARCBuffer *pointer = parcBuffer_Flip(parcBuffer_ParseHexString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")); + ccnxManifestHashGroup_AppendPointer(group, CCNxManifestHashGroupPointerType_Data, pointer); + + // Set the metadata now + CCNxName *groupLocator = ccnxName_CreateFromCString("ccnx:/locator"); + ccnxManifestHashGroup_SetLocator(group, groupLocator); + + PARCBuffer *digest = parcBuffer_Allocate(16); + for (size_t i = 0; i < parcBuffer_Limit(digest); i++) { + parcBuffer_PutUint8(digest, 0); + } + parcBuffer_Flip(digest); + ccnxManifestHashGroup_SetOverallDataDigest(group, digest); + + size_t entrySize = 1; + ccnxManifestHashGroup_SetEntrySize(group, entrySize); + + size_t dataSize = 2; + ccnxManifestHashGroup_SetDataSize(group, dataSize); + + size_t blockSize = 3; + ccnxManifestHashGroup_SetBlockSize(group, blockSize); + + size_t treeHeight = 4; + ccnxManifestHashGroup_SetTreeHeight(group, treeHeight); + + // Add the hash group to the manifest + ccnxManifest_AddHashGroup(manifest, group); + + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + size_t result = ccnxCodecSchemaV1ManifestEncoder_Encode(encoder, manifest); + + // Compute the expected size with all of the metadata. + // This packet was crafted by hand. + size_t expected = 4; // hash group TL + expected += 4 + parcBuffer_Remaining(pointer); // pointer TL, pointer V + expected += 4; // metadata TL + expected += 4 * (4 + 8); // 64-bit integer property TLs, Vs + expected += 4 + parcBuffer_Remaining(digest); // digest T and V + expected += 4 + strlen("ccnx:/locator"); // name TL, segment TL, and segment V + + // Compute only the size of the metadata + size_t metadataSize = expected; + metadataSize -= 4; // top-level TL container + metadataSize -= (4 + parcBuffer_Remaining(pointer)); // pointer TLV + metadataSize -= 4; // metadata TL container + + assertTrue(result == expected, "Expected an empty Manifest to be encoded to size %zu, got %zu", expected, result); + + // Now do the encoding + CCNxCodecNetworkBufferIoVec *iovec = ccnxCodecTlvEncoder_CreateIoVec(encoder); + const struct iovec *vector = ccnxCodecNetworkBufferIoVec_GetArray(iovec); + + uint8_t expectedVector[129] = { 0x00, 0x07, + 0x00, expected - 4, + 0x00, CCNxCodecSchemaV1Types_CCNxManifestHashGroup_Metadata, + 0x00, metadataSize, + 0x00, CCNxCodecSchemaV1Types_CCNxManifestHashGroupMetadata_Locator, + 0x00, strlen("ccnx:/locator"), + 'c', 'c', + 'n', 'x', + ':', '/', + 'l', 'o', + 'c', 'a', + 't', 'o', + 'r', + 0x00, CCNxCodecSchemaV1Types_CCNxManifestHashGroupMetadata_DataSize, + 0x00, 0x08, + 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00, + 0x00, dataSize, + 0x00, CCNxCodecSchemaV1Types_CCNxManifestHashGroupMetadata_BlockSize, + 0x00, 0x08, + 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00, + 0x00, blockSize, + 0x00, CCNxCodecSchemaV1Types_CCNxManifestHashGroupMetadata_EntrySize, + 0x00, 0x08, + 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00, + 0x00, entrySize, + 0x00, CCNxCodecSchemaV1Types_CCNxManifestHashGroupMetadata_TreeHeight, + 0x00, 0x08, + 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00, + 0x00, treeHeight, + 0x00, CCNxCodecSchemaV1Types_CCNxManifestHashGroupMetadata_OverallDataSha256, + 0x00, parcBuffer_Remaining(digest), + 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00, + 0x00, CCNxCodecSchemaV1Types_CCNxManifestHashGroup_DataPointer, + 0x00, parcBuffer_Remaining(pointer), + 0xFF, 0xFF, + 0xFF, 0xFF, + 0xFF, 0xFF, + 0xFF, 0xFF, + 0xFF, 0xFF, + 0xFF, 0xFF, + 0xFF, 0xFF, + 0xFF, 0xFF }; + assertTrue(vector->iov_len == expected, "Expected the IO vector to contain the encoded manifest"); + assertTrue(memcmp(vector->iov_base, expectedVector, vector->iov_len) == 0, "Expected the same HashGroup to be encoded"); + + ccnxCodecNetworkBufferIoVec_Release(&iovec); + + // Cleanup + parcBuffer_Release(&pointer); + parcBuffer_Release(&digest); + ccnxManifestHashGroup_Release(&group); + ccnxName_Release(&groupLocator); + + ccnxCodecTlvEncoder_Destroy(&encoder); + ccnxManifest_Release(&manifest); + ccnxName_Release(&locator); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(ccnxCodecSchemaV1_ManifestEncoder); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-common/ccnx/common/codec/schema_v1/test/test_ccnxCodecSchemaV1_MessageDecoder.c b/libccnx-common/ccnx/common/codec/schema_v1/test/test_ccnxCodecSchemaV1_MessageDecoder.c new file mode 100644 index 00000000..ab339fc1 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/test/test_ccnxCodecSchemaV1_MessageDecoder.c @@ -0,0 +1,320 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../ccnxCodecSchemaV1_MessageDecoder.c" +#include <parc/algol/parc_SafeMemory.h> +#include <LongBow/unit-test.h> +#include <ccnx/common/codec/schema_v1/testdata/v1_content_nameA_keyid1_rsasha256.h> +#include <ccnx/common/codec/schema_v1/testdata/v1_interest_all_fields.h> + +#include "testrig_packetwrapper.c" + + +// ========================================================================= + +LONGBOW_TEST_RUNNER(ccnxCodecSchemaV1_MessageDecoder) +{ + LONGBOW_RUN_TEST_FIXTURE(ContentObject); + LONGBOW_RUN_TEST_FIXTURE(Interest); + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(ccnxCodecSchemaV1_MessageDecoder) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(ccnxCodecSchemaV1_MessageDecoder) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// ========================================================================= +// Utility functions to get a field in the format we expect for the testrig. +// We cannot use the facades because those try to assert a type on the dictionary which +// is not part of the MessageDecoder. + +static CCNxName * +_getName(CCNxTlvDictionary *contentObjectDictionary) +{ + return ccnxTlvDictionary_GetName(contentObjectDictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_NAME); +} + +static PARCBuffer * +_getPayload(const CCNxTlvDictionary *contentObjectDictionary) +{ + return ccnxTlvDictionary_GetBuffer(contentObjectDictionary, + CCNxCodecSchemaV1TlvDictionary_MessageFastArray_PAYLOAD); +} + +static int64_t +_getPayloadType(CCNxTlvDictionary *contentObjectDictionary) +{ + return (int64_t) ccnxTlvDictionary_GetInteger(contentObjectDictionary, + CCNxCodecSchemaV1TlvDictionary_MessageFastArray_PAYLOADTYPE); +} + +static int64_t +_getExpiryTime(CCNxTlvDictionary *contentObjectDictionary) +{ + return (int64_t) ccnxTlvDictionary_GetInteger(contentObjectDictionary, + CCNxCodecSchemaV1TlvDictionary_MessageFastArray_EXPIRY_TIME); +} + +static int64_t +_getEndChunkNumber(CCNxTlvDictionary *contentObjectDictionary) +{ + return (int64_t) ccnxTlvDictionary_GetInteger(contentObjectDictionary, + CCNxCodecSchemaV1TlvDictionary_MessageFastArray_ENDSEGMENT); +} + +static PARCCryptoHash * +_getKeyIdRestriction(CCNxTlvDictionary *messageDictionary) +{ + return (PARCCryptoHash *) ccnxTlvDictionary_GetObject(messageDictionary, + CCNxCodecSchemaV1TlvDictionary_MessageFastArray_KEYID_RESTRICTION); +} + +static PARCCryptoHash * +_getHashRestriction(CCNxTlvDictionary *messageDictionary) +{ + return (PARCCryptoHash *) ccnxTlvDictionary_GetObject(messageDictionary, + CCNxCodecSchemaV1TlvDictionary_MessageFastArray_OBJHASH_RESTRICTION); +} + + +// ========================================================================= + +LONGBOW_TEST_FIXTURE(ContentObject) +{ + LONGBOW_RUN_TEST_CASE(ContentObject, Name); + LONGBOW_RUN_TEST_CASE(ContentObject, Payload); + LONGBOW_RUN_TEST_CASE(ContentObject, PayloadType); + LONGBOW_RUN_TEST_CASE(ContentObject, ExpiryTime); + LONGBOW_RUN_TEST_CASE(ContentObject, EndChunkNumber); +} + +LONGBOW_TEST_FIXTURE_SETUP(ContentObject) +{ + longBowTestCase_SetClipBoardData(testCase, + commonSetup(v1_content_nameA_keyid1_rsasha256, + sizeof(v1_content_nameA_keyid1_rsasha256), + v1_content_nameA_keyid1_rsasha256_truthTableEntries, + V1_MANIFEST_OBJ_CONTENTOBJECT)); + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(ContentObject) +{ + commonTeardown(longBowTestCase_GetClipBoardData(testCase)); + + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(ContentObject, Name) +{ +// TestData *data = longBowTestCase_GetClipBoardData(testCase); +// testNameGetter(data, V1_MANIFEST_OBJ_NAME, ccnxCodecSchemaV1MessageDecoder_Decode, _getName); +} + +LONGBOW_TEST_CASE(ContentObject, Payload) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + testBufferGetter(data, V1_MANIFEST_OBJ_PAYLOAD, ccnxCodecSchemaV1MessageDecoder_Decode, _getPayload); +} + +LONGBOW_TEST_CASE(ContentObject, PayloadType) +{ + // The payload type is translated from the wire format value to the CCNxPayloadType value, + // so this test cannot use the automated framework as the value in the dictionary will not + // be the same as the value in the wire format + + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + bool decodeSuccess = ccnxCodecSchemaV1MessageDecoder_Decode(data->decoder, data->dictionary); + assertTrue(decodeSuccess, "failure on ccnxCodecSchemaV1MessageDecoder_Decode"); + + CCNxPayloadType testPayloadType = (CCNxPayloadType) _getPayloadType(data->dictionary); + + // look up the true name buffer from the truth table + TlvExtent extent = getTruthTableExtent(data->truthTable, V1_MANIFEST_OBJ_PAYLOADTYPE); + + PARCBuffer + *truthbuffer = parcBuffer_Wrap(data->packet, data->packetLength, extent.offset, extent.offset + extent.length); + uint64_t truthvalue = -2; + ccnxCodecTlvUtilities_GetVarInt(truthbuffer, parcBuffer_Remaining(truthbuffer), &truthvalue); + + CCNxPayloadType truthPayloadType = -1; + bool success = + _translateWirePayloadTypeToCCNxPayloadType((CCNxCodecSchemaV1Types_PayloadType) truthvalue, &truthPayloadType); + assertTrue(success, "failure in _translateWirePayloadTypeToCCNxPayloadType for wireFormatValue %" + PRId64, truthvalue); + + parcBuffer_Release(&truthbuffer); + + assertTrue(truthPayloadType == testPayloadType, "Wrong value, got %d expected %d", testPayloadType, + truthPayloadType); +} + +LONGBOW_TEST_CASE(ContentObject, ExpiryTime) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + testInt64Getter(data, V1_MANIFEST_OBJ_EXPIRY_TIME, ccnxCodecSchemaV1MessageDecoder_Decode, _getExpiryTime); +} + +LONGBOW_TEST_CASE(ContentObject, EndChunkNumber) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + testInt64Getter(data, V1_MANIFEST_OBJ_ENDSEGMENT, ccnxCodecSchemaV1MessageDecoder_Decode, _getEndChunkNumber); +} + +// ========================================================================= + +LONGBOW_TEST_FIXTURE(Interest) +{ + LONGBOW_RUN_TEST_CASE(Interest, Name); + LONGBOW_RUN_TEST_CASE(Interest, Payload); + LONGBOW_RUN_TEST_CASE(Interest, KeyIdRestriction); + LONGBOW_RUN_TEST_CASE(Interest, HashRestriction); +} + +LONGBOW_TEST_FIXTURE_SETUP(Interest) +{ + longBowTestCase_SetClipBoardData(testCase, + commonSetup(v1_interest_all_fields, + sizeof(v1_interest_all_fields), + TRUTHTABLENAME(v1_interest_all_fields), + V1_MANIFEST_INT_INTEREST)); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Interest) +{ + commonTeardown(longBowTestCase_GetClipBoardData(testCase)); + + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Interest, Name) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + testNameGetter(data, V1_MANIFEST_INT_NAME, ccnxCodecSchemaV1MessageDecoder_Decode, _getName); +} + +LONGBOW_TEST_CASE(Interest, Payload) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + testBufferGetter(data, V1_MANIFEST_INT_PAYLOAD, ccnxCodecSchemaV1MessageDecoder_Decode, _getPayload); +} + +LONGBOW_TEST_CASE(Interest, KeyIdRestriction) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + testHashGetter(data, V1_MANIFEST_INT_KEYID, ccnxCodecSchemaV1MessageDecoder_Decode, _getKeyIdRestriction); +} + +LONGBOW_TEST_CASE(Interest, HashRestriction) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + testHashGetter(data, V1_MANIFEST_INT_OBJHASH, ccnxCodecSchemaV1MessageDecoder_Decode, _getHashRestriction); +} + + +// ========================================================================= + +LONGBOW_TEST_FIXTURE(Local) +{ + LONGBOW_RUN_TEST_CASE(Local, _translateWirePayloadTypeToCCNxPayloadType); +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Local, _translateWirePayloadTypeToCCNxPayloadType) +{ + uint32_t sentinel = 0xFFFF; + + struct { + CCNxCodecSchemaV1Types_PayloadType wire; + CCNxPayloadType payloadType; + bool success; + } vectors[] = { + { .wire = CCNxCodecSchemaV1Types_PayloadType_Data, .payloadType = CCNxPayloadType_DATA, .success = true }, + { .wire = CCNxCodecSchemaV1Types_PayloadType_Key, .payloadType = CCNxPayloadType_KEY, .success = true }, + { .wire = CCNxCodecSchemaV1Types_PayloadType_Link, .payloadType = CCNxPayloadType_LINK, .success = true }, + { .wire = -2, .payloadType = -2, .success = false }, + { .wire = sentinel, .payloadType = sentinel } + }; + + for (int i = 0; vectors[i].wire != sentinel; i++) { + CCNxPayloadType payloadType = -1; + bool success = _translateWirePayloadTypeToCCNxPayloadType(vectors[i].wire, &payloadType); + assertTrue(success == vectors[i].success, "Incorrect return index %d, expected %d got %d", i, + vectors[i].success, success); + + if (success) { + assertTrue(payloadType == vectors[i].payloadType, "Wrong payloadType index %d, expected %d got %d", i, + vectors[i].payloadType, payloadType); + } + } +} + +// ========================================================================= + + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(ccnxCodecSchemaV1_MessageDecoder); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-common/ccnx/common/codec/schema_v1/test/test_ccnxCodecSchemaV1_MessageEncoder.c b/libccnx-common/ccnx/common/codec/schema_v1/test/test_ccnxCodecSchemaV1_MessageEncoder.c new file mode 100644 index 00000000..49452626 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/test/test_ccnxCodecSchemaV1_MessageEncoder.c @@ -0,0 +1,644 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../ccnxCodecSchemaV1_MessageEncoder.c" +#include <parc/algol/parc_SafeMemory.h> +#include <LongBow/unit-test.h> +#include <ccnx/common/codec/schema_v1/testdata/v1_content_nameA_keyid1_rsasha256.h> +#include <ccnx/common/codec/schema_v1/testdata/v1_interest_all_fields.h> +#include <ccnx/common/codec/schema_v1/testdata/v1_content_nameless_nosig.h> + +#include "testrig_encoder.c" + +#include <ccnx/common/ccnx_ContentObject.h> +#include <ccnx/common/ccnx_Interest.h> + +#include <ccnx/common/internal/ccnx_InterestDefault.h> + +// ========================================================================= + +LONGBOW_TEST_RUNNER(ccnxCodecSchemaV1_MessageEncoder) +{ + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); + LONGBOW_RUN_TEST_FIXTURE(UnknownType); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(ccnxCodecSchemaV1_MessageEncoder) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(ccnxCodecSchemaV1_MessageEncoder) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + + +// ========================================================================= + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(ContentObject, Interest); + LONGBOW_RUN_TEST_CASE(ContentObject, ContentObject); + LONGBOW_RUN_TEST_CASE(ContentObject, ContentObjectNameless); + LONGBOW_RUN_TEST_CASE(InterestReturn, InterestReturn); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(ContentObject, Interest) +{ + // create an Interest that replicates v1_interest_all_fields + TlvExtent keyidExtent = getTruthTableExtent(TRUTHTABLENAME(v1_interest_all_fields), V1_MANIFEST_INT_KEYID); + TlvExtent hashExtent = getTruthTableExtent(TRUTHTABLENAME(v1_interest_all_fields), V1_MANIFEST_INT_OBJHASH); + TlvExtent payloadExtent = getTruthTableExtent(TRUTHTABLENAME(v1_interest_all_fields), V1_MANIFEST_INT_PAYLOAD); + TlvExtent interestExtent = getTruthTableExtent(TRUTHTABLENAME(v1_interest_all_fields), V1_MANIFEST_INT_INTEREST); + + CCNxName *name = ccnxName_CreateFromCString("lci:/3=cool"); + uint32_t lifetime = CCNxInterestDefault_LifetimeMilliseconds; + uint32_t hoplimit = CCNxInterestDefault_HopLimit; + + printf("%d %d\n", keyidExtent.offset + 4, keyidExtent.offset + keyidExtent.length - 4); + PARCBuffer *keyid = parcBuffer_Wrap(v1_interest_all_fields, sizeof(v1_interest_all_fields), keyidExtent.offset + 4, keyidExtent.offset + keyidExtent.length); + PARCBuffer *hash = parcBuffer_Wrap(v1_interest_all_fields, sizeof(v1_interest_all_fields), hashExtent.offset + 4, hashExtent.offset + hashExtent.length); + PARCBuffer *payload = parcBuffer_Wrap(v1_interest_all_fields, sizeof(v1_interest_all_fields), payloadExtent.offset, payloadExtent.offset + payloadExtent.length); + + CCNxTlvDictionary *interest = + ccnxInterest_CreateWithImpl(&CCNxInterestFacadeV1_Implementation, + name, lifetime, keyid, hash, hoplimit); + + ccnxInterest_SetPayloadAndId(interest, payload); + + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + ccnxCodecSchemaV1MessageEncoder_Encode(encoder, interest); + + PARCBuffer *truth = parcBuffer_Wrap(v1_interest_all_fields, + sizeof(v1_interest_all_fields), + interestExtent.offset, + interestExtent.offset + interestExtent.length); + + testCompareEncoderToBuffer(encoder, truth); + + ccnxCodecTlvEncoder_Destroy(&encoder); + parcBuffer_Release(&truth); + parcBuffer_Release(&payload); + parcBuffer_Release(&hash); + parcBuffer_Release(&keyid); + ccnxName_Release(&name); + ccnxTlvDictionary_Release(&interest); +} + +LONGBOW_TEST_CASE(InterestReturn, InterestReturn) +{ + // create an Interest that replicates v1_interest_all_fields + TlvExtent keyidExtent = getTruthTableExtent(TRUTHTABLENAME(v1_interest_all_fields), V1_MANIFEST_INT_KEYID); + TlvExtent hashExtent = getTruthTableExtent(TRUTHTABLENAME(v1_interest_all_fields), V1_MANIFEST_INT_OBJHASH); + TlvExtent payloadExtent = getTruthTableExtent(TRUTHTABLENAME(v1_interest_all_fields), V1_MANIFEST_INT_PAYLOAD); + TlvExtent interestExtent = getTruthTableExtent(TRUTHTABLENAME(v1_interest_all_fields), V1_MANIFEST_INT_INTEREST); + + CCNxName *name = ccnxName_CreateFromCString("lci:/3=cool"); + uint32_t lifetime = CCNxInterestDefault_LifetimeMilliseconds; + uint32_t hoplimit = CCNxInterestDefault_HopLimit; + + PARCBuffer *keyid = parcBuffer_Wrap(v1_interest_all_fields, sizeof(v1_interest_all_fields), keyidExtent.offset + 4, keyidExtent.offset + keyidExtent.length); + PARCBuffer *hash = parcBuffer_Wrap(v1_interest_all_fields, sizeof(v1_interest_all_fields), hashExtent.offset + 4, hashExtent.offset + hashExtent.length); + PARCBuffer *payload = parcBuffer_Wrap(v1_interest_all_fields, sizeof(v1_interest_all_fields), payloadExtent.offset, payloadExtent.offset + payloadExtent.length); + + CCNxTlvDictionary *interest = + ccnxInterest_CreateWithImpl(&CCNxInterestFacadeV1_Implementation, + name, lifetime, keyid, hash, hoplimit); + parcBuffer_Release(&hash); + parcBuffer_Release(&keyid); + ccnxName_Release(&name); + + ccnxInterest_SetPayloadAndId(interest, payload); + parcBuffer_Release(&payload); + + CCNxInterestReturn *interestReturn = + ccnxInterestReturn_Create(interest, CCNxInterestReturn_ReturnCode_HopLimitExceeded); + ccnxTlvDictionary_Release(&interest); + + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + ccnxCodecSchemaV1MessageEncoder_Encode(encoder, interestReturn); + + size_t packetSize = sizeof(v1_interest_all_fields); + uint8_t *testPacket = (uint8_t *) parcSafeMemory_Allocate(packetSize); + memcpy(testPacket, v1_interest_all_fields, packetSize); + testPacket[1] = 0x02; //InterestReturn + testPacket[5] = CCNxInterestReturn_ReturnCode_HopLimitExceeded; + + PARCBuffer *truth = parcBuffer_Wrap(testPacket, packetSize, + interestExtent.offset, + interestExtent.offset + interestExtent.length); + + testCompareEncoderToBuffer(encoder, truth); + + parcSafeMemory_Deallocate((void **) &testPacket); + ccnxCodecTlvEncoder_Destroy(&encoder); + parcBuffer_Release(&truth); + ccnxInterestReturn_Release(&interestReturn); +} + +LONGBOW_TEST_CASE(ContentObject, ContentObjectNameless) +{ + // create a Content Object that replicates v1_content_nameA_keyid1_rsasha256 + TlvExtent payloadExtent = getTruthTableExtent(TRUTHTABLENAME(v1_content_nameless_nosig), V1_MANIFEST_OBJ_PAYLOAD); + TlvExtent contentObjectExtent = getTruthTableExtent(TRUTHTABLENAME(v1_content_nameless_nosig), V1_MANIFEST_OBJ_CONTENTOBJECT); + + PARCBuffer *payload = parcBuffer_Wrap(v1_content_nameless_nosig, sizeof(v1_content_nameless_nosig), payloadExtent.offset, payloadExtent.offset + payloadExtent.length); + + CCNxTlvDictionary *contentobject = + ccnxContentObject_CreateWithImplAndPayload(&CCNxContentObjectFacadeV1_Implementation, + NULL, CCNxPayloadType_KEY, payload); + + ccnxContentObject_SetExpiryTime(contentobject, 0x01434B198400ULL); + ccnxContentObject_SetFinalChunkNumber(contentobject, 0x06050403); + + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + ccnxCodecSchemaV1MessageEncoder_Encode(encoder, contentobject); + + PARCBuffer *truth = parcBuffer_Wrap(v1_content_nameless_nosig, sizeof(v1_content_nameless_nosig), contentObjectExtent.offset, contentObjectExtent.offset + contentObjectExtent.length); + + testCompareEncoderToBuffer(encoder, truth); + + ccnxCodecTlvEncoder_Destroy(&encoder); + parcBuffer_Release(&truth); + parcBuffer_Release(&payload); + ccnxTlvDictionary_Release(&contentobject); +} + +LONGBOW_TEST_CASE(ContentObject, ContentObject) +{ + // create a Content Object that replicates v1_content_nameA_keyid1_rsasha256 + TlvExtent payloadExtent = getTruthTableExtent(TRUTHTABLENAME(v1_content_nameA_keyid1_rsasha256), V1_MANIFEST_OBJ_PAYLOAD); + TlvExtent contentObjectExtent = getTruthTableExtent(TRUTHTABLENAME(v1_content_nameA_keyid1_rsasha256), V1_MANIFEST_OBJ_CONTENTOBJECT); + + CCNxName *name = ccnxName_CreateFromCString("lci:/3=hello/0xf000=ouch"); + + PARCBuffer *payload = parcBuffer_Wrap(v1_content_nameA_keyid1_rsasha256, sizeof(v1_content_nameA_keyid1_rsasha256), payloadExtent.offset, payloadExtent.offset + payloadExtent.length); + + CCNxTlvDictionary *contentobject = + ccnxContentObject_CreateWithImplAndPayload(&CCNxContentObjectFacadeV1_Implementation, + name, CCNxPayloadType_KEY, payload); + + ccnxContentObject_SetExpiryTime(contentobject, 0x01434B198400ULL); + ccnxContentObject_SetFinalChunkNumber(contentobject, 0x06050403); + + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + ccnxCodecSchemaV1MessageEncoder_Encode(encoder, contentobject); + + PARCBuffer *truth = parcBuffer_Wrap(v1_content_nameA_keyid1_rsasha256, sizeof(v1_content_nameA_keyid1_rsasha256), contentObjectExtent.offset, contentObjectExtent.offset + contentObjectExtent.length); + + testCompareEncoderToBuffer(encoder, truth); + + ccnxCodecTlvEncoder_Destroy(&encoder); + parcBuffer_Release(&truth); + parcBuffer_Release(&payload); + ccnxName_Release(&name); + ccnxTlvDictionary_Release(&contentobject); +} + + +// ========================================================================= + +LONGBOW_TEST_FIXTURE(Interest) +{ + LONGBOW_RUN_TEST_CASE(Interest, ccnxCodecSchemaV1MessageEncoder_Encode); +} + +LONGBOW_TEST_FIXTURE_SETUP(Interest) +{ + longBowTestCase_SetClipBoardData(testCase, + commonSetup(v1_interest_all_fields, + sizeof(v1_interest_all_fields), + TRUTHTABLENAME(v1_interest_all_fields), + V1_MANIFEST_INT_INTEREST)); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Interest) +{ + testrigencoder_CommonTeardown(longBowTestCase_GetClipBoardData(testCase)); + + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Interest, ccnxCodecSchemaV1MessageEncoder_Encode) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + ccnxCodecSchemaV1MessageEncoder_Encode(data->encoder, data->dictionary); + + testCompareEncoderToBuffer(data->encoder, data->memoryRegion); +} + +// ========================================================================= + +LONGBOW_TEST_FIXTURE(InterestReturn) +{ + LONGBOW_RUN_TEST_CASE(InterestReturn, ccnxCodecSchemaV1MessageEncoder_Encode); +} + +LONGBOW_TEST_FIXTURE_SETUP(InterestReturn) +{ + size_t packetSize = sizeof(v1_interest_all_fields); + uint8_t *testPacket = (uint8_t *) parcSafeMemory_Allocate(packetSize); + memcpy(testPacket, v1_interest_all_fields, packetSize); + testPacket[1] = 0x02; //InterestReturn + testPacket[5] = CCNxInterestReturn_ReturnCode_HopLimitExceeded; + + longBowTestCase_SetClipBoardData(testCase, + commonSetup(v1_interest_all_fields, + sizeof(v1_interest_all_fields), + TRUTHTABLENAME(v1_interest_all_fields), + V1_MANIFEST_INT_INTEREST)); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(InterestReturn) +{ + testrigencoder_CommonTeardown(longBowTestCase_GetClipBoardData(testCase)); + + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(InterestReturn, ccnxCodecSchemaV1MessageEncoder_Encode) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + ccnxCodecSchemaV1MessageEncoder_Encode(data->encoder, data->dictionary); + + testCompareEncoderToBuffer(data->encoder, data->memoryRegion); +} + +// ========================================================================= + +LONGBOW_TEST_FIXTURE(Local) +{ + LONGBOW_RUN_TEST_CASE(Local, _encodeName); + LONGBOW_RUN_TEST_CASE(Local, _encodePayload); + LONGBOW_RUN_TEST_CASE(Local, _encodePayloadType); + LONGBOW_RUN_TEST_CASE(Local, _encodeExpiryTime); + LONGBOW_RUN_TEST_CASE(Local, _encodeEndChunkNumber); + LONGBOW_RUN_TEST_CASE(Local, _encodeKeyIdRestriction); + LONGBOW_RUN_TEST_CASE(Local, _encodeContentObjectHashRestriction); +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_TlvDictionary.h> +#include <parc/algol/parc_Varint.h> + +LONGBOW_TEST_CASE(Local, _encodeName) +{ + CCNxName *name = ccnxName_CreateFromCString("lci:/0xf001=foot/0xf002=toe/0xf003=nail"); + uint8_t encoded[] = { + 0x00, 0x00, 0x00, 23, + 0xF0, 0x01, 0x00, 4, + 'f', 'o', 'o', 't', + 0xF0, 0x02, 0x00, 3, + 't', 'o', 'e', + 0xF0, 0x03, 0x00, 4, + 'n', 'a', 'i', 'l' + }; + + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + + ccnxTlvDictionary_PutName(dictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_NAME, name); + + _encodeName(encoder, dictionary); + + ccnxCodecTlvEncoder_Finalize(encoder); + PARCBuffer *test = ccnxCodecTlvEncoder_CreateBuffer(encoder); + PARCBuffer *truth = parcBuffer_Wrap(encoded, sizeof(encoded), 0, sizeof(encoded)); + assertTrue(parcBuffer_Equals(truth, test), "buffers do not match") + { + printf("Expected:\n"); + parcBuffer_Display(truth, 3); + printf("Got:\n"); + parcBuffer_Display(test, 3); + } + + parcBuffer_Release(&truth); + parcBuffer_Release(&test); + ccnxName_Release(&name); + ccnxCodecTlvEncoder_Destroy(&encoder); + ccnxTlvDictionary_Release(&dictionary); +} + +LONGBOW_TEST_CASE(Local, _encodePayload) +{ + uint8_t payload[] = { 0xf1, 0xf2, 0xf3 }; + uint8_t encoded[] = { + 0x00, 0x01, 0x00, 3, + 0xf1, 0xf2, 0xf3 + }; + + PARCBuffer *buffer = parcBuffer_Wrap(payload, sizeof(payload), 0, sizeof(payload)); + + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + + ccnxTlvDictionary_PutBuffer(dictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_PAYLOAD, buffer); + + _encodePayload(encoder, dictionary); + + PARCBuffer *truth = parcBuffer_Wrap(encoded, sizeof(encoded), 0, sizeof(encoded)); + ccnxCodecTlvEncoder_Finalize(encoder); + PARCBuffer *test = ccnxCodecTlvEncoder_CreateBuffer(encoder); + assertTrue(parcBuffer_Equals(truth, test), "buffers do not match") + { + printf("Expected:\n"); + parcBuffer_Display(truth, 3); + printf("Got:\n"); + parcBuffer_Display(test, 3); + } + + parcBuffer_Release(&truth); + parcBuffer_Release(&test); + parcBuffer_Release(&buffer); + ccnxCodecTlvEncoder_Destroy(&encoder); + ccnxTlvDictionary_Release(&dictionary); +} + +LONGBOW_TEST_CASE(Local, _encodePayloadType) +{ + CCNxPayloadType type = CCNxPayloadType_LINK; + + uint8_t encoded[] = { + 0x00, 0x05, 0x00, 1, + CCNxCodecSchemaV1Types_PayloadType_Link + }; + + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + + ccnxTlvDictionary_PutInteger(dictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_PAYLOADTYPE, type); + + _encodePayloadType(encoder, dictionary); + + PARCBuffer *truth = parcBuffer_Wrap(encoded, sizeof(encoded), 0, sizeof(encoded)); + ccnxCodecTlvEncoder_Finalize(encoder); + PARCBuffer *test = ccnxCodecTlvEncoder_CreateBuffer(encoder); + assertTrue(parcBuffer_Equals(truth, test), "buffers do not match") + { + printf("Expected:\n"); + parcBuffer_Display(truth, 3); + printf("Got:\n"); + parcBuffer_Display(test, 3); + } + + parcBuffer_Release(&truth); + parcBuffer_Release(&test); + ccnxCodecTlvEncoder_Destroy(&encoder); + ccnxTlvDictionary_Release(&dictionary); +} + +LONGBOW_TEST_CASE(Local, _encodeExpiryTime) +{ + uint64_t expiry = 0x123456789ABCDEF0ULL; + uint8_t encoded[] = { + 0x00, 0x06, 0x00, 8, + 0x12, 0x34, 0x56, 0x78, + 0x9A, 0xBC, 0xDE, 0xF0 + }; + + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + + ccnxTlvDictionary_PutInteger(dictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_EXPIRY_TIME, expiry); + + _encodeExpiryTime(encoder, dictionary); + + PARCBuffer *truth = parcBuffer_Wrap(encoded, sizeof(encoded), 0, sizeof(encoded)); + ccnxCodecTlvEncoder_Finalize(encoder); + PARCBuffer *test = ccnxCodecTlvEncoder_CreateBuffer(encoder); + assertTrue(parcBuffer_Equals(truth, test), "buffers do not match") + { + printf("Expected:\n"); + parcBuffer_Display(truth, 3); + printf("Got:\n"); + parcBuffer_Display(test, 3); + } + + parcBuffer_Release(&truth); + parcBuffer_Release(&test); + ccnxCodecTlvEncoder_Destroy(&encoder); + ccnxTlvDictionary_Release(&dictionary); +} + +LONGBOW_TEST_CASE(Local, _encodeEndChunkNumber) +{ + uint64_t endChunkNumber = 0x818283ULL; + uint8_t encoded[] = { + 0x00, 0x19, 0x00, 3, + 0x81, 0x82, 0x83 + }; + + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + + ccnxTlvDictionary_PutInteger(dictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_ENDSEGMENT, endChunkNumber); + + _encodeEndChunkNumber(encoder, dictionary); + + PARCBuffer *truth = parcBuffer_Wrap(encoded, sizeof(encoded), 0, sizeof(encoded)); + ccnxCodecTlvEncoder_Finalize(encoder); + PARCBuffer *test = ccnxCodecTlvEncoder_CreateBuffer(encoder); + assertTrue(parcBuffer_Equals(truth, test), "buffers do not match") + { + printf("Expected:\n"); + parcBuffer_Display(truth, 3); + printf("Got:\n"); + parcBuffer_Display(test, 3); + } + + parcBuffer_Release(&truth); + parcBuffer_Release(&test); + ccnxCodecTlvEncoder_Destroy(&encoder); + ccnxTlvDictionary_Release(&dictionary); +} + +LONGBOW_TEST_CASE(Local, _encodeKeyIdRestriction) +{ + uint8_t encoded[] = { + 0x00, 0x02, 0x00, 0x24, + 0x00, 0x01, 0x00, 0x20, + 0x12, 0x34, 0x56, 0x78,0x9A, 0xBC, 0xDE, 0xF0, + 0x12, 0x34, 0x56, 0x78,0x9A, 0xBC, 0xDE, 0xF0, + 0x12, 0x34, 0x56, 0x78,0x9A, 0xBC, 0xDE, 0xF0, + 0x12, 0x34, 0x56, 0x78,0x9A, 0xBC, 0xDE, 0xF0, + }; + + PARCBuffer *buffer = parcBuffer_Wrap(encoded, sizeof(encoded), 8, sizeof(encoded)); + PARCCryptoHash *hash = parcCryptoHash_Create(PARCCryptoHashType_SHA256, buffer); + + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + + ccnxTlvDictionary_PutObject(dictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_KEYID_RESTRICTION, hash); + + _encodeKeyIdRestriction(encoder, dictionary); + + PARCBuffer *truth = parcBuffer_Wrap(encoded, sizeof(encoded), 0, sizeof(encoded)); + ccnxCodecTlvEncoder_Finalize(encoder); + PARCBuffer *test = ccnxCodecTlvEncoder_CreateBuffer(encoder); + assertTrue(parcBuffer_Equals(truth, test), "buffers do not match") + { + printf("Expected:\n"); + parcBuffer_Display(truth, 3); + printf("Got:\n"); + parcBuffer_Display(test, 3); + } + + parcBuffer_Release(&truth); + parcBuffer_Release(&test); + parcBuffer_Release(&buffer); + ccnxCodecTlvEncoder_Destroy(&encoder); + ccnxTlvDictionary_Release(&dictionary); + parcCryptoHash_Release(&hash); +} + +LONGBOW_TEST_CASE(Local, _encodeContentObjectHashRestriction) +{ + uint8_t encoded[] = { + 0x00, 0x03, 0x00, 0x24, + 0x00, 0x01, 0x00, 0x20, + 0x12, 0x34, 0x56, 0x78,0x9A, 0xBC, 0xDE, 0xF0, + 0x12, 0x34, 0x56, 0x78,0x9A, 0xBC, 0xDE, 0xF0, + 0x12, 0x34, 0x56, 0x78,0x9A, 0xBC, 0xDE, 0xF0, + 0x12, 0x34, 0x56, 0x78,0x9A, 0xBC, 0xDE, 0xF0, + }; + + PARCBuffer *buffer = parcBuffer_Wrap(encoded, sizeof(encoded), 8, sizeof(encoded)); + PARCCryptoHash *hash = parcCryptoHash_Create(PARCCryptoHashType_SHA256, buffer); + + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + + ccnxTlvDictionary_PutObject(dictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_OBJHASH_RESTRICTION, hash); + + _encodeContentObjectHashRestriction(encoder, dictionary); + + PARCBuffer *truth = parcBuffer_Wrap(encoded, sizeof(encoded), 0, sizeof(encoded)); + ccnxCodecTlvEncoder_Finalize(encoder); + PARCBuffer *test = ccnxCodecTlvEncoder_CreateBuffer(encoder); + assertTrue(parcBuffer_Equals(truth, test), "buffers do not match") + { + printf("Expected:\n"); + parcBuffer_Display(truth, 3); + printf("Got:\n"); + parcBuffer_Display(test, 3); + } + + parcBuffer_Release(&truth); + parcBuffer_Release(&test); + parcBuffer_Release(&buffer); + ccnxCodecTlvEncoder_Destroy(&encoder); + ccnxTlvDictionary_Release(&dictionary); + parcCryptoHash_Release(&hash); +} + + +// ========================================================================= + +LONGBOW_TEST_FIXTURE(UnknownType) +{ + LONGBOW_RUN_TEST_CASE(UnknownType, Unknown); +} + +LONGBOW_TEST_FIXTURE_SETUP(UnknownType) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(UnknownType) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(UnknownType, Unknown) +{ + CCNxTlvDictionary *unknown = ccnxTlvDictionary_Create(CCNxCodecSchemaV1TlvDictionary_MessageFastArray_END, 1); + + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + ssize_t length = ccnxCodecSchemaV1MessageEncoder_Encode(encoder, unknown); + assertTrue(length < 0, "Did not get error return when encoding unknown type"); + + CCNxCodecError *error = ccnxCodecTlvEncoder_GetError(encoder); + assertNotNull(error, "encoder did not set the error"); + + ccnxCodecTlvEncoder_Destroy(&encoder); + ccnxTlvDictionary_Release(&unknown); +} + +// ================================================================================== + + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(ccnxCodecSchemaV1_MessageEncoder); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-common/ccnx/common/codec/schema_v1/test/test_ccnxCodecSchemaV1_NameCodec.c b/libccnx-common/ccnx/common/codec/schema_v1/test/test_ccnxCodecSchemaV1_NameCodec.c new file mode 100755 index 00000000..8a33a0e2 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/test/test_ccnxCodecSchemaV1_NameCodec.c @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../ccnxCodecSchemaV1_NameCodec.c" +#include <parc/algol/parc_SafeMemory.h> +#include <LongBow/unit-test.h> + +LONGBOW_TEST_RUNNER(ccnxTlvCodec_Name) +{ + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(ccnxTlvCodec_Name) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(ccnxTlvCodec_Name) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, ccnxTlvCodecName_Decode_RightType); + LONGBOW_RUN_TEST_CASE(Global, ccnxTlvCodecName_Decode_WrongType); + LONGBOW_RUN_TEST_CASE(Global, ccnxTlvCodecName_Encode); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + if (parcSafeMemory_ReportAllocation(STDOUT_FILENO) != 0) { + printf("('%s' leaks memory by %d (allocs - frees)) ", longBowTestCase_GetName(testCase), parcMemory_Outstanding()); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, ccnxTlvCodecName_Decode_RightType) +{ + PARCBuffer *buffer = parcBuffer_WrapCString("brandywine"); + CCNxNameSegment *segment = ccnxNameSegment_CreateTypeValue(CCNxNameLabelType_NAME, buffer); + CCNxName *truth = ccnxName_Append(ccnxName_Create(), segment); + ccnxNameSegment_Release(&segment); + parcBuffer_Release(&buffer); + + uint8_t decodeBytes[] = { 0x10, 0x20, 0x00, 0x0E, 0x00, CCNxNameLabelType_NAME, 0x00, 0x0A, 'b', 'r', 'a', 'n', 'd', 'y', 'w', 'i', 'n', 'e' }; + PARCBuffer *decodeBuffer = parcBuffer_Wrap(decodeBytes, sizeof(decodeBytes), 0, sizeof(decodeBytes)); + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(decodeBuffer); + CCNxName *test = ccnxCodecSchemaV1NameCodec_Decode(decoder, 0x1020); + + assertTrue(ccnxName_Equals(truth, test), "Name segments do not match"); + + ccnxName_Release(&test); + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&decodeBuffer); + ccnxName_Release(&truth); +} + +LONGBOW_TEST_CASE(Global, ccnxTlvCodecName_Decode_WrongType) +{ + uint8_t decodeBytes[] = { 0x10, 0x20, 0x00, 0x0E, 0x00, CCNxNameLabelType_NAME, 0x00, 0x0A, 'b', 'r', 'a', 'n', 'd', 'y', 'w', 'i', 'n', 'e' }; + PARCBuffer *decodeBuffer = parcBuffer_Wrap(decodeBytes, sizeof(decodeBytes), 0, sizeof(decodeBytes)); + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(decodeBuffer); + CCNxName *test = ccnxCodecSchemaV1NameCodec_Decode(decoder, 0xFFFF); + + assertNull(test, "Name should have returned NULL because the name type does not match"); + assertTrue(ccnxCodecTlvDecoder_Position(decoder) == 0, "Position should not have moved, expected 0, got %zu", ccnxCodecTlvDecoder_Position(decoder)); + + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&decodeBuffer); +} + +LONGBOW_TEST_CASE(Global, ccnxTlvCodecName_Encode) +{ + uint8_t truthBytes[] = { 0x10, 0x20, 0x00, 0x0E, 0x00, CCNxNameLabelType_NAME, 0x00, 0x0A, 'b', 'r', 'a', 'n', 'd', 'y', 'w', 'i', 'n', 'e' }; + PARCBuffer *truth = parcBuffer_Wrap(truthBytes, sizeof(truthBytes), 0, sizeof(truthBytes)); + + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + ccnxCodecTlvEncoder_Initialize(encoder); + + PARCBuffer *buffer = parcBuffer_WrapCString("brandywine"); + CCNxNameSegment *segment = ccnxNameSegment_CreateTypeValue(CCNxNameLabelType_NAME, buffer); + + CCNxName *name = ccnxName_Append(ccnxName_Create(), segment); + ccnxNameSegment_Release(&segment); + parcBuffer_Release(&buffer); + + ccnxCodecSchemaV1NameCodec_Encode(encoder, 0x1020, name); + + ccnxCodecTlvEncoder_Finalize(encoder); + PARCBuffer *test = ccnxCodecTlvEncoder_CreateBuffer(encoder); + + if (!parcBuffer_Equals(truth, test)) { + printf("Buffers do not match\n"); + printf("Excpected:\n"); + parcBuffer_Display(truth, 3); + printf("Got:\n"); + parcBuffer_Display(test, 3); + assertTrue(parcBuffer_Equals(truth, test), "Buffers do not match"); + } + + ccnxName_Release(&name); + parcBuffer_Release(&test); + ccnxCodecTlvEncoder_Destroy(&encoder); + parcBuffer_Release(&truth); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(ccnxTlvCodec_Name); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-common/ccnx/common/codec/schema_v1/test/test_ccnxCodecSchemaV1_NameSegmentCodec.c b/libccnx-common/ccnx/common/codec/schema_v1/test/test_ccnxCodecSchemaV1_NameSegmentCodec.c new file mode 100755 index 00000000..77800055 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/test/test_ccnxCodecSchemaV1_NameSegmentCodec.c @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../ccnxCodecSchemaV1_NameSegmentCodec.c" +#include <parc/algol/parc_SafeMemory.h> +#include <LongBow/unit-test.h> + +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_NameCodec.h> + +LONGBOW_TEST_RUNNER(ccnxTlvCodec_Name) +{ + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(ccnxTlvCodec_Name) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(ccnxTlvCodec_Name) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, ccnxTlvCodecNameSegment_Decode); + LONGBOW_RUN_TEST_CASE(Global, ccnxTlvCodecNameSegment_Decode_TLShort); + LONGBOW_RUN_TEST_CASE(Global, ccnxTlvCodecNameSegment_Decode_VShort); + LONGBOW_RUN_TEST_CASE(Global, ccnxTlvCodecNameSegment_Decode); + LONGBOW_RUN_TEST_CASE(Global, ccnxTlvCodecNameSegment_Encode); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + if (parcSafeMemory_ReportAllocation(STDOUT_FILENO) != 0) { + printf("('%s' leaks memory by %d (allocs - frees)) ", longBowTestCase_GetName(testCase), parcMemory_Outstanding()); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, ccnxTlvCodecNameSegment_Decode) +{ + PARCBuffer *buffer = parcBuffer_WrapCString("brandywine"); + CCNxNameSegment *truth = ccnxNameSegment_CreateTypeValue(CCNxNameLabelType_NAME, buffer); + parcBuffer_Release(&buffer); + + uint8_t decodeBytes[] = { 0x00, CCNxNameLabelType_NAME, 0x00, 0x0A, 'b', 'r', 'a', 'n', 'd', 'y', 'w', 'i', 'n', 'e' }; + PARCBuffer *decodeBuffer = parcBuffer_Wrap(decodeBytes, sizeof(decodeBytes), 0, sizeof(decodeBytes)); + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(decodeBuffer); + CCNxNameSegment *test = ccnxCodecSchemaV1NameSegmentCodec_Decode(decoder); + + assertTrue(ccnxNameSegment_Equals(truth, test), "Name segments do not match"); + + ccnxNameSegment_Release(&test); + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&decodeBuffer); + ccnxNameSegment_Release(&truth); +} + +/** + * In this case, there are not enough bytes in the buffer to decode the T and L + */ +LONGBOW_TEST_CASE(Global, ccnxTlvCodecNameSegment_Decode_TLShort) +{ + uint8_t decodeBytes[] = { 0x00, CCNxNameLabelType_NAME, 0x00, 0x0A, 'b', 'r', 'a' }; + PARCBuffer *decodeBuffer = parcBuffer_Wrap(decodeBytes, sizeof(decodeBytes), 0, sizeof(decodeBytes)); + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(decodeBuffer); + CCNxNameSegment *test = ccnxCodecSchemaV1NameSegmentCodec_Decode(decoder); + + assertNull(test, "Name segments should have been null because there are not enough bytes in buffer"); + + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&decodeBuffer); +} + +/** + * In this case, we decode the T and L, but there are not enough bytes for the V + */ +LONGBOW_TEST_CASE(Global, ccnxTlvCodecNameSegment_Decode_VShort) +{ + uint8_t decodeBytes[] = { 0x00, CCNxNameLabelType_NAME, 0x00 }; + PARCBuffer *decodeBuffer = parcBuffer_Wrap(decodeBytes, sizeof(decodeBytes), 0, sizeof(decodeBytes)); + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(decodeBuffer); + CCNxNameSegment *test = ccnxCodecSchemaV1NameSegmentCodec_Decode(decoder); + + assertNull(test, "Name segments should have been null because there are not enough bytes in buffer"); + + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&decodeBuffer); +} + + +LONGBOW_TEST_CASE(Global, ccnxTlvCodecNameSegment_Encode) +{ + uint8_t truthBytes[] = { 0x00, CCNxNameLabelType_NAME, 0x00, 0x0A, 'b', 'r', 'a', 'n', 'd', 'y', 'w', 'i', 'n', 'e' }; + PARCBuffer *truth = parcBuffer_Wrap(truthBytes, sizeof(truthBytes), 0, sizeof(truthBytes)); + + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + ccnxCodecTlvEncoder_Initialize(encoder); + + PARCBuffer *buffer = parcBuffer_WrapCString("brandywine"); + CCNxNameSegment *segment = ccnxNameSegment_CreateTypeValue(CCNxNameLabelType_NAME, buffer); + parcBuffer_Release(&buffer); + + ccnxCodecSchemaV1NameSegmentCodec_Encode(encoder, segment); + + ccnxCodecTlvEncoder_Finalize(encoder); + PARCBuffer *test = ccnxCodecTlvEncoder_CreateBuffer(encoder); + assertTrue(parcBuffer_Equals(truth, test), "Buffers do not match"); + + parcBuffer_Release(&test); + ccnxNameSegment_Release(&segment); + ccnxCodecTlvEncoder_Destroy(&encoder); + parcBuffer_Release(&truth); +} + +int +main(int argc, char *argv[argc]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(ccnxTlvCodec_Name); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-common/ccnx/common/codec/schema_v1/test/test_ccnxCodecSchemaV1_OptionalHeadersDecoder.c b/libccnx-common/ccnx/common/codec/schema_v1/test/test_ccnxCodecSchemaV1_OptionalHeadersDecoder.c new file mode 100644 index 00000000..abe47608 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/test/test_ccnxCodecSchemaV1_OptionalHeadersDecoder.c @@ -0,0 +1,321 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../ccnxCodecSchemaV1_OptionalHeadersDecoder.c" +#include <parc/algol/parc_SafeMemory.h> + +#include <inttypes.h> + +#include <LongBow/unit-test.h> + +typedef struct test_data { + PARCBuffer *optionalHeader; + CCNxCodecTlvDecoder *decoder; + CCNxTlvDictionary *dictionary; + + // truth table + PARCBuffer *interestLifetime; + PARCBuffer *cacheTime; + PARCBuffer *interestFrag; + PARCBuffer *objectFrag; + PARCBuffer *customHeader; + + // the key for the customHeader + uint16_t customHeaderType; +} TestData; + +/** + * A packet with all the defined optional headers plus one custom header + * This is not actually a packet one would see in real life as it has + * headers from both an Interest and a Content Object + */ +static uint8_t packet_with_headers[] = { + 0x01, 0x01, 0x00, 120, // ver = 1, type = interest, length = 120 + 0x01, 0x00, 0x00, 88, // hopLimit = 1, reserved = 0, header length = 88 + // ------------------------ + // byte 8 + 0x00, 0x01, 0x00, 0x08, // Interest Lifetime (type 1) + 0x20, 0x30, 0x40, 0x50, // 0x2030405060708090 + 0x60, 0x70, 0x80, 0x90, + // ------------------------ + // byte 20 + 0x00, 0x02, 0x00, 0x08, // Recommended Cache Time (type 2) + 0x21, 0x31, 0x41, 0x51, // 0x2030405060708090 + 0x61, 0x71, 0x81, 0x91, + // ------------------------ + // byte 32 + 0x00, 0x03, 0x00, 0x0C, // Interest Fragment (type 3) + 0x01, 0x02, 0x03, 0x04, + 0x05, 0x06, 0x07, 0x08, // fragment 0x0102030405060708 + 0x05, 0xDC, 0x04, 0x00, // MTU 1500, fragcnt 4, fragnum 0 + // ------------------------ + // byte 48 + 0x00, 0x04, 0x00, 20, // ContentObject Fragment (type 4) + 0xC1, 0xC2, 0xC3, 0xC4, + 0xC5, 0xC6, 0xC7, 0xC8, // fragment 0xC1C2C3C4C5C6C7C8 + 0x05, 0xDC, 0x04, 0x00, // MTU 1500, fragcnt 4, fragnum 0 + 0xD1, 0xD2, 0xD3, 0xD4, + 0xD5, 0xD6, 0xD7, 0xD8, // fragment 0xD1D2D3D4D5D6D7D8 + // ------------------------ + // byte 72 + 0x01, 0x00, 0x00, 12, // Custom header (type 256), length 12 + 0xA0, 0xA1, 0xA2, 0xA3, + 0xA4, 0xA5, 0xA6, 0xA7, + 0xA8, 0xA9, 0xAA, 0xAB, + // ------------------------ + // byte 88 + 0x00, 0x01, 0x00, 29, // type = interest, length = 29 + // ------------------------ + 0x00, 0x00, 0x00, 0x10, // type = name, length = 16 + 0x00, 0x02, 0x00, 0x04, // type = binary, length = 5 + 'h', 'e', 'l', 'l', // "hello" + 0xF0, 0x00, 0x00, 0x04, // type = app, length = 4 + 'o', 'u', 'c', 'h', // "ouch" + // ------------------------ + 0x00, 0x01, 0x00, 0x04, // type = keyid, length = 4 + 0xA0, 0xB0, 0xC0, 0xD0, // 0xA0B0C0D0 + // ------------------------ + // byte 120 +}; + +static TestData * +_commonSetup(void) +{ + TestData *data = parcMemory_AllocateAndClear(sizeof(TestData)); + assertNotNull(data, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(TestData)); + + // setup the decoder and decode the optional headers + data->optionalHeader = parcBuffer_Wrap(packet_with_headers, sizeof(packet_with_headers), 8, 88); + data->decoder = ccnxCodecTlvDecoder_Create(data->optionalHeader); + data->dictionary = ccnxTlvDictionary_Create(10, 5); + + // setup the truth values + data->interestLifetime = parcBuffer_Wrap(packet_with_headers, sizeof(packet_with_headers), 12, 20); + data->cacheTime = parcBuffer_Wrap(packet_with_headers, sizeof(packet_with_headers), 24, 32); + + data->interestFrag = parcBuffer_Wrap(packet_with_headers, sizeof(packet_with_headers), 36, 48); + data->objectFrag = parcBuffer_Wrap(packet_with_headers, sizeof(packet_with_headers), 52, 72); + + data->customHeader = parcBuffer_Wrap(packet_with_headers, sizeof(packet_with_headers), 76, 88); + data->customHeaderType = 0x0100; + + return data; +} + +static void +_commonTeardown(TestData *data) +{ + ccnxTlvDictionary_Release(&data->dictionary); + ccnxCodecTlvDecoder_Destroy(&data->decoder); + parcBuffer_Release(&data->optionalHeader); + + parcBuffer_Release(&data->interestLifetime); + parcBuffer_Release(&data->cacheTime); + parcBuffer_Release(&data->interestFrag); + parcBuffer_Release(&data->objectFrag); + parcBuffer_Release(&data->customHeader); + + parcMemory_Deallocate((void **) &data); +} + +LONGBOW_TEST_RUNNER(ccnxCodecSchemaV1_OptionalHeadersDecoder) +{ + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(ccnxCodecSchemaV1_OptionalHeadersDecoder) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(ccnxCodecSchemaV1_OptionalHeadersDecoder) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecSchemaV1OptionalHeadersDecoder_Decode_TooLong); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecSchemaV1OptionalHeadersDecoder_GetContentObjectFragmentHeader); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecSchemaV1OptionalHeadersDecoder_GetInterestFragmentHeader); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecSchemaV1OptionalHeadersDecoder_GetInterestLifetimeHeader); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecSchemaV1OptionalHeadersDecoder_GetRecommendedCacheTimeHeader); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecSchemaV1OptionalHeadersDecoder_GetCustomHeader); + + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecSchemaV1OptionalHeadersDecoder_GetContentObjectFragmentHeader_Missing); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecSchemaV1OptionalHeadersDecoder_GetInterestFragmentHeader_Missing); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecSchemaV1OptionalHeadersDecoder_GetInterestLifetimeHeader_Missing); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecSchemaV1OptionalHeadersDecoder_GetRecommendedCacheTimeHeader_Missing); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecSchemaV1OptionalHeadersDecoder_GetCustomHeader_Missing); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + longBowTestCase_SetClipBoardData(testCase, _commonSetup()); + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + _commonTeardown(longBowTestCase_GetClipBoardData(testCase)); + + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +/** + * One of the TLVs will extend beyond the end of the buffer. Should fail. + */ +LONGBOW_TEST_CASE(Global, ccnxCodecSchemaV1OptionalHeadersDecoder_Decode_TooLong) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + // Make one of the fields 255 bytes + uint8_t original = packet_with_headers[10]; + packet_with_headers[10] = 0xFF; + bool success = ccnxCodecSchemaV1OptionalHeadersDecoder_Decode(data->decoder, data->dictionary); + + // now set it back + packet_with_headers[10] = original; + + assertFalse(success, "Should have failed to parse when a TLV exceeds bounary"); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecSchemaV1OptionalHeadersDecoder_GetContentObjectFragmentHeader) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + ccnxCodecSchemaV1OptionalHeadersDecoder_Decode(data->decoder, data->dictionary); + PARCBuffer *test = ccnxCodecSchemaV1OptionalHeadersDecoder_GetContentObjectFragmentHeader(data->dictionary); + assertTrue(parcBuffer_Equals(test, data->objectFrag), "Wrong value") + { + printf("Expected: \n"); + parcBuffer_Display(data->objectFrag, 3); + printf("Got:\n"); + parcBuffer_Display(test, 3); + } +} + +LONGBOW_TEST_CASE(Global, ccnxCodecSchemaV1OptionalHeadersDecoder_GetInterestFragmentHeader) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + ccnxCodecSchemaV1OptionalHeadersDecoder_Decode(data->decoder, data->dictionary); + PARCBuffer *test = ccnxCodecSchemaV1OptionalHeadersDecoder_GetInterestFragmentHeader(data->dictionary); + assertTrue(parcBuffer_Equals(test, data->interestFrag), "Wrong value") + { + printf("Expected: \n"); + parcBuffer_Display(data->interestFrag, 3); + printf("Got:\n"); + parcBuffer_Display(test, 3); + } +} + +LONGBOW_TEST_CASE(Global, ccnxCodecSchemaV1OptionalHeadersDecoder_GetInterestLifetimeHeader) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + ccnxCodecSchemaV1OptionalHeadersDecoder_Decode(data->decoder, data->dictionary); + uint64_t lifetime = ccnxCodecSchemaV1OptionalHeadersDecoder_GetInterestLifetimeHeader(data->dictionary); + + uint64_t trueLifetime = 0; + ccnxCodecTlvUtilities_GetVarInt(data->interestLifetime, parcBuffer_Remaining(data->interestLifetime), &trueLifetime); + + assertTrue(trueLifetime == lifetime, "wrong value, expected %" PRIx64 " got %" PRIx64, trueLifetime, lifetime); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecSchemaV1OptionalHeadersDecoder_GetRecommendedCacheTimeHeader) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + ccnxCodecSchemaV1OptionalHeadersDecoder_Decode(data->decoder, data->dictionary); + uint64_t cachetime = ccnxCodecSchemaV1OptionalHeadersDecoder_GetRecommendedCacheTimeHeader(data->dictionary); + + uint64_t trueCachetime = 0; + ccnxCodecTlvUtilities_GetVarInt(data->cacheTime, parcBuffer_Remaining(data->cacheTime), &trueCachetime); + + assertTrue(trueCachetime == cachetime, "wrong value, expected %" PRIx64 " got %" PRIx64, trueCachetime, cachetime); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecSchemaV1OptionalHeadersDecoder_GetCustomHeader) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + ccnxCodecSchemaV1OptionalHeadersDecoder_Decode(data->decoder, data->dictionary); + PARCBuffer *test = ccnxCodecSchemaV1OptionalHeadersDecoder_GetCustomType(data->dictionary, data->customHeaderType); + assertTrue(parcBuffer_Equals(test, data->customHeader), "Wrong value for header type %02X", data->customHeaderType) + { + printf("Expected: \n"); + parcBuffer_Display(data->customHeader, 3); + printf("Got:\n"); + parcBuffer_Display(test, 3); + } +} + +// ======== +// test for missing values + +LONGBOW_TEST_CASE(Global, ccnxCodecSchemaV1OptionalHeadersDecoder_GetContentObjectFragmentHeader_Missing) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCBuffer *test = ccnxCodecSchemaV1OptionalHeadersDecoder_GetContentObjectFragmentHeader(data->dictionary); + assertNull(test, "Did not get null buffer for missing field, got %p", (void *) test); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecSchemaV1OptionalHeadersDecoder_GetInterestFragmentHeader_Missing) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCBuffer *test = ccnxCodecSchemaV1OptionalHeadersDecoder_GetInterestFragmentHeader(data->dictionary); + assertNull(test, "Did not get null buffer for missing field, got %p", (void *) test); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecSchemaV1OptionalHeadersDecoder_GetInterestLifetimeHeader_Missing) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + bool exists = ccnxTlvDictionary_IsValueInteger(data->dictionary, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_InterestLifetime); + assertFalse(exists, "Dictionary reports it has a missing field"); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecSchemaV1OptionalHeadersDecoder_GetRecommendedCacheTimeHeader_Missing) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + bool exists = ccnxTlvDictionary_IsValueInteger(data->dictionary, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_RecommendedCacheTime); + assertFalse(exists, "Dictionary reports it has a missing field"); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecSchemaV1OptionalHeadersDecoder_GetCustomHeader_Missing) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCBuffer *test = ccnxCodecSchemaV1OptionalHeadersDecoder_GetCustomType(data->dictionary, data->customHeaderType); + assertNull(test, "Did not get null buffer for missing field, got %p", (void *) test); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(ccnxCodecSchemaV1_OptionalHeadersDecoder); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-common/ccnx/common/codec/schema_v1/test/test_ccnxCodecSchemaV1_OptionalHeadersEncoder.c b/libccnx-common/ccnx/common/codec/schema_v1/test/test_ccnxCodecSchemaV1_OptionalHeadersEncoder.c new file mode 100644 index 00000000..1950eef2 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/test/test_ccnxCodecSchemaV1_OptionalHeadersEncoder.c @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../ccnxCodecSchemaV1_OptionalHeadersEncoder.c" +#include <parc/algol/parc_SafeMemory.h> +#include <LongBow/unit-test.h> + +#include <ccnx/common/codec/schema_v1/testdata/v1_interest_nameA.h> +#include <ccnx/common/codec/schema_v1/testdata/v1_content_nameA_crc32c.h> + +#include "testrig_encoder.c" + +static TruthTableEntry +TRUTHTABLENAME(interest_optional_headers)[] = +{ + { .wellKnownType = false, .indexOrKey = V1_MANIFEST_INT_OPTHEAD, .bodyManifest = true, .extent = { 8, 28 } }, // index = 0 + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_INT_E2EFRAG, .bodyManifest = false, .extent = { 12, 12 } }, // index = 1 + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_INT_LIFETIME, .bodyManifest = false, .extent = { 28, 8 } }, // index = 2 + { .wellKnownType = false, .indexOrKey = T_INVALID, .extent = { 0, 0 } }, +}; + +static TruthTableEntry +TRUTHTABLENAME(contentobject_optional_headers)[] = +{ + { .wellKnownType = false, .indexOrKey = V1_MANIFEST_OBJ_OPTHEAD, .bodyManifest = true, .extent = { 8, 36 } }, // 0 + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_OBJ_E2EFRAG, .bodyManifest = false, .extent = { 12, 20 } }, + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_OBJ_RecommendedCacheTime, .bodyManifest = false, .extent = { 36, 8 } }, + { .wellKnownType = false, .indexOrKey = T_INVALID, .extent = { 0, 0 } }, +}; + + +LONGBOW_TEST_RUNNER(ccnxCodecSchemaV0_OptionalHeadersEncoder) +{ + LONGBOW_RUN_TEST_FIXTURE(Interest); + LONGBOW_RUN_TEST_FIXTURE(ContentObject); + LONGBOW_RUN_TEST_FIXTURE(UnknownType); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(ccnxCodecSchemaV0_OptionalHeadersEncoder) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(ccnxCodecSchemaV0_OptionalHeadersEncoder) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// ================================================================================== + +LONGBOW_TEST_FIXTURE(Interest) +{ + LONGBOW_RUN_TEST_CASE(Interest, ccnxCodecSchemaV1OptionalHeadersEncoder_Encode); +} + +LONGBOW_TEST_FIXTURE_SETUP(Interest) +{ + longBowTestCase_SetClipBoardData(testCase, + commonSetup(v1_interest_nameA, + sizeof(v1_interest_nameA), + TRUTHTABLENAME(interest_optional_headers), + V1_MANIFEST_INT_OPTHEAD)); + + // commonSetup will not add headers... + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + addBuffer(data, data->dictionary, TRUTHTABLENAME(interest_optional_headers)[1].extent.offset, TRUTHTABLENAME(interest_optional_headers)[1].extent.offset + TRUTHTABLENAME(interest_optional_headers)[1].extent.length, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_INTFRAG); + + addBuffer(data, data->dictionary, TRUTHTABLENAME(interest_optional_headers)[2].extent.offset, TRUTHTABLENAME(interest_optional_headers)[2].extent.offset + TRUTHTABLENAME(interest_optional_headers)[2].extent.length, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_InterestLifetime); + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Interest) +{ + testrigencoder_CommonTeardown(longBowTestCase_GetClipBoardData(testCase)); + + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Interest, ccnxCodecSchemaV1OptionalHeadersEncoder_Encode) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + ssize_t length = ccnxCodecSchemaV1OptionalHeadersEncoder_Encode(data->encoder, data->dictionary); + assertTrue(length >= 0, "Error on Encode: length %zd", length); + testCompareEncoderToBuffer(data->encoder, data->memoryRegion); +} + +// ================================================================================== + +LONGBOW_TEST_FIXTURE(ContentObject) +{ + LONGBOW_RUN_TEST_CASE(ContentObject, ccnxCodecSchemaV1OptionalHeadersEncoder_Encode); +} + +LONGBOW_TEST_FIXTURE_SETUP(ContentObject) +{ + longBowTestCase_SetClipBoardData(testCase, + commonSetup(v1_content_nameA_crc32c, + sizeof(v1_content_nameA_crc32c), + TRUTHTABLENAME(contentobject_optional_headers), + V1_MANIFEST_OBJ_OPTHEAD)); + + // commonSetup will not add headers... + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + addBuffer(data, data->dictionary, TRUTHTABLENAME(contentobject_optional_headers)[1].extent.offset, TRUTHTABLENAME(contentobject_optional_headers)[1].extent.offset + TRUTHTABLENAME(contentobject_optional_headers)[1].extent.length, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_OBJFRAG); + + addBuffer(data, data->dictionary, TRUTHTABLENAME(contentobject_optional_headers)[2].extent.offset, TRUTHTABLENAME(contentobject_optional_headers)[2].extent.offset + TRUTHTABLENAME(contentobject_optional_headers)[2].extent.length, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_RecommendedCacheTime); + + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(ContentObject) +{ + testrigencoder_CommonTeardown(longBowTestCase_GetClipBoardData(testCase)); + + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(ContentObject, ccnxCodecSchemaV1OptionalHeadersEncoder_Encode) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + ssize_t length = ccnxCodecSchemaV1OptionalHeadersEncoder_Encode(data->encoder, data->dictionary); + assertTrue(length >= 0, "Error on Encode: length %zd", length); + testCompareEncoderToBuffer(data->encoder, data->memoryRegion); +} + +// ================================================================================== + +LONGBOW_TEST_FIXTURE(UnknownType) +{ + LONGBOW_RUN_TEST_CASE(UnknownType, Unknown); +} + +LONGBOW_TEST_FIXTURE_SETUP(UnknownType) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(UnknownType) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(UnknownType, Unknown) +{ + CCNxTlvDictionary *unknown = ccnxTlvDictionary_Create(CCNxCodecSchemaV1TlvDictionary_MessageFastArray_END, 1); + + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + ssize_t length = ccnxCodecSchemaV1OptionalHeadersEncoder_Encode(encoder, unknown); + assertTrue(length < 0, "Did not get error return when encoding unknown type"); + + CCNxCodecError *error = ccnxCodecTlvEncoder_GetError(encoder); + assertNotNull(error, "encoder did not set the error"); + + ccnxCodecTlvEncoder_Destroy(&encoder); + ccnxTlvDictionary_Release(&unknown); +} + +// ================================================================================== + + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(ccnxCodecSchemaV0_OptionalHeadersEncoder); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-common/ccnx/common/codec/schema_v1/test/test_ccnxCodecSchemaV1_PacketDecoder.c b/libccnx-common/ccnx/common/codec/schema_v1/test/test_ccnxCodecSchemaV1_PacketDecoder.c new file mode 100755 index 00000000..42d5be41 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/test/test_ccnxCodecSchemaV1_PacketDecoder.c @@ -0,0 +1,684 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Decode whole packets then spot-check that fields from each section appear. We don't need + * to exhastively test here, as the individual decoders are exhaustively tested. + * + */ +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../ccnxCodecSchemaV1_PacketDecoder.c" +#include <parc/algol/parc_SafeMemory.h> +#include <LongBow/unit-test.h> +#include <ccnx/common/codec/schema_v1/testdata/v1_content_nameA_keyid1_rsasha256.h> +#include <ccnx/common/codec/schema_v1/testdata/v1_content_zero_payload.h> +#include <ccnx/common/codec/schema_v1/testdata/v1_content_no_payload.h> +#include <ccnx/common/codec/schema_v1/testdata/v1_interest_all_fields.h> +#include <ccnx/common/codec/schema_v1/testdata/v1_interest_nameA_crc32c.h> +#include <ccnx/common/codec/schema_v1/testdata/v1_interest_bad_message_length.h> +#include <ccnx/common/codec/schema_v1/testdata/v1_interest_bad_validation_alg.h> +#include <ccnx/common/codec/schema_v1/testdata/v1_interest_validation_alg_overrun.h> +#include <ccnx/common/codec/schema_v1/testdata/v1_cpi_add_route_crc32c.h> + +#include "testrig_packetwrapper.c" + +#include <ccnx/common/internal/ccnx_ContentObjectFacadeV1.h> +#include <ccnx/common/internal/ccnx_ValidationFacadeV1.h> + +// ========================================================================= + +LONGBOW_TEST_RUNNER(ccnxCodecSchemaV1_PacketDecoder) +{ + LONGBOW_RUN_TEST_FIXTURE(ContentObject); + LONGBOW_RUN_TEST_FIXTURE(Control); + LONGBOW_RUN_TEST_FIXTURE(Interest); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(ccnxCodecSchemaV1_PacketDecoder) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(ccnxCodecSchemaV1_PacketDecoder) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + + +// ========================================================================= + +LONGBOW_TEST_FIXTURE(ContentObject) +{ + LONGBOW_RUN_TEST_CASE(ContentObject, ContentObject_RsaSha256_FixedHeader); + LONGBOW_RUN_TEST_CASE(ContentObject, ContentObject_RsaSha256_Name); + LONGBOW_RUN_TEST_CASE(ContentObject, ContentObject_RsaSha256_ExpiryTime); + LONGBOW_RUN_TEST_CASE(ContentObject, ContentObject_RsaSha256_ValidationAlg_KeyId); + LONGBOW_RUN_TEST_CASE(ContentObject, ContentObject_RsaSha256_ValidationPayload); + LONGBOW_RUN_TEST_CASE(ContentObject, ContentObject_zero_payload); + LONGBOW_RUN_TEST_CASE(ContentObject, ContentObject_no_payload); +} + +LONGBOW_TEST_FIXTURE_SETUP(ContentObject) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(ContentObject) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(ContentObject, ContentObject_RsaSha256_FixedHeader) +{ + PARCBuffer *packetBuffer = parcBuffer_Wrap(v1_content_nameA_keyid1_rsasha256, sizeof(v1_content_nameA_keyid1_rsasha256), 0, sizeof(v1_content_nameA_keyid1_rsasha256)); + + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(packetBuffer); + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateContentObject(); + bool success = ccnxCodecSchemaV1PacketDecoder_Decode(decoder, dictionary); + assertTrue(success, "Error on decode: %s", ccnxCodecError_ToString(ccnxCodecTlvDecoder_GetError(decoder))); + + PARCBuffer *fixedHeader = ccnxTlvDictionary_GetBuffer(dictionary, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_FixedHeader); + + // leave the FixedHeader buffer at position 0 + parcBuffer_Rewind(fixedHeader); + + PARCBuffer *trueFixedHeader = parcBuffer_Wrap(v1_content_nameA_keyid1_rsasha256, 8, 0, 8); + assertTrue(parcBuffer_Equals(fixedHeader, trueFixedHeader), "Buffers not equal") + { + printf("Expected\n"); + parcBuffer_Display(trueFixedHeader, 3); + printf("Got\n"); + parcBuffer_Display(fixedHeader, 3); + } + + parcBuffer_Release(&trueFixedHeader); + parcBuffer_Release(&packetBuffer); + ccnxTlvDictionary_Release(&dictionary); + ccnxCodecTlvDecoder_Destroy(&decoder); +} + +LONGBOW_TEST_CASE(ContentObject, ContentObject_RsaSha256_Name) +{ + PARCBuffer *packetBuffer = parcBuffer_Wrap(v1_content_nameA_keyid1_rsasha256, + sizeof(v1_content_nameA_keyid1_rsasha256), + 0, + sizeof(v1_content_nameA_keyid1_rsasha256)); + + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(packetBuffer); + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateContentObject(); + bool success = ccnxCodecSchemaV1PacketDecoder_Decode(decoder, dictionary); + assertTrue(success, "Error on decode: %s", ccnxCodecError_ToString(ccnxCodecTlvDecoder_GetError(decoder))); + + CCNxName *name = ccnxTlvDictionary_GetName(dictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_NAME); + CCNxName *trueName = ccnxName_CreateFromCString(v1_content_nameA_keyid1_rsasha256_URI); + + assertTrue(ccnxName_Equals(name, trueName), "Buffers not equal") + { + printf("Expected\n"); + ccnxName_Display(trueName, 3); + printf("Got\n"); + ccnxName_Display(name, 3); + } + + ccnxName_Release(&trueName); + parcBuffer_Release(&packetBuffer); + ccnxTlvDictionary_Release(&dictionary); + ccnxCodecTlvDecoder_Destroy(&decoder); +} + +LONGBOW_TEST_CASE(ContentObject, ContentObject_RsaSha256_ExpiryTime) +{ + PARCBuffer *packetBuffer = parcBuffer_Wrap(v1_content_nameA_keyid1_rsasha256, sizeof(v1_content_nameA_keyid1_rsasha256), 0, sizeof(v1_content_nameA_keyid1_rsasha256)); + + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(packetBuffer); + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateContentObject(); + bool success = ccnxCodecSchemaV1PacketDecoder_Decode(decoder, dictionary); + assertTrue(success, "Error on decode: %s", ccnxCodecError_ToString(ccnxCodecTlvDecoder_GetError(decoder))); + + uint64_t expiryTime = ccnxTlvDictionary_GetInteger(dictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_EXPIRY_TIME); + + TlvExtent expiryExtent = getTruthTableExtent(TRUTHTABLENAME(v1_content_nameA_keyid1_rsasha256), V1_MANIFEST_OBJ_EXPIRY_TIME); + + parcBuffer_SetPosition(packetBuffer, expiryExtent.offset); + uint64_t trueTime; + ccnxCodecTlvUtilities_GetVarInt(packetBuffer, expiryExtent.length, &trueTime); + + assertTrue(expiryTime == trueTime, "Wrong time, expected %" PRIx64 " got %" PRIx64, trueTime, expiryTime); + + parcBuffer_Release(&packetBuffer); + ccnxTlvDictionary_Release(&dictionary); + ccnxCodecTlvDecoder_Destroy(&decoder); +} + +LONGBOW_TEST_CASE(ContentObject, ContentObject_RsaSha256_ValidationAlg_KeyId) +{ + PARCBuffer *packetBuffer = parcBuffer_Wrap(v1_content_nameA_keyid1_rsasha256, sizeof(v1_content_nameA_keyid1_rsasha256), 0, sizeof(v1_content_nameA_keyid1_rsasha256)); + + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(packetBuffer); + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateContentObject(); + bool success = ccnxCodecSchemaV1PacketDecoder_Decode(decoder, dictionary); + assertTrue(success, "Error on decode: %s", ccnxCodecError_ToString(ccnxCodecTlvDecoder_GetError(decoder))); + + PARCBuffer *keyid = ccnxTlvDictionary_GetBuffer(dictionary, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_KEYID); + + TlvExtent keyidExtent = getTruthTableExtent(TRUTHTABLENAME(v1_content_nameA_keyid1_rsasha256), V1_MANIFEST_OBJ_KEYID); + PARCBuffer *trueKeyid = parcBuffer_Wrap(v1_content_nameA_keyid1_rsasha256, sizeof(v1_content_nameA_keyid1_rsasha256), keyidExtent.offset, keyidExtent.offset + keyidExtent.length); + + assertTrue(parcBuffer_Equals(keyid, trueKeyid), "Buffers not equal") + { + printf("Expected\n"); + parcBuffer_Display(trueKeyid, 3); + printf("Got\n"); + parcBuffer_Display(keyid, 3); + } + + parcBuffer_Release(&trueKeyid); + parcBuffer_Release(&packetBuffer); + ccnxTlvDictionary_Release(&dictionary); + ccnxCodecTlvDecoder_Destroy(&decoder); +} + +LONGBOW_TEST_CASE(ContentObject, ContentObject_RsaSha256_ValidationPayload) +{ + PARCBuffer *packetBuffer = parcBuffer_Wrap(v1_content_nameA_keyid1_rsasha256, sizeof(v1_content_nameA_keyid1_rsasha256), 0, sizeof(v1_content_nameA_keyid1_rsasha256)); + + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(packetBuffer); + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateContentObject(); + bool success = ccnxCodecSchemaV1PacketDecoder_Decode(decoder, dictionary); + assertTrue(success, "Error on decode: %s", ccnxCodecError_ToString(ccnxCodecTlvDecoder_GetError(decoder))); + + PARCBuffer *validationPayload = ccnxTlvDictionary_GetBuffer(dictionary, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_PAYLOAD); + + TlvExtent payloadExtent = getTruthTableExtent(TRUTHTABLENAME(v1_content_nameA_keyid1_rsasha256), V1_MANIFEST_OBJ_SIGBITS); + PARCBuffer *truePayload = parcBuffer_Wrap(v1_content_nameA_keyid1_rsasha256, sizeof(v1_content_nameA_keyid1_rsasha256), payloadExtent.offset, payloadExtent.offset + payloadExtent.length); + + assertTrue(parcBuffer_Equals(validationPayload, truePayload), "Buffers not equal") + { + printf("Expected\n"); + parcBuffer_Display(truePayload, 3); + printf("Got\n"); + parcBuffer_Display(validationPayload, 3); + } + + parcBuffer_Release(&truePayload); + parcBuffer_Release(&packetBuffer); + ccnxTlvDictionary_Release(&dictionary); + ccnxCodecTlvDecoder_Destroy(&decoder); +} + +LONGBOW_TEST_CASE(ContentObject, ContentObject_zero_payload) +{ + PARCBuffer *packetBuffer = parcBuffer_Wrap(v1_content_zero_payload, sizeof(v1_content_zero_payload), 0, sizeof(v1_content_zero_payload)); + + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(packetBuffer); + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateContentObject(); + bool success = ccnxCodecSchemaV1PacketDecoder_Decode(decoder, dictionary); + assertTrue(success, "Error on decode: %s", ccnxCodecError_ToString(ccnxCodecTlvDecoder_GetError(decoder))); + + CCNxName *testName = CCNxContentObjectFacadeV1_Implementation.getName(dictionary); + assertNotNull(testName, "Got null name on decode"); + + PARCBuffer *testPayload = CCNxContentObjectFacadeV1_Implementation.getPayload(dictionary); + assertNotNull(testPayload, "got null payload") + assertTrue(parcBuffer_Remaining(testPayload) == 0, "Wrong length, expected 0 got %zu", parcBuffer_Remaining(testPayload)); + + parcBuffer_Release(&packetBuffer); + ccnxTlvDictionary_Release(&dictionary); + ccnxCodecTlvDecoder_Destroy(&decoder); +} + +LONGBOW_TEST_CASE(ContentObject, ContentObject_no_payload) +{ + PARCBuffer *packetBuffer = parcBuffer_Wrap(v1_content_no_payload, sizeof(v1_content_no_payload), 0, sizeof(v1_content_no_payload)); + + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(packetBuffer); + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateContentObject(); + bool success = ccnxCodecSchemaV1PacketDecoder_Decode(decoder, dictionary); + assertTrue(success, "Error on decode: %s", ccnxCodecError_ToString(ccnxCodecTlvDecoder_GetError(decoder))); + + CCNxName *test = CCNxContentObjectFacadeV1_Implementation.getName(dictionary); + assertNotNull(test, "Got null name on decode"); + + parcBuffer_Release(&packetBuffer); + ccnxTlvDictionary_Release(&dictionary); + ccnxCodecTlvDecoder_Destroy(&decoder); +} + + +// ========================================================================= + +LONGBOW_TEST_FIXTURE(Control) +{ + LONGBOW_RUN_TEST_CASE(Control, CPIAddRoute_Crc32c_FixedHeader); + LONGBOW_RUN_TEST_CASE(Control, CPIAddRoute_Crc32c_Payload); + LONGBOW_RUN_TEST_CASE(Control, CPIAddRoute_Crc32c_ValidationAlg_CryptoSuite); + LONGBOW_RUN_TEST_CASE(Control, CPIAddRoute_Crc32c_ValidationPayload); +} + +LONGBOW_TEST_FIXTURE_SETUP(Control) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Control) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Control, CPIAddRoute_Crc32c_FixedHeader) +{ + PARCBuffer *packetBuffer = parcBuffer_Wrap(v1_cpi_add_route_crc32c, sizeof(v1_cpi_add_route_crc32c), 0, sizeof(v1_cpi_add_route_crc32c)); + + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(packetBuffer); + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateContentObject(); + bool success = ccnxCodecSchemaV1PacketDecoder_Decode(decoder, dictionary); + assertTrue(success, "Error on decode: %s", ccnxCodecError_ToString(ccnxCodecTlvDecoder_GetError(decoder))); + + PARCBuffer *fixedHeader = ccnxTlvDictionary_GetBuffer(dictionary, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_FixedHeader); + + // leave the FixedHeader buffer at position 0 + parcBuffer_Rewind(fixedHeader); + + PARCBuffer *trueFixedHeader = parcBuffer_Wrap(v1_cpi_add_route_crc32c, 8, 0, 8); + assertTrue(parcBuffer_Equals(fixedHeader, trueFixedHeader), "Buffers not equal") + { + printf("Expected\n"); + parcBuffer_Display(trueFixedHeader, 3); + printf("Got\n"); + parcBuffer_Display(fixedHeader, 3); + } + + parcBuffer_Release(&trueFixedHeader); + parcBuffer_Release(&packetBuffer); + ccnxTlvDictionary_Release(&dictionary); + ccnxCodecTlvDecoder_Destroy(&decoder); +} + +LONGBOW_TEST_CASE(Control, CPIAddRoute_Crc32c_Payload) +{ + PARCBuffer *packetBuffer = parcBuffer_Wrap(v1_cpi_add_route_crc32c, sizeof(v1_cpi_add_route_crc32c), 0, sizeof(v1_cpi_add_route_crc32c)); + + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(packetBuffer); + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateContentObject(); + bool success = ccnxCodecSchemaV1PacketDecoder_Decode(decoder, dictionary); + assertTrue(success, "Error on decode: %s", ccnxCodecError_ToString(ccnxCodecTlvDecoder_GetError(decoder))); + + PARCBuffer *test = ccnxTlvDictionary_GetBuffer(dictionary, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_PAYLOAD); + + TlvExtent validationPayloadExtent = getTruthTableExtent(TRUTHTABLENAME(v1_cpi_add_route_crc32c), V1_MANIFEST_CPI_SIGBITS); + PARCBuffer *trueValidationPayload = parcBuffer_Wrap(v1_cpi_add_route_crc32c, sizeof(v1_cpi_add_route_crc32c), validationPayloadExtent.offset, validationPayloadExtent.offset + validationPayloadExtent.length); + + assertTrue(parcBuffer_Equals(test, trueValidationPayload), "Buffers not equal") + { + printf("Expected\n"); + parcBuffer_Display(trueValidationPayload, 3); + printf("Got\n"); + parcBuffer_Display(test, 3); + } + + parcBuffer_Release(&trueValidationPayload); + parcBuffer_Release(&packetBuffer); + ccnxTlvDictionary_Release(&dictionary); + ccnxCodecTlvDecoder_Destroy(&decoder); +} + +LONGBOW_TEST_CASE(Control, CPIAddRoute_Crc32c_ValidationAlg_CryptoSuite) +{ + PARCBuffer *packetBuffer = parcBuffer_Wrap(v1_cpi_add_route_crc32c, sizeof(v1_cpi_add_route_crc32c), 0, sizeof(v1_cpi_add_route_crc32c)); + + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(packetBuffer); + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateContentObject(); + bool success = ccnxCodecSchemaV1PacketDecoder_Decode(decoder, dictionary); + assertTrue(success, "Error on decode: %s", ccnxCodecError_ToString(ccnxCodecTlvDecoder_GetError(decoder))); + + PARCCryptoSuite test = (PARCCryptoSuite) ccnxValidationFacadeV1_GetCryptoSuite(dictionary); + PARCCryptoSuite trueCryptoSuite = PARCCryptoSuite_NULL_CRC32C; + + assertTrue(test == trueCryptoSuite, "Wrong crypto suite, expected %d got %d", trueCryptoSuite, test); + + parcBuffer_Release(&packetBuffer); + ccnxTlvDictionary_Release(&dictionary); + ccnxCodecTlvDecoder_Destroy(&decoder); +} + +LONGBOW_TEST_CASE(Control, CPIAddRoute_Crc32c_ValidationPayload) +{ + PARCBuffer *packetBuffer = parcBuffer_Wrap(v1_cpi_add_route_crc32c, sizeof(v1_cpi_add_route_crc32c), 0, sizeof(v1_cpi_add_route_crc32c)); + + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(packetBuffer); + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateContentObject(); + bool success = ccnxCodecSchemaV1PacketDecoder_Decode(decoder, dictionary); + assertTrue(success, "Error on decode: %s", ccnxCodecError_ToString(ccnxCodecTlvDecoder_GetError(decoder))); + + PARCBuffer *validationPayload = ccnxTlvDictionary_GetBuffer(dictionary, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_PAYLOAD); + + TlvExtent payloadExtent = getTruthTableExtent(TRUTHTABLENAME(v1_cpi_add_route_crc32c), V1_MANIFEST_CPI_SIGBITS); + PARCBuffer *truePayload = parcBuffer_Wrap(v1_cpi_add_route_crc32c, sizeof(v1_cpi_add_route_crc32c), payloadExtent.offset, payloadExtent.offset + payloadExtent.length); + + assertTrue(parcBuffer_Equals(validationPayload, truePayload), "Buffers not equal") + { + printf("Expected\n"); + parcBuffer_Display(truePayload, 3); + printf("Got\n"); + parcBuffer_Display(validationPayload, 3); + } + + parcBuffer_Release(&truePayload); + parcBuffer_Release(&packetBuffer); + ccnxTlvDictionary_Release(&dictionary); + ccnxCodecTlvDecoder_Destroy(&decoder); +} + + +// ========================================================================= + +LONGBOW_TEST_FIXTURE(Interest) +{ + LONGBOW_RUN_TEST_CASE(Interest, interest_bad_message_length); + LONGBOW_RUN_TEST_CASE(Interest, interest_all_fields_FixedHeader); + LONGBOW_RUN_TEST_CASE(Interest, interest_all_fields_Lifetime); + LONGBOW_RUN_TEST_CASE(Interest, interest_all_fields_Name); + LONGBOW_RUN_TEST_CASE(Interest, interest_all_fields_ValidationAlg_KeyId); + LONGBOW_RUN_TEST_CASE(Interest, interest_all_fields_ValidationPayload); + LONGBOW_RUN_TEST_CASE(Interest, interest_nameA_crc32c_ValidationAlg_CryptoSuite); + LONGBOW_RUN_TEST_CASE(Interest, interest_nameA_crc32c_ValidationPayload); + LONGBOW_RUN_TEST_CASE(Interest, ccnxCodecSchemaV1PacketDecoder_BufferDecode); + LONGBOW_RUN_TEST_CASE(Interest, interest_bad_validation_alg); + LONGBOW_RUN_TEST_CASE(Interest, interest_validation_alg_overrun); +} + +LONGBOW_TEST_FIXTURE_SETUP(Interest) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Interest) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Interest, interest_bad_message_length) +{ + PARCBuffer *packetBuffer = parcBuffer_Wrap(v1_interest_bad_message_length, sizeof(v1_interest_bad_message_length), 0, sizeof(v1_interest_bad_message_length)); + + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(packetBuffer); + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + bool success = ccnxCodecSchemaV1PacketDecoder_Decode(decoder, dictionary); + assertFalse(success, "Should have seen and error on decode"); + + CCNxCodecError *error = ccnxCodecTlvDecoder_GetError(decoder); + assertNotNull(error, "Error not set when bad decode"); + + parcBuffer_Release(&packetBuffer); + ccnxTlvDictionary_Release(&dictionary); + ccnxCodecTlvDecoder_Destroy(&decoder); +} + +LONGBOW_TEST_CASE(Interest, interest_all_fields_FixedHeader) +{ + PARCBuffer *packetBuffer = parcBuffer_Wrap(v1_interest_all_fields, sizeof(v1_interest_all_fields), 0, sizeof(v1_interest_all_fields)); + + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(packetBuffer); + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + bool success = ccnxCodecSchemaV1PacketDecoder_Decode(decoder, dictionary); + assertTrue(success, "Error on decode: %s", ccnxCodecError_ToString(ccnxCodecTlvDecoder_GetError(decoder))); + + PARCBuffer *fixedHeader = ccnxTlvDictionary_GetBuffer(dictionary, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_FixedHeader); + + // leave the FixedHeader buffer at position 0 + parcBuffer_Rewind(fixedHeader); + + PARCBuffer *trueFixedHeader = parcBuffer_Wrap(v1_interest_all_fields, 8, 0, 8); + assertTrue(parcBuffer_Equals(fixedHeader, trueFixedHeader), "Buffers not equal") + { + printf("Expected\n"); + parcBuffer_Display(trueFixedHeader, 3); + printf("Got\n"); + parcBuffer_Display(fixedHeader, 3); + } + + parcBuffer_Release(&trueFixedHeader); + parcBuffer_Release(&packetBuffer); + ccnxTlvDictionary_Release(&dictionary); + ccnxCodecTlvDecoder_Destroy(&decoder); +} + +LONGBOW_TEST_CASE(Interest, interest_all_fields_Lifetime) +{ + PARCBuffer *packetBuffer = parcBuffer_Wrap(v1_interest_all_fields, sizeof(v1_interest_all_fields), 0, sizeof(v1_interest_all_fields)); + + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(packetBuffer); + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateContentObject(); + bool success = ccnxCodecSchemaV1PacketDecoder_Decode(decoder, dictionary); + assertTrue(success, "Error on decode: %s", ccnxCodecError_ToString(ccnxCodecTlvDecoder_GetError(decoder))); + + uint64_t lifetime = ccnxTlvDictionary_GetInteger(dictionary, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_InterestLifetime); + + TlvExtent lifetimeExtent = getTruthTableHeaderExtent(TRUTHTABLENAME(v1_interest_all_fields), V1_MANIFEST_INT_LIFETIME); + + parcBuffer_SetPosition(packetBuffer, lifetimeExtent.offset); + uint64_t trueTime; + ccnxCodecTlvUtilities_GetVarInt(packetBuffer, lifetimeExtent.length, &trueTime); + + assertTrue(lifetime == trueTime, "Wrong time, expected %" PRIx64 " got %" PRIx64, trueTime, lifetime); + + parcBuffer_Release(&packetBuffer); + ccnxTlvDictionary_Release(&dictionary); + ccnxCodecTlvDecoder_Destroy(&decoder); +} + +LONGBOW_TEST_CASE(Interest, interest_all_fields_Name) +{ + PARCBuffer *packetBuffer = parcBuffer_Wrap(v1_interest_all_fields, sizeof(v1_interest_all_fields), 0, sizeof(v1_interest_all_fields)); + + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(packetBuffer); + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + bool success = ccnxCodecSchemaV1PacketDecoder_Decode(decoder, dictionary); + assertTrue(success, "Error on decode: %s", ccnxCodecError_ToString(ccnxCodecTlvDecoder_GetError(decoder))); + + CCNxName *name = ccnxTlvDictionary_GetName(dictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_NAME); + CCNxName *trueName = ccnxName_CreateFromCString(v1_interest_all_fields_URI); + + assertTrue(ccnxName_Equals(name, trueName), "Buffers not equal") + { + printf("Expected\n"); + ccnxName_Display(trueName, 3); + printf("Got\n"); + ccnxName_Display(name, 3); + } + + ccnxName_Release(&trueName); + parcBuffer_Release(&packetBuffer); + ccnxTlvDictionary_Release(&dictionary); + ccnxCodecTlvDecoder_Destroy(&decoder); +} + +/* + * This packet does not have a validation section, so the test is that its missing + */ +LONGBOW_TEST_CASE(Interest, interest_all_fields_ValidationAlg_KeyId) +{ + PARCBuffer *packetBuffer = parcBuffer_Wrap(v1_interest_all_fields, sizeof(v1_interest_all_fields), 0, sizeof(v1_interest_all_fields)); + + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(packetBuffer); + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + bool success = ccnxCodecSchemaV1PacketDecoder_Decode(decoder, dictionary); + assertTrue(success, "Error on decode: %s", ccnxCodecError_ToString(ccnxCodecTlvDecoder_GetError(decoder))); + + PARCBuffer *keyid = ccnxTlvDictionary_GetBuffer(dictionary, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_KEYID); + assertNull(keyid, "Got a non-null keyid from a packet without one"); + + parcBuffer_Release(&packetBuffer); + ccnxTlvDictionary_Release(&dictionary); + ccnxCodecTlvDecoder_Destroy(&decoder); +} + +/* + * Packet does not have validation, test for missing + */ +LONGBOW_TEST_CASE(Interest, interest_all_fields_ValidationPayload) +{ + PARCBuffer *packetBuffer = parcBuffer_Wrap(v1_interest_all_fields, sizeof(v1_interest_all_fields), 0, sizeof(v1_interest_all_fields)); + + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(packetBuffer); + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + bool success = ccnxCodecSchemaV1PacketDecoder_Decode(decoder, dictionary); + assertTrue(success, "Error on decode: %s", ccnxCodecError_ToString(ccnxCodecTlvDecoder_GetError(decoder))); + + PARCBuffer *payload = ccnxTlvDictionary_GetBuffer(dictionary, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_PAYLOAD); + assertNull(payload, "Got a non-null validation payload from a packet without one"); + + parcBuffer_Release(&packetBuffer); + ccnxTlvDictionary_Release(&dictionary); + ccnxCodecTlvDecoder_Destroy(&decoder); +} + +LONGBOW_TEST_CASE(Interest, interest_nameA_crc32c_ValidationAlg_CryptoSuite) +{ + PARCBuffer *packetBuffer = parcBuffer_Wrap(v1_interest_nameA_crc32c, sizeof(v1_interest_nameA_crc32c), 0, sizeof(v1_interest_nameA_crc32c)); + + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(packetBuffer); + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + bool success = ccnxCodecSchemaV1PacketDecoder_Decode(decoder, dictionary); + assertTrue(success, "Error on decode: %s", ccnxCodecError_ToString(ccnxCodecTlvDecoder_GetError(decoder))); + + PARCCryptoSuite suite = (PARCCryptoSuite) ccnxTlvDictionary_GetInteger(dictionary, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_CRYPTO_SUITE); + PARCCryptoSuite trueSuite = PARCCryptoSuite_NULL_CRC32C; + + assertTrue(suite == trueSuite, "Wrong crypto suite, expected %d got %d", trueSuite, suite); + + parcBuffer_Release(&packetBuffer); + ccnxTlvDictionary_Release(&dictionary); + ccnxCodecTlvDecoder_Destroy(&decoder); +} + +LONGBOW_TEST_CASE(Interest, interest_nameA_crc32c_ValidationPayload) +{ + PARCBuffer *packetBuffer = parcBuffer_Wrap(v1_interest_nameA_crc32c, sizeof(v1_interest_nameA_crc32c), 0, sizeof(v1_interest_nameA_crc32c)); + + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(packetBuffer); + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + bool success = ccnxCodecSchemaV1PacketDecoder_Decode(decoder, dictionary); + assertTrue(success, "Error on decode: %s", ccnxCodecError_ToString(ccnxCodecTlvDecoder_GetError(decoder))); + + PARCBuffer *validationPayload = ccnxTlvDictionary_GetBuffer(dictionary, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_PAYLOAD); + + TlvExtent payloadExtent = getTruthTableExtent(TRUTHTABLENAME(v1_interest_nameA_crc32c), V1_MANIFEST_INT_ValidationPayload); + PARCBuffer *truePayload = parcBuffer_Wrap(v1_interest_nameA_crc32c, sizeof(v1_interest_nameA_crc32c), payloadExtent.offset, payloadExtent.offset + payloadExtent.length); + + assertTrue(parcBuffer_Equals(validationPayload, truePayload), "Buffers not equal") + { + printf("Expected\n"); + parcBuffer_Display(truePayload, 3); + printf("Got\n"); + parcBuffer_Display(validationPayload, 3); + } + + parcBuffer_Release(&truePayload); + parcBuffer_Release(&packetBuffer); + ccnxTlvDictionary_Release(&dictionary); + ccnxCodecTlvDecoder_Destroy(&decoder); +} + +LONGBOW_TEST_CASE(Interest, ccnxCodecSchemaV1PacketDecoder_BufferDecode) +{ + PARCBuffer *packetBuffer = parcBuffer_Wrap(v1_interest_nameA_crc32c, sizeof(v1_interest_nameA_crc32c), 0, sizeof(v1_interest_nameA_crc32c)); + + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + bool success = ccnxCodecSchemaV1PacketDecoder_BufferDecode(packetBuffer, dictionary); + assertTrue(success, "Error on decode"); + + PARCBuffer *validationPayload = ccnxTlvDictionary_GetBuffer(dictionary, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_PAYLOAD); + + TlvExtent payloadExtent = getTruthTableExtent(TRUTHTABLENAME(v1_interest_nameA_crc32c), V1_MANIFEST_INT_ValidationPayload); + PARCBuffer *truePayload = parcBuffer_Wrap(v1_interest_nameA_crc32c, sizeof(v1_interest_nameA_crc32c), payloadExtent.offset, payloadExtent.offset + payloadExtent.length); + + assertTrue(parcBuffer_Equals(validationPayload, truePayload), "Buffers not equal") + { + printf("Expected\n"); + parcBuffer_Display(truePayload, 3); + printf("Got\n"); + parcBuffer_Display(validationPayload, 3); + } + + parcBuffer_Release(&truePayload); + parcBuffer_Release(&packetBuffer); + ccnxTlvDictionary_Release(&dictionary); +} + +LONGBOW_TEST_CASE(Interest, interest_bad_validation_alg) +{ + PARCBuffer *packetBuffer = parcBuffer_Wrap(v1_interest_bad_validation_alg, sizeof(v1_interest_bad_validation_alg), 0, sizeof(v1_interest_bad_validation_alg)); + + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(packetBuffer); + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + bool success = ccnxCodecSchemaV1PacketDecoder_Decode(decoder, dictionary); + assertFalse(success, "Should have seen and error on decode"); + + CCNxCodecError *error = ccnxCodecTlvDecoder_GetError(decoder); + assertNotNull(error, "Error not set when bad decode"); + + parcBuffer_Release(&packetBuffer); + ccnxTlvDictionary_Release(&dictionary); + ccnxCodecTlvDecoder_Destroy(&decoder); +} + +LONGBOW_TEST_CASE(Interest, interest_validation_alg_overrun) +{ + PARCBuffer *packetBuffer = parcBuffer_Wrap(v1_interest_validation_alg_overrun, sizeof(v1_interest_validation_alg_overrun), 0, sizeof(v1_interest_validation_alg_overrun)); + + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(packetBuffer); + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + bool success = ccnxCodecSchemaV1PacketDecoder_Decode(decoder, dictionary); + assertFalse(success, "Should have seen and error on decode"); + + CCNxCodecError *error = ccnxCodecTlvDecoder_GetError(decoder); + assertNotNull(error, "Error not set when bad decode"); + + parcBuffer_Release(&packetBuffer); + ccnxTlvDictionary_Release(&dictionary); + ccnxCodecTlvDecoder_Destroy(&decoder); +} + +// ========================================================================= + + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(ccnxCodecSchemaV1_PacketDecoder); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-common/ccnx/common/codec/schema_v1/test/test_ccnxCodecSchemaV1_PacketEncoder.c b/libccnx-common/ccnx/common/codec/schema_v1/test/test_ccnxCodecSchemaV1_PacketEncoder.c new file mode 100755 index 00000000..eb79502e --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/test/test_ccnxCodecSchemaV1_PacketEncoder.c @@ -0,0 +1,1054 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../ccnxCodecSchemaV1_PacketEncoder.c" +#include <parc/algol/parc_SafeMemory.h> +#include <LongBow/unit-test.h> +#include <ccnx/common/codec/schema_v1/testdata/v1_content_nameA_keyid1_rsasha256.h> +#include <ccnx/common/codec/schema_v1/testdata/v1_interest_all_fields.h> +#include <ccnx/common/codec/schema_v1/testdata/v1_interest_nameA_crc32c.h> + +#include "testrig_encoder.c" + +#include <ccnx/common/ccnx_Interest.h> +#include <ccnx/common/ccnx_InterestReturn.h> +#include <ccnx/common/ccnx_ContentObject.h> + +#include <ccnx/common/validation/ccnxValidation_CRC32C.h> + + +// ========================================================================= + +LONGBOW_TEST_RUNNER(ccnxCodecSchemaV1_PacketEncoder) +{ + LONGBOW_RUN_TEST_FIXTURE(ContentObject); + LONGBOW_RUN_TEST_FIXTURE(Interest); + LONGBOW_RUN_TEST_FIXTURE(InterestReturn); + LONGBOW_RUN_TEST_FIXTURE(Control); + LONGBOW_RUN_TEST_FIXTURE(UnknownType); + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(ccnxCodecSchemaV1_PacketEncoder) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(ccnxCodecSchemaV1_PacketEncoder) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + + +// ========================================================================= + +LONGBOW_TEST_FIXTURE(ContentObject) +{ + LONGBOW_RUN_TEST_CASE(ContentObject, v1_content_nameA_keyid1_rsasha256); + LONGBOW_RUN_TEST_CASE(ContentObject, zero_length_payload); + LONGBOW_RUN_TEST_CASE(ContentObject, null_payload); + LONGBOW_RUN_TEST_CASE(ContentObject, no_cryptosuite); +} + +LONGBOW_TEST_FIXTURE_SETUP(ContentObject) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(ContentObject) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +/* + * Make a dictionary equivalent to v1_content_nameA_keyid1_rsasha256 and encode it then compare + */ +LONGBOW_TEST_CASE(ContentObject, v1_content_nameA_keyid1_rsasha256) +{ + CCNxName *name = ccnxName_CreateFromCString(v1_content_nameA_keyid1_rsasha256_URI); + + TlvExtent payloadExtent = + getTruthTableExtent(TRUTHTABLENAME(v1_content_nameA_keyid1_rsasha256), V1_MANIFEST_OBJ_PAYLOAD); + PARCBuffer *payload = parcBuffer_Wrap(v1_content_nameA_keyid1_rsasha256, + sizeof(v1_content_nameA_keyid1_rsasha256), + payloadExtent.offset, + payloadExtent.offset + payloadExtent.length); + + CCNxTlvDictionary *message = + ccnxContentObject_CreateWithImplAndPayload(&CCNxContentObjectFacadeV1_Implementation, + name, CCNxPayloadType_KEY, payload); + + TlvExtent fragmentExtent = + getTruthTableHeaderExtent(TRUTHTABLENAME(v1_content_nameA_keyid1_rsasha256), V1_MANIFEST_OBJ_E2EFRAG); + PARCBuffer *fragment = parcBuffer_Wrap(v1_content_nameA_keyid1_rsasha256, + sizeof(v1_content_nameA_keyid1_rsasha256), + fragmentExtent.offset, + fragmentExtent.offset + fragmentExtent.length); + ccnxTlvDictionary_PutBuffer(message, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_OBJFRAG, fragment); + + uint64_t expiryTime = 1388534400000ULL; + ccnxContentObject_SetExpiryTime(message, expiryTime); + + uint64_t endChunkNumber = 0x06050403; + ccnxContentObject_SetFinalChunkNumber(message, endChunkNumber); + + TlvExtent keyIdExtent = getTruthTableExtent(TRUTHTABLENAME(v1_content_nameA_keyid1_rsasha256), V1_MANIFEST_OBJ_KEYID); + PARCBuffer *keyid = parcBuffer_Wrap(v1_content_nameA_keyid1_rsasha256, + sizeof(v1_content_nameA_keyid1_rsasha256), + keyIdExtent.offset, + keyIdExtent.offset + keyIdExtent.length); + + TlvExtent keyExtent = getTruthTableExtent(TRUTHTABLENAME(v1_content_nameA_keyid1_rsasha256), V1_MANIFEST_OBJ_PUBKEY); + PARCBuffer *key = parcBuffer_Wrap(v1_content_nameA_keyid1_rsasha256, + sizeof(v1_content_nameA_keyid1_rsasha256), + keyExtent.offset, + keyExtent.offset + keyExtent.length); + + ccnxValidationFacadeV1_SetCryptoSuite(message, PARCCryptoSuite_RSA_SHA256); + ccnxValidationFacadeV1_SetKeyId(message, keyid); + ccnxValidationFacadeV1_SetPublicKey(message, key); + + TlvExtent sigExtent = + getTruthTableExtent(TRUTHTABLENAME(v1_content_nameA_keyid1_rsasha256), V1_MANIFEST_OBJ_SIGBITS); + PARCBuffer *sig = parcBuffer_Wrap(v1_content_nameA_keyid1_rsasha256, + sizeof(v1_content_nameA_keyid1_rsasha256), + sigExtent.offset, + sigExtent.offset + sigExtent.length); + + ccnxValidationFacadeV1_SetPayload(message, sig); + + // encode + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + ssize_t length = ccnxCodecSchemaV1PacketEncoder_Encode(encoder, message); + assertFalse(length < 0, "Got encoding error: %s", + ccnxCodecError_ToString(ccnxCodecTlvEncoder_GetError(encoder))); + assertTrue(length == sizeof(v1_content_nameA_keyid1_rsasha256), + "Wrong length, expected %zu got %zd", sizeof(v1_content_nameA_keyid1_rsasha256), length); + + // verify + PARCBuffer *truth = parcBuffer_Wrap(v1_content_nameA_keyid1_rsasha256, + sizeof(v1_content_nameA_keyid1_rsasha256), + 0, + sizeof(v1_content_nameA_keyid1_rsasha256)); + ccnxCodecTlvEncoder_Finalize(encoder); + PARCBuffer *test = ccnxCodecTlvEncoder_CreateBuffer(encoder); + assertTrue(parcBuffer_Equals(test, truth), "Buffers mismatch") + { + printf("Expected\n"); + parcBuffer_Display(truth, 3); + printf("Got\n"); + parcBuffer_Display(test, 3); + + uint8_t *truthOverlay = parcBuffer_Overlay(truth, 0); + uint8_t *testOverlay = parcBuffer_Overlay(test, 0); + + for (ssize_t i = 0; i < length; i++) { + if (truthOverlay[i] != testOverlay[i]) { + printf("Buffers differ at byte %zd, expected 0x%02x got 0x%02x\n", i, truthOverlay[i], testOverlay[i]); + break; + } + } + } + + // cleanup + parcBuffer_Release(&truth); + parcBuffer_Release(&test); + ccnxCodecTlvEncoder_Destroy(&encoder); + parcBuffer_Release(&sig); + parcBuffer_Release(&key); + parcBuffer_Release(&keyid); + parcBuffer_Release(&payload); + parcBuffer_Release(&fragment); + ccnxName_Release(&name); + ccnxTlvDictionary_Release(&message); +} + + +LONGBOW_TEST_CASE(ContentObject, zero_length_payload) +{ + CCNxName *name = ccnxName_CreateFromCString("lci:/no/payload"); + PARCBuffer *payload = parcBuffer_Allocate(0); + + CCNxTlvDictionary *message = + ccnxContentObject_CreateWithImplAndPayload(&CCNxContentObjectFacadeV1_Implementation, + name, CCNxPayloadType_DATA, payload); + + // encode + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + ssize_t length = ccnxCodecSchemaV1PacketEncoder_Encode(encoder, message); + assertFalse(length < 0, "Got encoding error: %s", ccnxCodecError_ToString(ccnxCodecTlvEncoder_GetError(encoder))); + + ccnxCodecTlvEncoder_Finalize(encoder); + PARCBuffer *encoded = ccnxCodecTlvEncoder_CreateBuffer(encoder); + assertNotNull(encoded, "got null output buffer"); + + parcBuffer_Display(encoded, 3); + + parcBuffer_Release(&encoded); + ccnxCodecTlvEncoder_Destroy(&encoder); + ccnxTlvDictionary_Release(&message); + parcBuffer_Release(&payload); + ccnxName_Release(&name); +} + +LONGBOW_TEST_CASE(ContentObject, null_payload) +{ + CCNxName *name = ccnxName_CreateFromCString("lci:/no/payload"); + + CCNxTlvDictionary *message = + ccnxContentObject_CreateWithImplAndPayload(&CCNxContentObjectFacadeV1_Implementation, + name, CCNxPayloadType_DATA, NULL); + // encode + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + ssize_t length = ccnxCodecSchemaV1PacketEncoder_Encode(encoder, message); + assertFalse(length < 0, "Got encoding error: %s", ccnxCodecError_ToString(ccnxCodecTlvEncoder_GetError(encoder))); + + ccnxCodecTlvEncoder_Finalize(encoder); + PARCBuffer *encoded = ccnxCodecTlvEncoder_CreateBuffer(encoder); + assertNotNull(encoded, "got null output buffer"); + + parcBuffer_Release(&encoded); + ccnxCodecTlvEncoder_Destroy(&encoder); + ccnxTlvDictionary_Release(&message); + ccnxName_Release(&name); +} + +/* + * A content object without a cryptosuite should not be signed + */ +LONGBOW_TEST_CASE(ContentObject, no_cryptosuite) +{ + CCNxName *name = ccnxName_CreateFromCString("lci:/no/payload"); + + CCNxTlvDictionary *message = + ccnxContentObject_CreateWithImplAndPayload(&CCNxContentObjectFacadeV1_Implementation, + name, CCNxPayloadType_DATA, NULL); + // encode + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + ssize_t length = ccnxCodecSchemaV1PacketEncoder_Encode(encoder, message); + assertFalse(length < 0, "Got encoding error: %s", ccnxCodecError_ToString(ccnxCodecTlvEncoder_GetError(encoder))); + + ccnxCodecTlvEncoder_Finalize(encoder); + PARCBuffer *encoded = ccnxCodecTlvEncoder_CreateBuffer(encoder); + assertNotNull(encoded, "got null output buffer"); + + // it should be 33 bytes without a signature + assertTrue(parcBuffer_Remaining(encoded) == 33, "Wrong length exepcted 33 got %zu", parcBuffer_Remaining(encoded)); + + parcBuffer_Release(&encoded); + ccnxCodecTlvEncoder_Destroy(&encoder); + ccnxTlvDictionary_Release(&message); + ccnxName_Release(&name); +} + +// ========================================================================= + +LONGBOW_TEST_FIXTURE(Interest) +{ + LONGBOW_RUN_TEST_CASE(Interest, v1_interest_nameA_crc32c); + LONGBOW_RUN_TEST_CASE(Interest, v1_interest_nameA_crc32c_IoVec); +} + +LONGBOW_TEST_FIXTURE_SETUP(Interest) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Interest) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +/* + * Make an interest equivalent to v1_interest_nameA_crc32c and encode it + */ +LONGBOW_TEST_CASE(Interest, v1_interest_nameA_crc32c) +{ + CCNxName *name = ccnxName_CreateFromCString(v1_interest_nameA_crc32c_URI); + + CCNxTlvDictionary *message = + ccnxInterest_CreateWithImpl(&CCNxInterestFacadeV1_Implementation, + name, CCNxInterestDefault_LifetimeMilliseconds, NULL, NULL, 32); + + TlvExtent fragmentExtent = getTruthTableHeaderExtent(TRUTHTABLENAME(v1_interest_nameA_crc32c), V1_MANIFEST_INT_E2EFRAG); + PARCBuffer *fragment = parcBuffer_Wrap(v1_interest_nameA_crc32c, sizeof(v1_interest_nameA_crc32c), fragmentExtent.offset, fragmentExtent.offset + fragmentExtent.length); + ccnxTlvDictionary_PutBuffer(message, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_INTFRAG, fragment); + + ccnxValidationFacadeV1_SetCryptoSuite(message, PARCCryptoSuite_NULL_CRC32C); + + TlvExtent sigExtent = getTruthTableExtent(TRUTHTABLENAME(v1_interest_nameA_crc32c), V1_MANIFEST_INT_ValidationPayload); + PARCBuffer *sig = parcBuffer_Wrap(v1_interest_nameA_crc32c, sizeof(v1_interest_nameA_crc32c), sigExtent.offset, sigExtent.offset + sigExtent.length); + + ccnxValidationFacadeV1_SetPayload(message, sig); + + // encode + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + ssize_t length = ccnxCodecSchemaV1PacketEncoder_Encode(encoder, message); + assertFalse(length < 0, "Got encoding error: %s", ccnxCodecError_ToString(ccnxCodecTlvEncoder_GetError(encoder))); + assertTrue(length == sizeof(v1_interest_nameA_crc32c), "Wrong length, expected %zu got %zd", sizeof(v1_interest_nameA_crc32c), length); + + // verify + PARCBuffer *truth = parcBuffer_Wrap(v1_interest_nameA_crc32c, sizeof(v1_interest_nameA_crc32c), 0, sizeof(v1_interest_nameA_crc32c)); + ccnxCodecTlvEncoder_Finalize(encoder); + PARCBuffer *test = ccnxCodecTlvEncoder_CreateBuffer(encoder); + assertTrue(parcBuffer_Equals(test, truth), "Buffers mismatch") + { + printf("Expected\n"); + parcBuffer_Display(truth, 3); + printf("Got\n"); + parcBuffer_Display(test, 3); + + uint8_t *truthOverlay = parcBuffer_Overlay(truth, 0); + uint8_t *testOverlay = parcBuffer_Overlay(test, 0); + + for (ssize_t i = 0; i < length; i++) { + if (truthOverlay[i] != testOverlay[i]) { + printf("Buffers differ at byte %zd, expected 0x%02x got 0x%02x", i, truthOverlay[i], testOverlay[i]); + break; + } + } + } + + // cleanup + parcBuffer_Release(&truth); + parcBuffer_Release(&test); + ccnxCodecTlvEncoder_Destroy(&encoder); + parcBuffer_Release(&sig); + parcBuffer_Release(&fragment); + ccnxName_Release(&name); + ccnxTlvDictionary_Release(&message); +} + +/* + * Make an interest equivalent to v1_interest_nameA_crc32c and encode it + */ +LONGBOW_TEST_CASE(Interest, v1_interest_nameA_crc32c_IoVec) +{ + CCNxName *name = ccnxName_CreateFromCString(v1_interest_nameA_crc32c_URI); + + CCNxTlvDictionary *message = + ccnxInterest_CreateWithImpl(&CCNxInterestFacadeV1_Implementation, + name, CCNxInterestDefault_LifetimeMilliseconds, NULL, NULL, 32); + + TlvExtent fragmentExtent = getTruthTableHeaderExtent(TRUTHTABLENAME(v1_interest_nameA_crc32c), V1_MANIFEST_INT_E2EFRAG); + PARCBuffer *fragment = parcBuffer_Wrap(v1_interest_nameA_crc32c, sizeof(v1_interest_nameA_crc32c), fragmentExtent.offset, fragmentExtent.offset + fragmentExtent.length); + ccnxTlvDictionary_PutBuffer(message, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_INTFRAG, fragment); + + ccnxValidationFacadeV1_SetCryptoSuite(message, PARCCryptoSuite_NULL_CRC32C); + + TlvExtent sigExtent = getTruthTableExtent(TRUTHTABLENAME(v1_interest_nameA_crc32c), V1_MANIFEST_INT_ValidationPayload); + PARCBuffer *sig = parcBuffer_Wrap(v1_interest_nameA_crc32c, sizeof(v1_interest_nameA_crc32c), sigExtent.offset, sigExtent.offset + sigExtent.length); + + ccnxValidationFacadeV1_SetPayload(message, sig); + + // encode + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + CCNxCodecNetworkBufferIoVec *iovec = ccnxCodecSchemaV1PacketEncoder_DictionaryEncode(message, NULL); + assertNotNull(iovec, "Got null iovec from ccnxCodecSchemaV1PacketEncoder_DictionaryEncode"); + + size_t length = ccnxCodecNetworkBufferIoVec_Length(iovec); + assertTrue(length == sizeof(v1_interest_nameA_crc32c), "Wrong length, expected %zu got %zd", sizeof(v1_interest_nameA_crc32c), length); + + // verify + PARCBuffer *truth = parcBuffer_Wrap(v1_interest_nameA_crc32c, sizeof(v1_interest_nameA_crc32c), 0, sizeof(v1_interest_nameA_crc32c)); + + int iovecCount = ccnxCodecNetworkBufferIoVec_GetCount(iovec); + PARCBuffer *test = parcBuffer_Allocate(length); + const struct iovec *array = ccnxCodecNetworkBufferIoVec_GetArray(iovec); + for (int i = 0; i < iovecCount; i++) { + parcBuffer_PutArray(test, array[i].iov_len, array[i].iov_base); + } + parcBuffer_Flip(test); + + assertTrue(parcBuffer_Equals(test, truth), "Buffers mismatch") + { + printf("Expected\n"); + parcBuffer_Display(truth, 3); + printf("Got\n"); + parcBuffer_Display(test, 3); + + uint8_t *truthOverlay = parcBuffer_Overlay(truth, 0); + uint8_t *testOverlay = parcBuffer_Overlay(test, 0); + + for (ssize_t i = 0; i < length; i++) { + if (truthOverlay[i] != testOverlay[i]) { + printf("Buffers differ at byte %zd, expected 0x%02x got 0x%02x", i, truthOverlay[i], testOverlay[i]); + break; + } + } + } + + // cleanup + ccnxCodecNetworkBufferIoVec_Release(&iovec); + parcBuffer_Release(&truth); + parcBuffer_Release(&test); + ccnxCodecTlvEncoder_Destroy(&encoder); + parcBuffer_Release(&sig); + parcBuffer_Release(&fragment); + ccnxName_Release(&name); + ccnxTlvDictionary_Release(&message); +} + +// ========================================================================= + +LONGBOW_TEST_FIXTURE(InterestReturn) +{ + LONGBOW_RUN_TEST_CASE(InterestReturn, v1_interest_return); +} + +LONGBOW_TEST_FIXTURE_SETUP(InterestReturn) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(InterestReturn) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +/* + * Make an interest return and encode it + */ +LONGBOW_TEST_CASE(InterestReturn, v1_interest_return) +{ + CCNxName *name = ccnxName_CreateFromCString(v1_interest_nameA_crc32c_URI); + + CCNxInterest *interest = + ccnxInterest_CreateWithImpl(&CCNxInterestFacadeV1_Implementation, + name, CCNxInterestDefault_LifetimeMilliseconds, NULL, NULL, 32); + ccnxName_Release(&name); + + TlvExtent fragmentExtent = getTruthTableHeaderExtent(TRUTHTABLENAME(v1_interest_nameA_crc32c), V1_MANIFEST_INT_E2EFRAG); + PARCBuffer *fragment = parcBuffer_Wrap(v1_interest_nameA_crc32c, sizeof(v1_interest_nameA_crc32c), fragmentExtent.offset, fragmentExtent.offset + fragmentExtent.length); + ccnxTlvDictionary_PutBuffer(interest, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_INTFRAG, fragment); + parcBuffer_Release(&fragment); + + ccnxValidationFacadeV1_SetCryptoSuite(interest, PARCCryptoSuite_NULL_CRC32C); + + TlvExtent sigExtent = getTruthTableExtent(TRUTHTABLENAME(v1_interest_nameA_crc32c), V1_MANIFEST_INT_ValidationPayload); + PARCBuffer *sig = parcBuffer_Wrap(v1_interest_nameA_crc32c, sizeof(v1_interest_nameA_crc32c), sigExtent.offset, sigExtent.offset + sigExtent.length); + + ccnxValidationFacadeV1_SetPayload(interest, sig); + parcBuffer_Release(&sig); + + CCNxTlvDictionary *message = + ccnxInterestReturn_Create(interest, CCNxInterestReturn_ReturnCode_NoResources); + ccnxInterest_Release(&interest); + + // encode + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + ssize_t length = ccnxCodecSchemaV1PacketEncoder_Encode(encoder, message); + ssize_t expectedLength = sizeof(v1_interest_nameA_crc32c); + assertFalse(length < 0, "Got encoding error: %s", ccnxCodecError_ToString(ccnxCodecTlvEncoder_GetError(encoder))); + assertTrue(length == expectedLength, "Wrong length, expected %zu got %zd", expectedLength, length); + + // verify + PARCBuffer *truth = parcBuffer_Wrap(v1_interest_nameA_crc32c_returned, sizeof(v1_interest_nameA_crc32c_returned), 0, sizeof(v1_interest_nameA_crc32c_returned)); + ccnxCodecTlvEncoder_Finalize(encoder); + PARCBuffer *test = ccnxCodecTlvEncoder_CreateBuffer(encoder); + assertTrue(parcBuffer_Equals(test, truth), "Buffers mismatch") + { + printf("Expected\n"); + parcBuffer_Display(truth, 3); + printf("Got\n"); + parcBuffer_Display(test, 3); + + uint8_t *truthOverlay = parcBuffer_Overlay(truth, 0); + uint8_t *testOverlay = parcBuffer_Overlay(test, 0); + + for (ssize_t i = 0; i < length; i++) { + if (truthOverlay[i] != testOverlay[i]) { + printf("Buffers differ at byte %zd, expected 0x%02x got 0x%02x\n", i, truthOverlay[i], testOverlay[i]); + } + } + } + + // cleanup + parcBuffer_Release(&truth); + parcBuffer_Release(&test); + ccnxCodecTlvEncoder_Destroy(&encoder); + ccnxTlvDictionary_Release(&message); +} + +// ========================================================================= + +LONGBOW_TEST_FIXTURE(Control) +{ + LONGBOW_RUN_TEST_CASE(Control, payload); + LONGBOW_RUN_TEST_CASE(Control, cryptosuite); +} + +LONGBOW_TEST_FIXTURE_SETUP(Control) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Control) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Control, payload) +{ + uint8_t encoded[] = { + 0x01, 0xa4, 0x00, 0x22, + 0x00, 0x00, 0x00, 0x08, + + 0xbe, 0xef, 0x00, 0x16, // control message + + 0x7b, 0x22, 0x74, 0x68, // {"th + 0x69, 0x73, 0x20, 0x69, // is i + 0x73, 0x22, 0x3a, 0x22, // s":" + 0x61, 0x6e, 0x6e, 0x6f, // anno + 0x79, 0x69, 0x6e, 0x67, // ying + 0x22, 0x7d // "} + }; + + + CCNxTlvDictionary *message = ccnxCodecSchemaV1TlvDictionary_CreateControl(); + + PARCJSON *json = parcJSON_Create(); + parcJSON_AddString(json, "this is", "annoying"); + + ccnxTlvDictionary_PutJson(message, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_PAYLOAD, json); + parcJSON_Release(&json); + + // encode + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + ssize_t length = ccnxCodecSchemaV1PacketEncoder_Encode(encoder, message); + + assertFalse(length < 0, "Got encoding error: %s", ccnxCodecError_ToString(ccnxCodecTlvEncoder_GetError(encoder))); + assertTrue(length == sizeof(encoded), "Wrong length, expected %zu got %zd", sizeof(encoded), length); + + // verify + ccnxCodecTlvEncoder_Finalize(encoder); + PARCBuffer *test = ccnxCodecTlvEncoder_CreateBuffer(encoder); + PARCBuffer *truth = parcBuffer_Wrap(encoded, sizeof(encoded), 0, sizeof(encoded)); + + assertTrue(parcBuffer_Equals(test, truth), "Buffers mismatch") + { + printf("Expected\n"); + parcBuffer_Display(truth, 3); + printf("Got\n"); + parcBuffer_Display(test, 3); + + uint8_t *truthOverlay = parcBuffer_Overlay(truth, 0); + uint8_t *testOverlay = parcBuffer_Overlay(test, 0); + + for (ssize_t i = 0; i < length; i++) { + if (truthOverlay[i] != testOverlay[i]) { + printf("Buffers differ at byte %zd, expected 0x%02x got 0x%02x", i, truthOverlay[i], testOverlay[i]); + break; + } + } + } + + parcBuffer_Release(&test); + ccnxCodecTlvEncoder_Destroy(&encoder); + ccnxTlvDictionary_Release(&message); + parcBuffer_Release(&truth); +} + +LONGBOW_TEST_CASE(Control, cryptosuite) +{ + uint8_t encoded[] = { + 0x01, 0xA4, 0x00, 16, + 0x00, 0x00, 0x00, 8, + 0xBE, 0xEF, 0x00, 4, + 'a', 'b', 'c', 'd', + 0x00, 0x03, 0x00, 4, + 0x00, 0x02, 0x00, 0 + }; + + PARCBuffer *truth = parcBuffer_Wrap(encoded, sizeof(encoded), 0, sizeof(encoded)); + PARCBuffer *payload = parcBuffer_Wrap(encoded, sizeof(encoded), 12, 16); + + CCNxTlvDictionary *message = ccnxCodecSchemaV1TlvDictionary_CreateControl(); + ccnxTlvDictionary_PutBuffer(message, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_PAYLOAD, payload); + ccnxValidationCRC32C_Set(message); + + ccnxValidationCRC32C_Set(message); + + // encode + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + PARCSigner *signer = ccnxValidationCRC32C_CreateSigner(); + ccnxCodecTlvEncoder_SetSigner(encoder, signer); + + ssize_t length = ccnxCodecSchemaV1PacketEncoder_Encode(encoder, message); + assertFalse(length < 0, "Got encoding error: %s", ccnxCodecError_ToString(ccnxCodecTlvEncoder_GetError(encoder))); + + ccnxCodecTlvEncoder_Finalize(encoder); + PARCBuffer *test = ccnxCodecTlvEncoder_CreateBuffer(encoder); + assertNotNull(test, "Got null buffer from encoder"); + uint8_t testSuite = parcBuffer_GetAtIndex(test, 21); + assertTrue(testSuite == 2, "Wrong cryptosuite, expected 2 got %u", testSuite); + + parcSigner_Release(&signer); + parcBuffer_Release(&test); + ccnxCodecTlvEncoder_Destroy(&encoder); + ccnxTlvDictionary_Release(&message); + parcBuffer_Release(&payload); + parcBuffer_Release(&truth); +} + +// ========================================================================= + +LONGBOW_TEST_FIXTURE(UnknownType) +{ + LONGBOW_RUN_TEST_CASE(UnknownType, unknown); +} + +LONGBOW_TEST_FIXTURE_SETUP(UnknownType) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(UnknownType) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +/* + * try to encode a message with unknown message type + */ +LONGBOW_TEST_CASE(UnknownType, unknown) +{ + // this initializes the dictionary to unknown type + CCNxTlvDictionary *message = ccnxTlvDictionary_Create(20, 20); + + // encode + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + ssize_t length = ccnxCodecSchemaV1PacketEncoder_Encode(encoder, message); + assertTrue(length < 0, "Did not get error condition for unknown type"); + + CCNxCodecError *error = ccnxCodecTlvEncoder_GetError(encoder); + assertNotNull(error, "Did not get an error for invalid encoding"); + + ccnxCodecTlvEncoder_Destroy(&encoder); + ccnxTlvDictionary_Release(&message); +} + +// ========================================================================= + +LONGBOW_TEST_FIXTURE(Local) +{ + LONGBOW_RUN_TEST_CASE(Local, _getHopLimit_Present); + LONGBOW_RUN_TEST_CASE(Local, _getHopLimit_Missing); + LONGBOW_RUN_TEST_CASE(Local, _encodeFixedHeader_ContentObject); + LONGBOW_RUN_TEST_CASE(Local, _encodeFixedHeader_Interest); + LONGBOW_RUN_TEST_CASE(Local, _encodeOptionalHeaders); + LONGBOW_RUN_TEST_CASE(Local, _encodeMessage_Interest); + LONGBOW_RUN_TEST_CASE(Local, _encodeMessage_ContentObject); + LONGBOW_RUN_TEST_CASE(Local, _encodeCPI); + LONGBOW_RUN_TEST_CASE(Local, _encodeMessage_Unknown); + LONGBOW_RUN_TEST_CASE(Local, _encodeValidationAlg_Present); + LONGBOW_RUN_TEST_CASE(Local, _encodeValidationAlg_Missing); + LONGBOW_RUN_TEST_CASE(Local, _encodeValidationPayload_Present); + LONGBOW_RUN_TEST_CASE(Local, _encodeValidationPayload_Missing); +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Local, _getHopLimit_Present) +{ + uint8_t hoplimit = 77; + CCNxTlvDictionary *dict = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + ccnxTlvDictionary_PutInteger(dict, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_HOPLIMIT, hoplimit); + + uint8_t test = _getHopLimit(dict); + assertTrue(test == hoplimit, "Got wrong hoplimit, expected %u got %u", hoplimit, test); + ccnxTlvDictionary_Release(&dict); +} + +LONGBOW_TEST_CASE(Local, _getHopLimit_Missing) +{ + CCNxTlvDictionary *dict = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + + uint8_t test = _getHopLimit(dict); + assertTrue(test == CCNxInterestDefault_HopLimit, "Got wrong hoplimit, expected %u got %u", CCNxInterestDefault_HopLimit, test); + ccnxTlvDictionary_Release(&dict); +} + +LONGBOW_TEST_CASE(Local, _encodeFixedHeader_ContentObject) +{ + uint8_t encoded[] = { + 0x01, 0x01, 0x00, 100, // ver = 1, type = content object, length = 100 + 0x00, 0x00, 0x00, 14, // reserved = 0x0000000, header length = 14 + }; + PARCBuffer *truth = parcBuffer_Wrap(encoded, sizeof(encoded), 0, sizeof(encoded)); + + CCNxTlvDictionary *dict = ccnxCodecSchemaV1TlvDictionary_CreateContentObject(); + + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + ssize_t length = _encodeFixedHeader(encoder, dict, CCNxCodecSchemaV1Types_PacketType_ContentObject, 14, 100); + assertTrue(length == 8, "wrong length, expected %d got %zd", 8, length); + + ccnxCodecTlvEncoder_Finalize(encoder); + PARCBuffer *test = ccnxCodecTlvEncoder_CreateBuffer(encoder); + assertTrue(parcBuffer_Equals(test, truth), "Buffers mismatch") + { + printf("Expected\n"); + parcBuffer_Display(truth, 3); + printf("Got\n"); + parcBuffer_Display(test, 3); + } + + parcBuffer_Release(&truth); + parcBuffer_Release(&test); + ccnxTlvDictionary_Release(&dict); + ccnxCodecTlvEncoder_Destroy(&encoder); +} + +LONGBOW_TEST_CASE(Local, _encodeFixedHeader_Interest) +{ + uint8_t encoded[] = { + 0x01, 0x00, 0x00, 100, // ver = 1, type = interest, length = 100 + 0x1f, 0x00, 0x00, 14, // hoplimit = 31, reserved = 0x0000, header length = 14 + }; + PARCBuffer *truth = parcBuffer_Wrap(encoded, sizeof(encoded), 0, sizeof(encoded)); + + CCNxTlvDictionary *dict = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + ccnxTlvDictionary_PutInteger(dict, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_HOPLIMIT, 31); + + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + ssize_t length = _encodeFixedHeader(encoder, dict, CCNxCodecSchemaV1Types_PacketType_Interest, 14, 100); + assertTrue(length == 8, "wrong length, expected %d got %zd", 8, length); + + ccnxCodecTlvEncoder_Finalize(encoder); + PARCBuffer *test = ccnxCodecTlvEncoder_CreateBuffer(encoder); + assertTrue(parcBuffer_Equals(test, truth), "Buffers mismatch") + { + printf("Expected\n"); + parcBuffer_Display(truth, 3); + printf("Got\n"); + parcBuffer_Display(test, 3); + } + + parcBuffer_Release(&truth); + parcBuffer_Release(&test); + ccnxTlvDictionary_Release(&dict); + ccnxCodecTlvEncoder_Destroy(&encoder); +} + +LONGBOW_TEST_CASE(Local, _encodeOptionalHeaders) +{ + uint8_t encoded[] = { + 0x00, 0x01, 0x00, 2, // Interest Lifetime (2 bytes) + 0xEA, 0xEB, + }; + PARCBuffer *truth = parcBuffer_Wrap(encoded, sizeof(encoded), 0, sizeof(encoded)); + + CCNxTlvDictionary *dict = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + ccnxTlvDictionary_PutInteger(dict, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_InterestLifetime, 0xEAEB); + + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + ssize_t length = _encodeOptionalHeaders(encoder, dict); + assertTrue(length == sizeof(encoded), "wrong length, expected %zu got %zd", sizeof(encoded), length); + + ccnxCodecTlvEncoder_Finalize(encoder); + PARCBuffer *test = ccnxCodecTlvEncoder_CreateBuffer(encoder); + assertTrue(parcBuffer_Equals(test, truth), "Buffers mismatch") + { + printf("Expected\n"); + parcBuffer_Display(truth, 3); + printf("Got\n"); + parcBuffer_Display(test, 3); + } + + parcBuffer_Release(&truth); + parcBuffer_Release(&test); + ccnxTlvDictionary_Release(&dict); + ccnxCodecTlvEncoder_Destroy(&encoder); +} + +LONGBOW_TEST_CASE(Local, _encodeMessage_Interest) +{ + uint8_t encoded[] = { + 0x00, 0x01, 0x00, 13, + 0x00, 0x00, 0x00, 9, + 0x00, CCNxNameLabelType_NAME, 0x00, 5, + 'p', 'o', 'p', 'p', 'y' + }; + PARCBuffer *truth = parcBuffer_Wrap(encoded, sizeof(encoded), 0, sizeof(encoded)); + + CCNxName *name = ccnxName_CreateFromCString("lci:/poppy"); + CCNxTlvDictionary *dict = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + ccnxTlvDictionary_PutName(dict, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_NAME, name); + + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + + CCNxCodecSchemaV1Types_PacketType packetType; + ssize_t length = _encodeMessage(encoder, dict, &packetType); + assertTrue(length == sizeof(encoded), "wrong length, expected %zu got %zd", sizeof(encoded), length); + assertTrue(packetType == CCNxCodecSchemaV1Types_PacketType_Interest, "Wrong packet type, expected %d got %d", + CCNxCodecSchemaV1Types_PacketType_Interest, packetType); + + ccnxCodecTlvEncoder_Finalize(encoder); + PARCBuffer *test = ccnxCodecTlvEncoder_CreateBuffer(encoder); + assertTrue(parcBuffer_Equals(test, truth), "Buffers mismatch") + { + printf("Expected\n"); + parcBuffer_Display(truth, 3); + printf("Got\n"); + parcBuffer_Display(test, 3); + } + + parcBuffer_Release(&truth); + parcBuffer_Release(&test); + ccnxName_Release(&name); + ccnxTlvDictionary_Release(&dict); + ccnxCodecTlvEncoder_Destroy(&encoder); +} + +LONGBOW_TEST_CASE(Local, _encodeMessage_ContentObject) +{ + uint8_t encoded[] = { + 0x00, 0x02, 0x00, 13, + 0x00, 0x00, 0x00, 9, + 0x00, CCNxNameLabelType_NAME, 0x00, 5, + 'p', 'o', 'p', 'p', 'y' + }; + PARCBuffer *truth = parcBuffer_Wrap(encoded, sizeof(encoded), 0, sizeof(encoded)); + + CCNxName *name = ccnxName_CreateFromCString("lci:/poppy"); + CCNxTlvDictionary *dict = ccnxCodecSchemaV1TlvDictionary_CreateContentObject(); + ccnxTlvDictionary_PutName(dict, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_NAME, name); + + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + + CCNxCodecSchemaV1Types_PacketType packetType; + ssize_t length = _encodeMessage(encoder, dict, &packetType); + assertTrue(length == sizeof(encoded), "wrong length, expected %zu got %zd", sizeof(encoded), length); + assertTrue(packetType == CCNxCodecSchemaV1Types_PacketType_ContentObject, "Wrong packet type, expected %d got %d", + CCNxCodecSchemaV1Types_PacketType_ContentObject, packetType); + + ccnxCodecTlvEncoder_Finalize(encoder); + PARCBuffer *test = ccnxCodecTlvEncoder_CreateBuffer(encoder); + assertTrue(parcBuffer_Equals(test, truth), "Buffers mismatch") + { + printf("Expected\n"); + parcBuffer_Display(truth, 3); + printf("Got\n"); + parcBuffer_Display(test, 3); + } + + parcBuffer_Release(&truth); + parcBuffer_Release(&test); + ccnxName_Release(&name); + ccnxTlvDictionary_Release(&dict); + ccnxCodecTlvEncoder_Destroy(&encoder); +} + +LONGBOW_TEST_CASE(Local, _encodeCPI) +{ + uint8_t encoded [] = { + 0x00, 0x02, 0x03, 0x99, // + }; + PARCBuffer *truth = parcBuffer_Wrap(encoded, sizeof(encoded), 0, sizeof(encoded)); + + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + CCNxTlvDictionary *dict = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + ccnxValidationCRC32C_Set(dict); + ccnxTlvDictionary_PutBuffer(dict, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_PAYLOAD, truth); + + ssize_t length = _encodeCPI(encoder, dict); + assertTrue(length == sizeof(encoded), "wrong length, expected %zu got %zd", sizeof(encoded), length); + + ccnxCodecTlvEncoder_Finalize(encoder); + PARCBuffer *test = ccnxCodecTlvEncoder_CreateBuffer(encoder); + assertTrue(parcBuffer_Equals(test, truth), "Buffers mismatch") + { + printf("Expected\n"); + parcBuffer_Display(truth, 3); + printf("Got\n"); + parcBuffer_Display(test, 3); + } + + parcBuffer_Release(&truth); + parcBuffer_Release(&test); + ccnxTlvDictionary_Release(&dict); + ccnxCodecTlvEncoder_Destroy(&encoder); +} + +/* + * This test requires that we set the message type to some unknown value, which we get if + * we create a dictionary with ccnxTlvDictionary_Create() and dont call anything to set + * the message type. It will be "CCNxTlvDictionary_Unknown". + */ +LONGBOW_TEST_CASE(Local, _encodeMessage_Unknown) +{ + CCNxTlvDictionary *dict = ccnxTlvDictionary_Create(20, 20); + + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + + CCNxCodecSchemaV1Types_PacketType packetType; + ssize_t length = _encodeMessage(encoder, dict, &packetType); + assertTrue(length < 0, "wrong length, expected negative got %zd", length); + + CCNxCodecError *error = ccnxCodecTlvEncoder_GetError(encoder); + assertNotNull(error, "Got null error when an error condition should have been set"); + + ccnxTlvDictionary_Release(&dict); + ccnxCodecTlvEncoder_Destroy(&encoder); +} + +LONGBOW_TEST_CASE(Local, _encodeValidationAlg_Present) +{ + uint8_t encoded [] = { + 0x00, 0x03, 0x00, 4, // validation alg, length = 4 + 0x00, 0x02, 0x00, 0x00, // CRC32C + }; + PARCBuffer *truth = parcBuffer_Wrap(encoded, sizeof(encoded), 0, sizeof(encoded)); + PARCBuffer *truePayload = parcBuffer_Wrap(encoded, sizeof(encoded), 4, sizeof(encoded)); + + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + CCNxTlvDictionary *dict = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + ccnxValidationCRC32C_Set(dict); + ccnxTlvDictionary_PutBuffer(dict, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_PAYLOAD, truePayload); + + ssize_t length = _encodeValidationAlg(encoder, dict); + assertTrue(length == sizeof(encoded), "wrong length, expected %zu got %zd", sizeof(encoded), length); + + ccnxCodecTlvEncoder_Finalize(encoder); + PARCBuffer *test = ccnxCodecTlvEncoder_CreateBuffer(encoder); + assertTrue(parcBuffer_Equals(test, truth), "Buffers mismatch") + { + printf("Expected\n"); + parcBuffer_Display(truth, 3); + printf("Got\n"); + parcBuffer_Display(test, 3); + } + + parcBuffer_Release(&truth); + parcBuffer_Release(&truePayload); + parcBuffer_Release(&test); + ccnxTlvDictionary_Release(&dict); + ccnxCodecTlvEncoder_Destroy(&encoder); +} + +LONGBOW_TEST_CASE(Local, _encodeValidationAlg_Missing) +{ + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + CCNxTlvDictionary *dict = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + + ssize_t length = _encodeValidationAlg(encoder, dict); + assertTrue(length == 0, "wrong length, expected %d got %zd", 0, length); + + ccnxTlvDictionary_Release(&dict); + ccnxCodecTlvEncoder_Destroy(&encoder); +} + +LONGBOW_TEST_CASE(Local, _encodeValidationPayload_Present) +{ + uint8_t encoded [] = { + 0x00, 0x04, 0x00, 4, // validation payload, length = 4 + 0x00, 0x02, 0x03, 0x99, // + }; + PARCBuffer *truth = parcBuffer_Wrap(encoded, sizeof(encoded), 0, sizeof(encoded)); + PARCBuffer *truePayload = parcBuffer_Wrap(encoded, sizeof(encoded), 4, sizeof(encoded)); + + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + CCNxTlvDictionary *dict = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + ccnxValidationCRC32C_Set(dict); + ccnxTlvDictionary_PutBuffer(dict, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_PAYLOAD, truePayload); + + ssize_t length = _encodeValidationPayload(encoder, dict); + assertTrue(length == sizeof(encoded), "wrong length, expected %zu got %zd", sizeof(encoded), length); + + ccnxCodecTlvEncoder_Finalize(encoder); + PARCBuffer *test = ccnxCodecTlvEncoder_CreateBuffer(encoder); + assertTrue(parcBuffer_Equals(test, truth), "Buffers mismatch") + { + printf("Expected\n"); + parcBuffer_Display(truth, 3); + printf("Got\n"); + parcBuffer_Display(test, 3); + } + + parcBuffer_Release(&truth); + parcBuffer_Release(&truePayload); + parcBuffer_Release(&test); + ccnxTlvDictionary_Release(&dict); + ccnxCodecTlvEncoder_Destroy(&encoder); +} + +LONGBOW_TEST_CASE(Local, _encodeValidationPayload_Missing) +{ + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + CCNxTlvDictionary *dict = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + + ssize_t length = _encodeValidationPayload(encoder, dict); + assertTrue(length == 0, "wrong length, expected %d got %zd", 0, length); + + ccnxTlvDictionary_Release(&dict); + ccnxCodecTlvEncoder_Destroy(&encoder); +} + + +// ================================================================================== + + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(ccnxCodecSchemaV1_PacketEncoder); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-common/ccnx/common/codec/schema_v1/test/test_ccnxCodecSchemaV1_TlvDictionary.c b/libccnx-common/ccnx/common/codec/schema_v1/test/test_ccnxCodecSchemaV1_TlvDictionary.c new file mode 100755 index 00000000..ccaa8b13 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/test/test_ccnxCodecSchemaV1_TlvDictionary.c @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../ccnxCodecSchemaV1_TlvDictionary.c" +#include <parc/algol/parc_SafeMemory.h> +#include <LongBow/unit-test.h> + +LONGBOW_TEST_RUNNER(ccnxCodecSchemaV1_TlvDictionary) +{ + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(ccnxCodecSchemaV1_TlvDictionary) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(ccnxCodecSchemaV1_TlvDictionary) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecSchemaV1TlvDictionary_CreateInterest); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecSchemaV1TlvDictionary_CreateInterestReturn); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecSchemaV1TlvDictionary_CreateContentObject); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecSchemaV1TlvDictionary_CreateControl); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + if (parcSafeMemory_ReportAllocation(STDOUT_FILENO) != 0) { + printf("('%s' leaks memory by %d (allocs - frees)) ", longBowTestCase_GetName(testCase), parcMemory_Outstanding()); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, ccnxCodecSchemaV1TlvDictionary_CreateInterest) +{ + CCNxTlvDictionary *test = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + assertNotNull(test, "Got null return from ccnxCodecSchemaV1TlvDictionary_CreateInterest"); + assertTrue(ccnxTlvDictionary_IsInterest(test), "Dictionary is not an interest"); + assertTrue(ccnxTlvDictionary_GetSchemaVersion(test) == CCNxTlvDictionary_SchemaVersion_V1, + "Wrong schema version, expected %d got %d", + CCNxTlvDictionary_SchemaVersion_V1, + ccnxTlvDictionary_GetSchemaVersion(test)); + ccnxTlvDictionary_Release(&test); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecSchemaV1TlvDictionary_CreateInterestReturn) +{ + CCNxTlvDictionary *test = ccnxCodecSchemaV1TlvDictionary_CreateInterestReturn(); + assertNotNull(test, "Got null return from ccnxCodecSchemaV1TlvDictionary_CreateInterest"); + assertTrue(ccnxTlvDictionary_IsInterestReturn(test), "Dictionary is not an interest"); + assertTrue(ccnxTlvDictionary_GetSchemaVersion(test) == CCNxTlvDictionary_SchemaVersion_V1, + "Wrong schema version, expected %d got %d", + CCNxTlvDictionary_SchemaVersion_V1, + ccnxTlvDictionary_GetSchemaVersion(test)); + ccnxTlvDictionary_Release(&test); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecSchemaV1TlvDictionary_CreateContentObject) +{ + CCNxTlvDictionary *test = ccnxCodecSchemaV1TlvDictionary_CreateContentObject(); + assertNotNull(test, "Got null return from ccnxCodecSchemaV1TlvDictionary_CreateContentObject"); + assertTrue(ccnxTlvDictionary_IsContentObject(test), "Dictionary is not a content object"); + assertTrue(ccnxTlvDictionary_GetSchemaVersion(test) == CCNxTlvDictionary_SchemaVersion_V1, + "Wrong schema version, expected %d got %d", + CCNxTlvDictionary_SchemaVersion_V1, + ccnxTlvDictionary_GetSchemaVersion(test)); + ccnxTlvDictionary_Release(&test); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecSchemaV1TlvDictionary_CreateControl) +{ + CCNxTlvDictionary *test = ccnxCodecSchemaV1TlvDictionary_CreateControl(); + assertNotNull(test, "Got null return from ccnxCodecSchemaV1TlvDictionary_CreateControl"); + assertTrue(ccnxTlvDictionary_IsControl(test), "Dictionary is not a control"); + assertTrue(ccnxTlvDictionary_GetSchemaVersion(test) == CCNxTlvDictionary_SchemaVersion_V1, + "Wrong schema version, expected %d got %d", + CCNxTlvDictionary_SchemaVersion_V1, + ccnxTlvDictionary_GetSchemaVersion(test)); + ccnxTlvDictionary_Release(&test); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(ccnxCodecSchemaV1_TlvDictionary); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-common/ccnx/common/codec/schema_v1/test/test_ccnxCodecSchemaV1_ValidationDecoder.c b/libccnx-common/ccnx/common/codec/schema_v1/test/test_ccnxCodecSchemaV1_ValidationDecoder.c new file mode 100755 index 00000000..82cf69d1 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/test/test_ccnxCodecSchemaV1_ValidationDecoder.c @@ -0,0 +1,450 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../ccnxCodecSchemaV1_ValidationDecoder.c" +#include <parc/algol/parc_SafeMemory.h> +#include <LongBow/unit-test.h> +#include <ccnx/common/codec/schema_v1/testdata/v1_content_nameA_keyid1_rsasha256.h> +#include <ccnx/common/codec/schema_v1/testdata/v1_interest_all_fields.h> + +#include "testrig_packetwrapper.c" + +// ========================================================================= + +LONGBOW_TEST_RUNNER(ccnxCodecSchemaV1_ValidationDecoder) +{ + LONGBOW_RUN_TEST_FIXTURE(DecodeAlg); + LONGBOW_RUN_TEST_FIXTURE(DecodePayload); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(ccnxCodecSchemaV1_ValidationDecoder) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(ccnxCodecSchemaV1_ValidationDecoder) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// ========================================================================= + +LONGBOW_TEST_FIXTURE(DecodeAlg) +{ + LONGBOW_RUN_TEST_CASE(DecodeAlg, CRC32C); + LONGBOW_RUN_TEST_CASE(DecodeAlg, HMAC_SHA256); + LONGBOW_RUN_TEST_CASE(DecodeAlg, RSA_SHA256); + + LONGBOW_RUN_TEST_CASE(DecodeAlg, Cert); + LONGBOW_RUN_TEST_CASE(DecodeAlg, PublicKey); + LONGBOW_RUN_TEST_CASE(DecodeAlg, KeyId); + LONGBOW_RUN_TEST_CASE(DecodeAlg, KeyName); + LONGBOW_RUN_TEST_CASE(DecodeAlg, SigTime); + + LONGBOW_RUN_TEST_CASE(DecodeAlg, KeyName_Invalid); +} + +LONGBOW_TEST_FIXTURE_SETUP(DecodeAlg) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(DecodeAlg) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(DecodeAlg, CRC32C) +{ + CCNxCodecSchemaV1TlvDictionary_CryptoSuite tlvSuite = CCNxCodecSchemaV1TlvDictionary_CryptoSuite_CRC32C; + PARCCryptoSuite parcSuite = PARCCryptoSuite_NULL_CRC32C; + + uint8_t encoded[] = { + 0x00, tlvSuite, 0x00, 0, + 0x00, 0x00, 0x00, 0x00 + }; + + PARCBuffer *buffer = parcBuffer_Wrap(encoded, sizeof(encoded), 0, sizeof(encoded)); + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(buffer); + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateContentObject(); + bool success = ccnxCodecSchemaV1ValidationDecoder_DecodeAlg(decoder, dictionary); + assertTrue(success, "Failed ccnxCodecSchemaV1ValidationDecoder_DecodeAlg: %s", ccnxCodecError_ToString(ccnxCodecTlvDecoder_GetError(decoder))); + + PARCCryptoSuite test = (PARCCryptoSuite) ccnxTlvDictionary_GetInteger(dictionary, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_CRYPTO_SUITE); + assertTrue(test == parcSuite, "Got wrong suite, expected %d got %d", parcSuite, test); + + ccnxTlvDictionary_Release(&dictionary); + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(DecodeAlg, HMAC_SHA256) +{ + CCNxCodecSchemaV1TlvDictionary_CryptoSuite tlvSuite = CCNxCodecSchemaV1TlvDictionary_CryptoSuite_HmacSha256; + PARCCryptoSuite parcSuite = PARCCryptoSuite_HMAC_SHA256; + + uint8_t encoded[] = { + 0x00, tlvSuite, 0x00, 0, + 0x00, 0x00, 0x00, 0x00 + }; + + PARCBuffer *buffer = parcBuffer_Wrap(encoded, sizeof(encoded), 0, sizeof(encoded)); + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(buffer); + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateContentObject(); + bool success = ccnxCodecSchemaV1ValidationDecoder_DecodeAlg(decoder, dictionary); + assertTrue(success, "Failed ccnxCodecSchemaV1ValidationDecoder_DecodeAlg: %s", ccnxCodecError_ToString(ccnxCodecTlvDecoder_GetError(decoder))); + + PARCCryptoSuite test = (PARCCryptoSuite) ccnxTlvDictionary_GetInteger(dictionary, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_CRYPTO_SUITE); + assertTrue(test == parcSuite, "Got wrong suite, expected %d got %d", parcSuite, test); + + ccnxTlvDictionary_Release(&dictionary); + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(DecodeAlg, RSA_SHA256) +{ + CCNxCodecSchemaV1TlvDictionary_CryptoSuite tlvSuite = CCNxCodecSchemaV1TlvDictionary_CryptoSuite_RsaSha256; + PARCCryptoSuite parcSuite = PARCCryptoSuite_RSA_SHA256; + + uint8_t encoded[] = { + 0x00, tlvSuite, 0x00, 0, + 0x00, 0x00, 0x00, 0x00 + }; + + PARCBuffer *buffer = parcBuffer_Wrap(encoded, sizeof(encoded), 0, sizeof(encoded)); + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(buffer); + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateContentObject(); + bool success = ccnxCodecSchemaV1ValidationDecoder_DecodeAlg(decoder, dictionary); + assertTrue(success, "Failed ccnxCodecSchemaV1ValidationDecoder_DecodeAlg: %s", ccnxCodecError_ToString(ccnxCodecTlvDecoder_GetError(decoder))); + + PARCCryptoSuite test = (PARCCryptoSuite) ccnxTlvDictionary_GetInteger(dictionary, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_CRYPTO_SUITE); + assertTrue(test == parcSuite, "Got wrong suite, expected %d got %d", parcSuite, test); + + ccnxTlvDictionary_Release(&dictionary); + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(DecodeAlg, Cert) +{ + uint8_t encoded[] = { + 0x00, 0x06, 0x00, 10, + 0x00, 0x0C, 0x00, 6, + 0x0a, 0x0b, 0x0c, 0x0d, + 0x0e, 0x0f + }; + + PARCBuffer *buffer = parcBuffer_Wrap(encoded, sizeof(encoded), 0, sizeof(encoded)); + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(buffer); + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateContentObject(); + bool success = ccnxCodecSchemaV1ValidationDecoder_DecodeAlg(decoder, dictionary); + assertTrue(success, "Failed ccnxCodecSchemaV1ValidationDecoder_DecodeAlg: %s", ccnxCodecError_ToString(ccnxCodecTlvDecoder_GetError(decoder))); + + PARCBuffer *truth = parcBuffer_Wrap(encoded, sizeof(encoded), 8, sizeof(encoded)); + PARCBuffer *test = ccnxTlvDictionary_GetBuffer(dictionary, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_CERT); + assertTrue(parcBuffer_Equals(truth, test), "Wrong buffer") + { + printf("Expected\n"); + parcBuffer_Display(truth, 3); + printf("Got\n"); + parcBuffer_Display(test, 3); + } + + parcBuffer_Release(&truth); + ccnxTlvDictionary_Release(&dictionary); + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(DecodeAlg, PublicKey) +{ + uint8_t encoded[] = { + 0x00, 0x06, 0x00, 10, + 0x00, 0x0B, 0x00, 6, + 0x0a, 0x0b, 0x0c, 0x0d, + 0x0e, 0x0f + }; + + PARCBuffer *buffer = parcBuffer_Wrap(encoded, sizeof(encoded), 0, sizeof(encoded)); + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(buffer); + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateContentObject(); + bool success = ccnxCodecSchemaV1ValidationDecoder_DecodeAlg(decoder, dictionary); + assertTrue(success, "Failed ccnxCodecSchemaV1ValidationDecoder_DecodeAlg: %s", ccnxCodecError_ToString(ccnxCodecTlvDecoder_GetError(decoder))); + + PARCBuffer *truth = parcBuffer_Wrap(encoded, sizeof(encoded), 8, sizeof(encoded)); + PARCBuffer *test = ccnxTlvDictionary_GetBuffer(dictionary, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_KEY); + assertTrue(parcBuffer_Equals(truth, test), "Wrong buffer") + { + printf("Expected\n"); + parcBuffer_Display(truth, 3); + printf("Got\n"); + parcBuffer_Display(test, 3); + } + + parcBuffer_Release(&truth); + ccnxTlvDictionary_Release(&dictionary); + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(DecodeAlg, KeyId) +{ + uint8_t encoded[] = { + 0x00, 0x06, 0x00, 10, + 0x00, 0x09, 0x00, 6, + 0x0a, 0x0b, 0x0c, 0x0d, + 0x0e, 0x0f + }; + + PARCBuffer *buffer = parcBuffer_Wrap(encoded, sizeof(encoded), 0, sizeof(encoded)); + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(buffer); + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateContentObject(); + bool success = ccnxCodecSchemaV1ValidationDecoder_DecodeAlg(decoder, dictionary); + assertTrue(success, "Failed ccnxCodecSchemaV1ValidationDecoder_DecodeAlg: %s", ccnxCodecError_ToString(ccnxCodecTlvDecoder_GetError(decoder))); + + PARCBuffer *truth = parcBuffer_Wrap(encoded, sizeof(encoded), 8, sizeof(encoded)); + PARCBuffer *test = ccnxTlvDictionary_GetBuffer(dictionary, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_KEYID); + assertTrue(parcBuffer_Equals(truth, test), "Wrong buffer") + { + printf("Expected\n"); + parcBuffer_Display(truth, 3); + printf("Got\n"); + parcBuffer_Display(test, 3); + } + + parcBuffer_Release(&truth); + ccnxTlvDictionary_Release(&dictionary); + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(DecodeAlg, KeyName) +{ + uint8_t encoded[] = { + 0x00, 0x06, 0x00, 44, + 0x00, 0x0E, 0x00, 40, + // --- name + 0x00, 0x00, 0x00, 16, + 0x00, 0x03, 0x00, 5, + 'a', 'p', 'p', 'l', + 'e', + 0x00, 0x03, 0x00, 3, + 'p', 'i', 'e', + // --- keyid + 0x00, 0x01, 0x00, 4, + 0xa1, 0xa2, 0xa3, 0xa4, + // --- hash + 0x00, 0x02, 0x00, 8, + 0xb1, 0xb2, 0xb3, 0xb4, + 0xb5, 0xb6, 0xb7, 0xb8, + }; + + PARCBuffer *buffer = parcBuffer_Wrap(encoded, sizeof(encoded), 0, sizeof(encoded)); + CCNxName *name = ccnxName_CreateFromCString("lci:/3=apple/3=pie"); + PARCBuffer *keyid = parcBuffer_Wrap(encoded, sizeof(encoded), 32, 36); + PARCBuffer *hash = parcBuffer_Wrap(encoded, sizeof(encoded), 40, 48); + + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(buffer); + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateContentObject(); + bool success = ccnxCodecSchemaV1ValidationDecoder_DecodeAlg(decoder, dictionary); + assertTrue(success, "Failed ccnxCodecSchemaV1ValidationDecoder_DecodeAlg: %s", ccnxCodecError_ToString(ccnxCodecTlvDecoder_GetError(decoder))); + + // now test the 3 decoded fields + CCNxName *testName = ccnxTlvDictionary_GetName(dictionary, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_KEYNAME_NAME); + assertTrue(ccnxName_Equals(testName, name), "keynames do not match") + { + printf("Expected\n"); + ccnxName_Display(name, 3); + printf("Got\n"); + ccnxName_Display(testName, 3); + } + + PARCBuffer *testKeyid = ccnxTlvDictionary_GetBuffer(dictionary, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_KEYNAME_KEYID); + assertTrue(parcBuffer_Equals(testKeyid, keyid), "keyid do not match") + { + printf("Expected\n"); + parcBuffer_Display(keyid, 3); + printf("Got\n"); + parcBuffer_Display(testKeyid, 3); + } + + PARCBuffer *testHash = ccnxTlvDictionary_GetBuffer(dictionary, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_KEYNAME_OBJHASH); + assertTrue(parcBuffer_Equals(testHash, hash), "keyid do not match") + { + printf("Expected\n"); + parcBuffer_Display(hash, 3); + printf("Got\n"); + parcBuffer_Display(testHash, 3); + } + + parcBuffer_Release(&keyid); + parcBuffer_Release(&hash); + ccnxName_Release(&name); + ccnxTlvDictionary_Release(&dictionary); + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(DecodeAlg, KeyName_Invalid) +{ + // link is missing the Name + uint8_t encoded[] = { + 0x00, 0x06, 0x00, 24, + 0x00, 0x0E, 0x00, 20, + // --- keyid + 0x00, 0x01, 0x00, 4, + 0xa1, 0xa2, 0xa3, 0xa4, + // --- hash + 0x00, 0x02, 0x00, 8, + 0xb1, 0xb2, 0xb3, 0xb4, + 0xb5, 0xb6, 0xb7, 0xb8, + }; + + PARCBuffer *buffer = parcBuffer_Wrap(encoded, sizeof(encoded), 0, sizeof(encoded)); + + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(buffer); + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateContentObject(); + bool success = ccnxCodecSchemaV1ValidationDecoder_DecodeAlg(decoder, dictionary); + assertFalse(success, "Should have failed decode as keyname is invalid"); + + ccnxTlvDictionary_Release(&dictionary); + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(DecodeAlg, SigTime) +{ + uint64_t sigtime = 0x1122334455667788ULL; + uint8_t encoded[] = { + 0x00, 0x06, 0x00, 12, + 0x00, 0x0F, 0x00, 8, + 0x11, 0x22, 0x33, 0x44, + 0x55, 0x66, 0x77, 0x88 + }; + + PARCBuffer *buffer = parcBuffer_Wrap(encoded, sizeof(encoded), 0, sizeof(encoded)); + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(buffer); + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateContentObject(); + bool success = ccnxCodecSchemaV1ValidationDecoder_DecodeAlg(decoder, dictionary); + assertTrue(success, "Failed ccnxCodecSchemaV1ValidationDecoder_DecodeAlg: %s", ccnxCodecError_ToString(ccnxCodecTlvDecoder_GetError(decoder))); + + uint64_t test = ccnxTlvDictionary_GetInteger(dictionary, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_SIGNTIME); + assertTrue(test == sigtime, "Wrong sig time, expected %" PRIx64 ", got %" PRIx64, sigtime, test); + ccnxTlvDictionary_Release(&dictionary); + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&buffer); +} + + +// ========================================================================= + +LONGBOW_TEST_FIXTURE(DecodePayload) +{ + LONGBOW_RUN_TEST_CASE(DecodePayload, payload); + LONGBOW_RUN_TEST_CASE(DecodePayload, payload_zero); +} + +LONGBOW_TEST_FIXTURE_SETUP(DecodePayload) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(DecodePayload) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(DecodePayload, payload) +{ + uint8_t encoded[] = { + 0x00, 0x04, 0x00, 8, + 0x11, 0x22, 0x33, 0x44, + 0x55, 0x66, 0x77, 0x88 + }; + + PARCBuffer *buffer = parcBuffer_Wrap(encoded, sizeof(encoded), 0, sizeof(encoded)); + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(buffer); + + // the caller has already parsed the T and L, so we point to just payload + ccnxCodecTlvDecoder_Advance(decoder, 4); + + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateContentObject(); + bool success = ccnxCodecSchemaV1ValidationDecoder_DecodePayload(decoder, dictionary); + assertTrue(success, "Failed to decode a valid payload: %s", ccnxCodecError_ToString(ccnxCodecTlvDecoder_GetError(decoder))); + + PARCBuffer *truth = parcBuffer_Wrap(encoded, sizeof(encoded), 4, 12); + PARCBuffer *test = ccnxTlvDictionary_GetBuffer(dictionary, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_PAYLOAD); + assertTrue(parcBuffer_Equals(truth, test), "Buffer mismatch") + { + printf("Expected\n"); + parcBuffer_Display(truth, 3); + printf("Got\n"); + parcBuffer_Display(test, 3); + } + + parcBuffer_Release(&truth); + ccnxTlvDictionary_Release(&dictionary); + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(DecodePayload, payload_zero) +{ + uint8_t encoded[] = { + 0x00, 0x04, 0x00, 0, + }; + + PARCBuffer *buffer = parcBuffer_Wrap(encoded, sizeof(encoded), 0, sizeof(encoded)); + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(buffer); + + // the caller has already parsed the T and L, so we point to just payload + ccnxCodecTlvDecoder_Advance(decoder, 4); + + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateContentObject(); + bool success = ccnxCodecSchemaV1ValidationDecoder_DecodePayload(decoder, dictionary); + assertFalse(success, "Should have failed on 0-length payload"); + + ccnxTlvDictionary_Release(&dictionary); + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&buffer); +} + +// ========================================================================= + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(ccnxCodecSchemaV1_ValidationDecoder); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-common/ccnx/common/codec/schema_v1/test/test_ccnxCodecSchemaV1_ValidationEncoder.c b/libccnx-common/ccnx/common/codec/schema_v1/test/test_ccnxCodecSchemaV1_ValidationEncoder.c new file mode 100755 index 00000000..8a4d951a --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/test/test_ccnxCodecSchemaV1_ValidationEncoder.c @@ -0,0 +1,565 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../ccnxCodecSchemaV1_ValidationEncoder.c" +#include <parc/algol/parc_SafeMemory.h> +#include <LongBow/unit-test.h> +#include <ccnx/common/codec/schema_v1/testdata/v1_content_nameA_keyid1_rsasha256.h> +#include <ccnx/common/codec/schema_v1/testdata/v1_interest_all_fields.h> +#include <ccnx/common/codec/schema_v1/testdata/v1_interest_nameA_crc32c.h> + +#include "testrig_packetwrapper.c" + +#include <ccnx/common/internal/ccnx_ValidationFacadeV1.h> + +#include <ccnx/common/validation/ccnxValidation_CRC32C.h> +#include <ccnx/common/validation/ccnxValidation_HmacSha256.h> + +// ========================================================================= + +LONGBOW_TEST_RUNNER(ccnxCodecSchemaV1_ValidationDecoder) +{ + LONGBOW_RUN_TEST_FIXTURE(EncodeAlg); + LONGBOW_RUN_TEST_FIXTURE(EncodePayload); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(ccnxCodecSchemaV1_ValidationDecoder) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(ccnxCodecSchemaV1_ValidationDecoder) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// ========================================================================= + +LONGBOW_TEST_FIXTURE(EncodeAlg) +{ + LONGBOW_RUN_TEST_CASE(EncodeAlg, CRC32C); + LONGBOW_RUN_TEST_CASE(EncodeAlg, HMAC_SHA256); + LONGBOW_RUN_TEST_CASE(EncodeAlg, RSA_SHA256); + LONGBOW_RUN_TEST_CASE(EncodeAlg, DeduceFromSigner); + + LONGBOW_RUN_TEST_CASE(EncodeAlg, _encodeCertificate); + LONGBOW_RUN_TEST_CASE(EncodeAlg, _encodePublicKey); + LONGBOW_RUN_TEST_CASE(EncodeAlg, _encodeKeyId); + LONGBOW_RUN_TEST_CASE(EncodeAlg, _encodeKeyName); + LONGBOW_RUN_TEST_CASE(EncodeAlg, _encodeSignatureTime_Specified); + LONGBOW_RUN_TEST_CASE(EncodeAlg, _encodeSignatureTime_Generated); +} + +LONGBOW_TEST_FIXTURE_SETUP(EncodeAlg) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(EncodeAlg) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(EncodeAlg, CRC32C) +{ + CCNxCodecSchemaV1TlvDictionary_CryptoSuite tlvSuite = CCNxCodecSchemaV1TlvDictionary_CryptoSuite_CRC32C; + PARCCryptoSuite suite = PARCCryptoSuite_NULL_CRC32C; + + uint8_t encoded[] = { + 0x00, tlvSuite, 0x00, 0, + }; + + PARCBuffer *trueEncoded = parcBuffer_Wrap(encoded, sizeof(encoded), 0, sizeof(encoded)); + + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateContentObject(); + ccnxTlvDictionary_PutInteger(dictionary, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_CRYPTO_SUITE, suite); + + ssize_t length = ccnxCodecSchemaV1ValidationEncoder_EncodeAlg(encoder, dictionary); + assertFalse(length < 0, "Error on encoding: %s", ccnxCodecError_ToString(ccnxCodecTlvEncoder_GetError(encoder))); + + ccnxCodecTlvEncoder_Finalize(encoder); + PARCBuffer *test = ccnxCodecTlvEncoder_CreateBuffer(encoder); + assertTrue(parcBuffer_Equals(trueEncoded, test), "Wrong buffer") + { + printf("Expected\n"); + parcBuffer_Display(trueEncoded, 3); + printf("Got\n"); + parcBuffer_Display(test, 3); + } + + parcBuffer_Release(&test); + ccnxTlvDictionary_Release(&dictionary); + ccnxCodecTlvEncoder_Destroy(&encoder); + parcBuffer_Release(&trueEncoded); +} + +LONGBOW_TEST_CASE(EncodeAlg, HMAC_SHA256) +{ + CCNxCodecSchemaV1TlvDictionary_CryptoSuite tlvSuite = CCNxCodecSchemaV1TlvDictionary_CryptoSuite_HmacSha256; + PARCCryptoSuite suite = PARCCryptoSuite_HMAC_SHA256; + + uint8_t encoded[] = { + 0x00, tlvSuite, 0x00, 0, + }; + + PARCBuffer *trueEncoded = parcBuffer_Wrap(encoded, sizeof(encoded), 0, sizeof(encoded)); + + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateContentObject(); + ccnxTlvDictionary_PutInteger(dictionary, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_CRYPTO_SUITE, suite); + + ssize_t length = ccnxCodecSchemaV1ValidationEncoder_EncodeAlg(encoder, dictionary); + assertFalse(length < 0, "Error on encoding: %s", ccnxCodecError_ToString(ccnxCodecTlvEncoder_GetError(encoder))); + + ccnxCodecTlvEncoder_Finalize(encoder); + PARCBuffer *test = ccnxCodecTlvEncoder_CreateBuffer(encoder); + assertTrue(parcBuffer_Equals(trueEncoded, test), "Wrong buffer") + { + printf("Expected\n"); + parcBuffer_Display(trueEncoded, 3); + printf("Got\n"); + parcBuffer_Display(test, 3); + } + + parcBuffer_Release(&test); + ccnxTlvDictionary_Release(&dictionary); + ccnxCodecTlvEncoder_Destroy(&encoder); + parcBuffer_Release(&trueEncoded); +} + +LONGBOW_TEST_CASE(EncodeAlg, RSA_SHA256) +{ + CCNxCodecSchemaV1TlvDictionary_CryptoSuite tlvSuite = CCNxCodecSchemaV1TlvDictionary_CryptoSuite_RsaSha256; + PARCCryptoSuite suite = PARCCryptoSuite_RSA_SHA256; + + uint8_t encoded[] = { + 0x00, tlvSuite, 0x00, 0, + }; + + PARCBuffer *trueEncoded = parcBuffer_Wrap(encoded, sizeof(encoded), 0, sizeof(encoded)); + + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateContentObject(); + ccnxTlvDictionary_PutInteger(dictionary, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_CRYPTO_SUITE, suite); + + ssize_t length = ccnxCodecSchemaV1ValidationEncoder_EncodeAlg(encoder, dictionary); + assertFalse(length < 0, "Error on encoding: %s", ccnxCodecError_ToString(ccnxCodecTlvEncoder_GetError(encoder))); + + ccnxCodecTlvEncoder_Finalize(encoder); + PARCBuffer *test = ccnxCodecTlvEncoder_CreateBuffer(encoder); + assertTrue(parcBuffer_Equals(trueEncoded, test), "Wrong buffer") + { + printf("Expected\n"); + parcBuffer_Display(trueEncoded, 3); + printf("Got\n"); + parcBuffer_Display(test, 3); + } + + parcBuffer_Release(&test); + ccnxTlvDictionary_Release(&dictionary); + ccnxCodecTlvEncoder_Destroy(&encoder); + parcBuffer_Release(&trueEncoded); +} + +LONGBOW_TEST_CASE(EncodeAlg, DeduceFromSigner) +{ + PARCSigner *signer = ccnxValidationCRC32C_CreateSigner(); + CCNxCodecSchemaV1TlvDictionary_CryptoSuite tlvsuite = CCNxCodecSchemaV1TlvDictionary_CryptoSuite_CRC32C; + + uint8_t encoded[] = { + 0x00, tlvsuite, 0x00, 0, + }; + + PARCBuffer *trueEncoded = parcBuffer_Wrap(encoded, sizeof(encoded), 0, sizeof(encoded)); + + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + ccnxCodecTlvEncoder_SetSigner(encoder, signer); + + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateContentObject(); + + ssize_t length = ccnxCodecSchemaV1ValidationEncoder_EncodeAlg(encoder, dictionary); + assertFalse(length < 0, "Error on encoding: %s", ccnxCodecError_ToString(ccnxCodecTlvEncoder_GetError(encoder))); + + ccnxCodecTlvEncoder_Finalize(encoder); + PARCBuffer *test = ccnxCodecTlvEncoder_CreateBuffer(encoder); + assertTrue(parcBuffer_Equals(trueEncoded, test), "Wrong buffer") + { + printf("Expected\n"); + parcBuffer_Display(trueEncoded, 3); + printf("Got\n"); + parcBuffer_Display(test, 3); + } + + parcBuffer_Release(&test); + ccnxTlvDictionary_Release(&dictionary); + ccnxCodecTlvEncoder_Destroy(&encoder); + parcBuffer_Release(&trueEncoded); + parcSigner_Release(&signer); +} + +// ======= + +LONGBOW_TEST_CASE(EncodeAlg, _encodeCertificate) +{ + uint8_t encoded[] = { + 0x00, 0x0C, 0x00, 6, + 0x0a, 0x0b, 0x0c, 0x0d, + 0x0e, 0x0f + }; + + PARCBuffer *truth = parcBuffer_Wrap(encoded, sizeof(encoded), 0, sizeof(encoded)); + PARCBuffer *cert = parcBuffer_Wrap(encoded, sizeof(encoded), 4, sizeof(encoded)); + + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateContentObject(); + ccnxValidationFacadeV1_SetCertificate(dictionary, cert); + + ssize_t length = _encodeCertificate(encoder, dictionary); + assertTrue(length == sizeof(encoded), "Wrong length, expected %zu got %zd", sizeof(encoded), length); + + ccnxCodecTlvEncoder_Finalize(encoder); + PARCBuffer *test = ccnxCodecTlvEncoder_CreateBuffer(encoder); + assertTrue(parcBuffer_Equals(truth, test), "Wrong buffer") + { + printf("Expected\n"); + parcBuffer_Display(truth, 3); + printf("Got\n"); + parcBuffer_Display(test, 3); + } + + parcBuffer_Release(&test); + parcBuffer_Release(&truth); + parcBuffer_Release(&cert); + ccnxTlvDictionary_Release(&dictionary); + ccnxCodecTlvEncoder_Destroy(&encoder); +} + + +LONGBOW_TEST_CASE(EncodeAlg, _encodePublicKey) +{ + uint8_t encoded[] = { + 0x00, 0x0B, 0x00, 6, + 0x0a, 0x0b, 0x0c, 0x0d, + 0x0e, 0x0f + }; + + PARCBuffer *truth = parcBuffer_Wrap(encoded, sizeof(encoded), 0, sizeof(encoded)); + PARCBuffer *key = parcBuffer_Wrap(encoded, sizeof(encoded), 4, sizeof(encoded)); + + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateContentObject(); + ccnxValidationFacadeV1_SetPublicKey(dictionary, key); + + ssize_t length = _encodePublicKey(encoder, dictionary); + assertTrue(length == sizeof(encoded), "Wrong length, expected %zu got %zd", sizeof(encoded), length); + + ccnxCodecTlvEncoder_Finalize(encoder); + PARCBuffer *test = ccnxCodecTlvEncoder_CreateBuffer(encoder); + assertTrue(parcBuffer_Equals(truth, test), "Wrong buffer") + { + printf("Expected\n"); + parcBuffer_Display(truth, 3); + printf("Got\n"); + parcBuffer_Display(test, 3); + } + + parcBuffer_Release(&test); + parcBuffer_Release(&truth); + parcBuffer_Release(&key); + ccnxTlvDictionary_Release(&dictionary); + ccnxCodecTlvEncoder_Destroy(&encoder); +} + +LONGBOW_TEST_CASE(EncodeAlg, _encodeKeyId) +{ + uint8_t encoded[] = { + 0x00, 0x09, 0x00, 6, + 0x0a, 0x0b, 0x0c, 0x0d, + 0x0e, 0x0f + }; + + PARCBuffer *truth = parcBuffer_Wrap(encoded, sizeof(encoded), 0, sizeof(encoded)); + PARCBuffer *keyid = parcBuffer_Wrap(encoded, sizeof(encoded), 4, sizeof(encoded)); + + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateContentObject(); + ccnxValidationFacadeV1_SetKeyId(dictionary, keyid); + + ssize_t length = _encodeKeyId(encoder, dictionary); + assertTrue(length == sizeof(encoded), "Wrong length, expected %zu got %zd", sizeof(encoded), length); + + ccnxCodecTlvEncoder_Finalize(encoder); + PARCBuffer *test = ccnxCodecTlvEncoder_CreateBuffer(encoder); + assertTrue(parcBuffer_Equals(truth, test), "Wrong buffer") + { + printf("Expected\n"); + parcBuffer_Display(truth, 3); + printf("Got\n"); + parcBuffer_Display(test, 3); + } + + parcBuffer_Release(&test); + parcBuffer_Release(&truth); + parcBuffer_Release(&keyid); + ccnxTlvDictionary_Release(&dictionary); + ccnxCodecTlvEncoder_Destroy(&encoder); +} + +LONGBOW_TEST_CASE(EncodeAlg, _encodeKeyName) +{ + uint8_t encoded[] = { + 0x00, 0x0E, 0x00, 40, + // --- name + 0x00, 0x00, 0x00, 16, + 0x00, 0x03, 0x00, 5, + 'a', 'p', 'p', 'l', + 'e', + 0x00, 0x03, 0x00, 3, + 'p', 'i', 'e', + // --- keyid + 0x00, 0x01, 0x00, 4, + 0xa1, 0xa2, 0xa3, 0xa4, + // --- hash + 0x00, 0x02, 0x00, 8, + 0xb1, 0xb2, 0xb3, 0xb4, + 0xb5, 0xb6, 0xb7, 0xb8, + }; + + PARCBuffer *truth = parcBuffer_Wrap(encoded, sizeof(encoded), 0, sizeof(encoded)); + PARCBuffer *keyid = parcBuffer_Wrap(encoded, sizeof(encoded), 28, 32); + PARCBuffer *hash = parcBuffer_Wrap(encoded, sizeof(encoded), 36, 44); + + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + + CCNxName *name = ccnxName_CreateFromCString("lci:/3=apple/3=pie"); + CCNxLink *link = ccnxLink_Create(name, keyid, hash); + + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateContentObject(); + ccnxValidationFacadeV1_SetKeyName(dictionary, link); + + ssize_t length = _encodeKeyName(encoder, dictionary); + assertTrue(length == sizeof(encoded), "Wrong length, expected %zu got %zd", sizeof(encoded), length); + + ccnxCodecTlvEncoder_Finalize(encoder); + PARCBuffer *test = ccnxCodecTlvEncoder_CreateBuffer(encoder); + assertTrue(parcBuffer_Equals(truth, test), "Wrong buffer") + { + printf("Expected\n"); + parcBuffer_Display(truth, 3); + printf("Got\n"); + parcBuffer_Display(test, 3); + } + + parcBuffer_Release(&test); + parcBuffer_Release(&truth); + parcBuffer_Release(&keyid); + parcBuffer_Release(&hash); + ccnxName_Release(&name); + ccnxLink_Release(&link); + ccnxTlvDictionary_Release(&dictionary); + ccnxCodecTlvEncoder_Destroy(&encoder); +} + +LONGBOW_TEST_CASE(EncodeAlg, _encodeSignatureTime_Specified) +{ + uint64_t sigtime = 0x1122334455667788ULL; + uint8_t encoded[] = { + 0x00, 0x0F, 0x00, 8, + 0x11, 0x22, 0x33, 0x44, + 0x55, 0x66, 0x77, 0x88 + }; + + PARCBuffer *truth = parcBuffer_Wrap(encoded, sizeof(encoded), 0, sizeof(encoded)); + + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateContentObject(); + ccnxValidationFacadeV1_SetSigningTime(dictionary, sigtime); + + ssize_t length = _encodeSignatureTime(encoder, dictionary); + assertTrue(length == sizeof(encoded), "Wrong length, expected %zu got %zd", sizeof(encoded), length); + + ccnxCodecTlvEncoder_Finalize(encoder); + PARCBuffer *test = ccnxCodecTlvEncoder_CreateBuffer(encoder); + assertTrue(parcBuffer_Equals(truth, test), "Wrong buffer") + { + printf("Expected\n"); + parcBuffer_Display(truth, 3); + printf("Got\n"); + parcBuffer_Display(test, 3); + } + + parcBuffer_Release(&test); + parcBuffer_Release(&truth); + ccnxTlvDictionary_Release(&dictionary); + ccnxCodecTlvEncoder_Destroy(&encoder); +} + +/* + * Do not specify a signing time, but rather set a Signer and let the code + * create the time on its own + */ +LONGBOW_TEST_CASE(EncodeAlg, _encodeSignatureTime_Generated) +{ + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + + PARCBuffer *password = parcBuffer_Wrap("password", 8, 0, 8); + PARCSigner *signer = ccnxValidationHmacSha256_CreateSigner(password); + ccnxCodecTlvEncoder_SetSigner(encoder, signer); + parcSigner_Release(&signer); + + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateContentObject(); + + ssize_t length = _encodeSignatureTime(encoder, dictionary); + assertTrue(length == 12, "Wrong length, expected 12, got %zd", length); + + parcBuffer_Release(&password); + ccnxTlvDictionary_Release(&dictionary); + ccnxCodecTlvEncoder_Destroy(&encoder); +} + +// ========================================================================= + +LONGBOW_TEST_FIXTURE(EncodePayload) +{ + LONGBOW_RUN_TEST_CASE(EncodePayload, payload_Specified); + LONGBOW_RUN_TEST_CASE(EncodePayload, payload_Generated); +} + +LONGBOW_TEST_FIXTURE_SETUP(EncodePayload) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(EncodePayload) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(EncodePayload, payload_Specified) +{ + uint8_t encoded[] = { + 0x11, 0x22, 0x33, 0x44, + 0x55, 0x66, 0x77, 0x88 + }; + + PARCBuffer *truth = parcBuffer_Wrap(encoded, sizeof(encoded), 0, sizeof(encoded)); + + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateContentObject(); + ccnxValidationFacadeV1_SetPayload(dictionary, truth); + + ssize_t length = ccnxCodecSchemaV1ValidationEncoder_EncodePayload(encoder, dictionary); + assertTrue(length == sizeof(encoded), "Wrong length, expected %zu got %zd", sizeof(encoded), length); + + ccnxCodecTlvEncoder_Finalize(encoder); + PARCBuffer *test = ccnxCodecTlvEncoder_CreateBuffer(encoder); + assertTrue(parcBuffer_Equals(truth, test), "Wrong buffer") + { + printf("Expected\n"); + parcBuffer_Display(truth, 3); + printf("Got\n"); + parcBuffer_Display(test, 3); + } + + parcBuffer_Release(&test); + parcBuffer_Release(&truth); + ccnxTlvDictionary_Release(&dictionary); + ccnxCodecTlvEncoder_Destroy(&encoder); +} + +/* + * Put the guts of v1_interest_nameA_crc32c in to the encoding buffer and mark it + * as the signature block. Generate the CRC and make sure we got the right thing. + */ +LONGBOW_TEST_CASE(EncodePayload, payload_Generated) +{ + TlvExtent interestExtent = getTruthTableExtent(TRUTHTABLENAME(v1_interest_nameA_crc32c), V1_MANIFEST_INT_INTEREST); + + // This will test against the string (Interest, ValidationAlg, ValidationPayload) + PARCBuffer *truth = parcBuffer_Wrap(v1_interest_nameA_crc32c, + sizeof(v1_interest_nameA_crc32c), + interestExtent.offset, + sizeof(v1_interest_nameA_crc32c)); + + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + PARCSigner *signer = ccnxValidationCRC32C_CreateSigner(); + ccnxCodecTlvEncoder_SetSigner(encoder, signer); + parcSigner_Release(&signer); + + // This will append from the beginning of the Interest message up to the end of the ValidationAlg + // This space is all marked as the "to-be-signed" section. + ccnxCodecTlvEncoder_MarkSignatureStart(encoder); + size_t signedInfoLenth = sizeof(v1_interest_nameA_crc32c) - 8 - interestExtent.offset; + ccnxCodecTlvEncoder_AppendRawArray(encoder, signedInfoLenth, v1_interest_nameA_crc32c + interestExtent.offset); + ccnxCodecTlvEncoder_MarkSignatureEnd(encoder); + + // add the validation payload container, then generate the signature + ccnxCodecTlvEncoder_AppendContainer(encoder, CCNxCodecSchemaV1Types_MessageType_ValidationPayload, 4); + + // Do the actual encoding. This will calculate the signature on the fly. + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateContentObject(); + ssize_t length = ccnxCodecSchemaV1ValidationEncoder_EncodePayload(encoder, dictionary); + assertTrue(length == 4, "Wrong length, expected 4 got %zd", length); + + ccnxCodecTlvEncoder_Finalize(encoder); + PARCBuffer *test = ccnxCodecTlvEncoder_CreateBuffer(encoder); + + // Tests that we got the right signature (CRC32c in this case) + assertTrue(parcBuffer_Equals(truth, test), "Wrong buffer") + { + printf("Expected\n"); + parcBuffer_Display(truth, 3); + printf("Got\n"); + parcBuffer_Display(test, 3); + } + + ccnxTlvDictionary_Release(&dictionary); + ccnxCodecTlvEncoder_Destroy(&encoder); + parcBuffer_Release(&truth); + parcBuffer_Release(&test); +} + +// ========================================================================= + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(ccnxCodecSchemaV1_ValidationDecoder); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-common/ccnx/common/codec/schema_v1/test/testrig_encoder.c b/libccnx-common/ccnx/common/codec/schema_v1/test/testrig_encoder.c new file mode 100644 index 00000000..f925dd28 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/test/testrig_encoder.c @@ -0,0 +1,472 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_TlvDictionary.h> +#include <ccnx/common/codec/ccnxCodec_EncodingBuffer.h> + +#include <ccnx/common/codec/schema_v1/testdata/v1_InterestSchema.h> +#include <ccnx/common/codec/schema_v1/testdata/v1_ContentObjectSchema.h> + + +#include <ccnx/common/codec/schema_v1/testdata/v1_testrig_truthTable.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_PacketEncoder.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_NameCodec.h> + +#include <ccnx/common/codec/test/testrig_Compare.c> + +/** + * Finds a row in the truthtable where bodyManifest is TRUE and the + * indexOrKey equals 'key'. + */ +TlvExtent +getTruthTableExtent(TruthTableEntry *ttentry, int key) +{ + for (int i = 0; ttentry[i].indexOrKey != T_INVALID; i++) { + if (ttentry[i].bodyManifest && ttentry[i].indexOrKey == key) { + return ttentry[i].extent; + } + } + return (TlvExtent) { 0, 0 }; +} + +/** + * Finds a row in the truthtable where bodyManifest is FALSE and the + * indexOrKey equals 'key'. + */ +TlvExtent +getTruthTableHeaderExtent(TruthTableEntry *ttentry, int key) +{ + for (int i = 0; ttentry[i].indexOrKey != T_INVALID; i++) { + if (!ttentry[i].bodyManifest && ttentry[i].indexOrKey == key) { + return ttentry[i].extent; + } + } + return (TlvExtent) { 0, 0 }; +} + +typedef struct test_data { + // the memory region extracted from a Truth Table entry + PARCBuffer *memoryRegion; + + CCNxCodecTlvEncoder *encoder; + CCNxTlvDictionary *dictionary; + + uint8_t *packet; + size_t packetLength; + TruthTableEntry *truthTable; + + // If the user creates one of these, we'll destroy it. + PARCSigner *signer; +} TestData; + +static SchemaV1ManifestContentObjectBody manifestContentObjectContainerArray[] = { + V1_MANIFEST_OBJ_CONTENTOBJECT, + V1_MANIFEST_OBJ_NAMEAUTH, + V1_MANIFEST_OBJ_ValidationPayload, + V1_MANIFEST_OBJ_KEYNAME, + V1_MANIFEST_OBJ_METADATA, + V1_MANIFEST_OBJ_ValidationAlg, + V1_MANIFEST_OBJ_BODYEND +}; + +static bool +isContentObjectContainer(SchemaV1ManifestContentObjectBody value) +{ + for (int i = 0; manifestContentObjectContainerArray[i] != V1_MANIFEST_OBJ_BODYEND; i++) { + if (value == manifestContentObjectContainerArray[i]) { + return true; + } + } + return false; +} + +/** + * The testdata truth tables were written with the tlv_1.0 array indicies, so we + * need to translate those old indicies to the new indicies. + */ +static CCNxCodecSchemaV1TlvDictionary_MessageFastArray +translateTestDataManifestToSchemaKey(SchemaV1ManifestContentObjectBody oldKey) +{ + switch (oldKey) { + case V1_MANIFEST_INT_NAME: + // fallthrough + case V1_MANIFEST_OBJ_NAME: + return CCNxCodecSchemaV1TlvDictionary_MessageFastArray_NAME; + + case V1_MANIFEST_OBJ_PAYLOAD: + return CCNxCodecSchemaV1TlvDictionary_MessageFastArray_PAYLOAD; + + case V1_MANIFEST_OBJ_KEYID: + return (CCNxCodecSchemaV1TlvDictionary_MessageFastArray) CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_KEYID; + + case V1_MANIFEST_OBJ_CRYPTO_SUITE: + return (CCNxCodecSchemaV1TlvDictionary_MessageFastArray) CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_CRYPTO_SUITE; + + case V1_MANIFEST_OBJ_KEY: + return (CCNxCodecSchemaV1TlvDictionary_MessageFastArray) CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_KEY; + + case V1_MANIFEST_OBJ_CERT: + return (CCNxCodecSchemaV1TlvDictionary_MessageFastArray) CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_CERT; + + case V1_MANIFEST_OBJ_KEYNAME_NAME: + return (CCNxCodecSchemaV1TlvDictionary_MessageFastArray) CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_KEYNAME_NAME; + + case V1_MANIFEST_OBJ_KEYNAME_OBJHASH: + return (CCNxCodecSchemaV1TlvDictionary_MessageFastArray) CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_KEYNAME_OBJHASH; + + case V1_MANIFEST_OBJ_OBJ_TYPE: + return CCNxCodecSchemaV1TlvDictionary_MessageFastArray_PAYLOADTYPE; + + case V1_MANIFEST_OBJ_SIGBITS: + return (CCNxCodecSchemaV1TlvDictionary_MessageFastArray) CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_PAYLOAD; + + case V1_MANIFEST_OBJ_SigningTime: + return (CCNxCodecSchemaV1TlvDictionary_MessageFastArray) CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_SIGNTIME; + + case V1_MANIFEST_OBJ_ENDSEGMENT: + return CCNxCodecSchemaV1TlvDictionary_MessageFastArray_ENDSEGMENT; + + case V1_MANIFEST_INT_KEYID: + return CCNxCodecSchemaV1TlvDictionary_MessageFastArray_KEYID_RESTRICTION; + + case V1_MANIFEST_INT_OBJHASH: + return CCNxCodecSchemaV1TlvDictionary_MessageFastArray_OBJHASH_RESTRICTION; + + default: + trapIllegalValue(oldKey, "Unexpected old manifest value: %d", oldKey); + break; + } + return -1; +} + +/** + * The testdata truth tables were written with the tlv_1.0 array indicies, so we + * need to translate those old indicies to the new indicies. + */ +static CCNxCodecSchemaV1TlvDictionary_HeadersFastArray +translateOldOptionalHeadersManifestToNewKey(CCNxTlvDictionary *packetDictionary, int oldKey) +{ + if (ccnxTlvDictionary_IsInterest(packetDictionary)) { + switch (oldKey) { + case V1_MANIFEST_INT_LIFETIME: + return CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_InterestLifetime; + + case V1_MANIFEST_INT_E2EFRAG: + return CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_INTFRAG; + + default: + trapIllegalValue(oldKey, "Unexpected old manifest value: %d", oldKey); + break; + } + } else if (ccnxTlvDictionary_IsContentObject(packetDictionary)) { + switch (oldKey) { + case V1_MANIFEST_OBJ_E2EFRAG: + return CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_OBJFRAG; + + default: + trapIllegalValue(oldKey, "Unexpected old manifest value: %d", oldKey); + break; + } + } + return -1; +} + +static void +addBuffer(TestData *data, CCNxTlvDictionary *packetDictionary, size_t item_start, size_t item_end, uint32_t translatedKey) +{ + PARCBuffer *itemBuffer = parcBuffer_Wrap(data->packet, data->packetLength, item_start, item_end); + ccnxTlvDictionary_PutBuffer(packetDictionary, translatedKey, itemBuffer); + parcBuffer_Release(&itemBuffer); +} + +/** + * The extent should be treated like a CCNxName, so decode it and add it as a CCNxName. + */ +static void +addName(TestData *data, CCNxTlvDictionary *packetDictionary, size_t item_start, size_t item_end, uint32_t translatedKey) +{ + // we need to backup 4 bytes to the the TLV container + PARCBuffer *itemBuffer = parcBuffer_Wrap(data->packet, data->packetLength, item_start - 4, item_end); + + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(itemBuffer); + CCNxName *name = ccnxCodecSchemaV1NameCodec_Decode(decoder, CCNxCodecSchemaV1Types_CCNxMessage_Name); + ccnxCodecTlvDecoder_Destroy(&decoder); + + ccnxTlvDictionary_PutName(packetDictionary, translatedKey, name); + ccnxName_Release(&name); + parcBuffer_Release(&itemBuffer); +} + + +/** + * Called on the body of a content object, does not include the fixed header or optional headers + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +static void +buildContentObjectDictionary(TestData *data, CCNxTlvDictionary *packetDictionary, TlvExtent extent) +{ + size_t start = extent.offset; + size_t end = start + extent.length; + + ccnxTlvDictionary_SetMessageType_ContentObject(packetDictionary, CCNxTlvDictionary_SchemaVersion_V1); + + for (int i = 0; data->truthTable[i].indexOrKey != T_INVALID; i++) { + size_t item_start = data->truthTable[i].extent.offset; + size_t item_end = item_start + data->truthTable[i].extent.length; + + // Is this item included in the given extent? + if (start < item_start && item_end <= end) { + // Is it a container or a nested dictionary? This check only applies to a ContentObject + if (!isContentObjectContainer(data->truthTable[i].indexOrKey)) { + uint32_t translatedKey = translateTestDataManifestToSchemaKey(data->truthTable[i].indexOrKey); + + if (data->truthTable[i].indexOrKey == V1_MANIFEST_OBJ_NAME) { + addName(data, packetDictionary, item_start, item_end, translatedKey); + } else { + addBuffer(data, packetDictionary, item_start, item_end, translatedKey); + } + } + } + } +} + +static void +buildInterestDictionary(TestData *data, CCNxTlvDictionary *packetDictionary, TlvExtent extent) +{ + size_t start = extent.offset; + size_t end = start + extent.length; + + ccnxTlvDictionary_SetMessageType_Interest(packetDictionary, CCNxTlvDictionary_SchemaVersion_V1); + + for (int i = 0; data->truthTable[i].indexOrKey != T_INVALID && data->truthTable[i].bodyManifest; i++) { + size_t item_start = data->truthTable[i].extent.offset; + size_t item_end = item_start + data->truthTable[i].extent.length; + + // Is this item included in the given extent? + if (start < item_start && item_end <= end) { + uint32_t translatedKey = translateTestDataManifestToSchemaKey(data->truthTable[i].indexOrKey); + + if (data->truthTable[i].indexOrKey == V1_MANIFEST_INT_NAME) { + addName(data, packetDictionary, item_start, item_end, translatedKey); + } else { + addBuffer(data, packetDictionary, item_start, item_end, translatedKey); + } + } + } +} + +/** + * Make a dictionary entry for everything inside the selected extent, not including it + * + * Use the truth table and for each listed item whose extent is within the given extent, + * add a dictionary entry + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +static void +buildMessageDictionary(TestData *data, CCNxTlvDictionary *dictionary, TlvExtent extent) +{ + uint8_t packetType = data->packet[1]; + switch (packetType) { + case CCNxCodecSchemaV1Types_PacketType_Interest: + buildInterestDictionary(data, dictionary, extent); + break; + + case CCNxCodecSchemaV1Types_PacketType_ContentObject: + buildContentObjectDictionary(data, dictionary, extent); + break; + + case CCNxCodecSchemaV1Types_PacketType_InterestReturn: + trapNotImplemented("not implemented"); + break; + + default: + trapIllegalValue(packetType, "Unknown PacketType"); + } +} + +static void +buildSetDictionaryType(TestData *data, CCNxTlvDictionary *dictionary) +{ + uint8_t packetType = data->packet[1]; + switch (packetType) { + case CCNxCodecSchemaV1Types_PacketType_Interest: + ccnxTlvDictionary_SetMessageType_Interest(dictionary, CCNxTlvDictionary_SchemaVersion_V1); + break; + + case CCNxCodecSchemaV1Types_PacketType_ContentObject: + ccnxTlvDictionary_SetMessageType_ContentObject(dictionary, CCNxTlvDictionary_SchemaVersion_V1); + break; + + case CCNxCodecSchemaV1Types_PacketType_InterestReturn: + trapNotImplemented("not implemented"); + break; + + default: + trapIllegalValue(packetType, "Unknown PacketType"); + } +} + +/** + * Builds a packet dictionary with OptionalHeaders and Message + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +static void +buildPacketDictionary(TestData *data, CCNxTlvDictionary *packetDictionary, TlvExtent extent) +{ + buildSetDictionaryType(data, packetDictionary); + + size_t start = extent.offset; + size_t end = start + extent.length; + + for (int i = 0; data->truthTable[i].indexOrKey != T_INVALID; i++) { + size_t item_start = data->truthTable[i].extent.offset; + size_t item_end = item_start + data->truthTable[i].extent.length; + + // Is this item included in the given extent? + if (start < item_start && item_end <= end) { + if (data->truthTable[i].bodyManifest == false) { + PARCBuffer *itemBuffer = parcBuffer_Wrap(data->packet, data->packetLength, item_start, item_end); + uint32_t translatedKey = translateOldOptionalHeadersManifestToNewKey(packetDictionary, data->truthTable[i].indexOrKey); + ccnxTlvDictionary_PutBuffer(packetDictionary, translatedKey, itemBuffer); + parcBuffer_Release(&itemBuffer); + } else { + buildMessageDictionary(data, packetDictionary, data->truthTable[i].extent); + } + + // advance start to skip over whatever we just included + start = item_end; + } + } +} + +/** + * Wraps the given (packet, length) in a PARCBuffer where the data->memoryRegion member will be set + * to a given extent within that PARCBuffer. The function will locate the truthTableKey in the truthTable and + * use it's extent as the bounds for the wrapped packet. + * + * For example, if the key V1_INT_NAME has the extent {32, 12}, then the PARCBuffer will wrap the packet + * memory and it will have and offset of 32 and a limit of 12. + */ +TestData * +commonSetup(uint8_t *packet, size_t length, TruthTableEntry *truthTable, int truthTableKey) +{ + TestData *data = parcMemory_AllocateAndClear(sizeof(TestData)); + assertNotNull(data, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(TestData)); + + TlvExtent extent = getTruthTableExtent(truthTable, truthTableKey); + + data->memoryRegion = parcBuffer_Wrap(packet, length, extent.offset, extent.offset + extent.length); + data->encoder = ccnxCodecTlvEncoder_Create(); + ccnxCodecTlvEncoder_Initialize(data->encoder); + + // use the content object lenghts, they are the largest + data->dictionary = ccnxTlvDictionary_Create(CCNxCodecSchemaV1TlvDictionary_MessageFastArray_END, CCNxCodecSchemaV1TlvDictionary_Lists_END); + + data->packet = packet; + data->packetLength = length; + data->truthTable = truthTable; + + buildMessageDictionary(data, data->dictionary, extent); + return data; +} + +/** + * Wraps a packet like commonSetup, but will do the whole packet including headers not just + * the message body. This is used by the PacketEncoder tests. + */ +TestData * +testrigencoder_CommonSetupWholePacket(uint8_t *packet, size_t length, TruthTableEntry *truthTable) +{ + TestData *data = parcMemory_AllocateAndClear(sizeof(TestData)); + assertNotNull(data, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(TestData)); + + data->memoryRegion = parcBuffer_Wrap(packet, length, 0, length); + data->encoder = ccnxCodecTlvEncoder_Create(); + + // use the content object lenghts, they are the largest + data->dictionary = ccnxTlvDictionary_Create(CCNxCodecSchemaV1TlvDictionary_MessageFastArray_END, CCNxCodecSchemaV1TlvDictionary_Lists_END); + + data->packet = packet; + data->packetLength = length; + data->truthTable = truthTable; + + buildPacketDictionary(data, data->dictionary, (TlvExtent) { 0, length }); + + return data; +} + +void +testrigencoder_CommonTeardown(TestData *data) +{ + ccnxTlvDictionary_Release(&data->dictionary); + ccnxCodecTlvEncoder_Destroy(&data->encoder); + parcBuffer_Release(&data->memoryRegion); + + if (data->signer) { + parcSigner_Release(&data->signer); + } + + parcMemory_Deallocate((void **) &data); +} + +void +testDisplayIoVec(CCNxCodecEncodingBufferIOVec *vec) +{ + printf("Display iovec %p with %d elements\n", (void *) vec, vec->iovcnt); + size_t totalLength = 0; + for (int i = 0; i < vec->iovcnt; i++) { + totalLength += vec->iov[i].iov_len; + printf(" %3d: base %p length %4zu total length %4zu\n", i, (void *) vec->iov[i].iov_base, vec->iov[i].iov_len, totalLength); + } + printf("done\n\n"); +} + +void +testExecute(TestData *data, ssize_t (*encoderFunction)(CCNxCodecTlvEncoder *encoder, CCNxTlvDictionary *dictionary)) +{ + encoderFunction(data->encoder, data->dictionary); + testCompareEncoderToBuffer(data->encoder, data->memoryRegion); +} diff --git a/libccnx-common/ccnx/common/codec/schema_v1/test/testrig_packetwrapper.c b/libccnx-common/ccnx/common/codec/schema_v1/test/testrig_packetwrapper.c new file mode 100644 index 00000000..9a213b13 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/test/testrig_packetwrapper.c @@ -0,0 +1,516 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * This test rig sets up test data to wrap a packet and prepare it for use in a decoder + * + * A hand-encoded packet, such as from the testdata directory can be passed to commonSetup and then + * run automated tests against it based on its manifest. + * + * Example: + * @code + * + * static uint8_t object_nameC_keyid3_protoinfo[] = { + * 0x00, 0x02, 0x00, 110, // ver = 0, type = object, length = 110 + * 0x00, 0x00, 0x00, 5, // reserved = 0, header length = 5 + * // --------------------------- + * // snip middle of packet + * // ------------------------ + * // byte offset 76 + * 0x00, 0x03, 0x00, 26, // Protocol Information, length = 26 + * 0x00, 0x0B, 0x00, 17, // Object Metadata, length = 17 + * 0x00, 0x0C, 0x00, 0x01, // Object Type, length = 1 + * 0x04, // LINK + * 0x00, 0x0D, 0x00, 8, // Creation Time + * 0x00, 0x00, 0x01, 0x43, // 1,388,534,400,000 msec + * 0x4B, 0x19, 0x84, 0x00, + * 0x00, 0x19, 0x00, 0x01, // EndSegment, length = 1 + * 42, + * // --------------------------- + * // snip to end of packet + * // --------------------------- + * }; + * + * static TruthTableEntry TRUTHTABLENAME(object_nameC_keyid3_protoinfo)[] = { + * { .wellKnownType = true, .indexOrKey = MANIFEST_OBJ_METADATA, .bodyManifest=true, .extent = { 80, 17} }, + * { .wellKnownType = true, .indexOrKey = MANIFEST_OBJ_OBJ_TYPE, .bodyManifest=true, .extent = { 84, 1} }, + * { .wellKnownType = false, .indexOrKey = T_INVALID, .extent = { 0, 0} }, + * }; + * + * LONGBOW_TEST_FIXTURE_SETUP(Global) + * { + * commonSetup(testCase, object_nameC_keyid3_protoinfo, sizeof(object_nameC_keyid3_protoinfo), object_nameC_keyid3_protoinfo_truthTableEntries, MANIFEST_OBJ_METADATA); + * return LONGBOW_STATUS_SUCCEEDED; + * } + * + * LONGBOW_TEST_CASE(Global, ccnxCodecSchemaV0ProtoInfo_GetEndSegmentNumber) + * { + * testInt32Getter(testCase, MANIFEST_OBJ_OBJ_TYPE, ccnxCodecSchemaV0Metadata_Decode, ccnxCodecSchemaV0Metadata_GetContentType); + * } + * + * @endcode + * + */ + +#include <inttypes.h> +#include <stdio.h> + +#include <ccnx/common/codec/ccnxCodec_TlvUtilities.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_NameCodec.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_HashCodec.h> +#include <ccnx/common/codec/schema_v1/testdata/v1_testrig_truthTable.h> + +/** + * Finds a row in the truthtable where bodyManifest is TRUE and the + * indexOrKey equals 'key'. + */ +TlvExtent +getTruthTableExtent(TruthTableEntry *ttentry, int key) +{ + for (int i = 0; ttentry[i].indexOrKey != T_INVALID; i++) { + if (ttentry[i].bodyManifest && ttentry[i].indexOrKey == key) { + return ttentry[i].extent; + } + } + return (TlvExtent) { 0, 0 }; +} + +/** + * Finds a row in the truthtable where bodyManifest is FALSE and the + * indexOrKey equals 'key'. + */ +TlvExtent +getTruthTableHeaderExtent(TruthTableEntry *ttentry, int key) +{ + for (int i = 0; ttentry[i].indexOrKey != T_INVALID; i++) { + if (!ttentry[i].bodyManifest && ttentry[i].indexOrKey == key) { + return ttentry[i].extent; + } + } + return (TlvExtent) { 0, 0 }; +} + +typedef struct test_data { + PARCBuffer *interest; + CCNxCodecTlvDecoder *decoder; + CCNxTlvDictionary *dictionary; + + uint8_t *packet; + size_t packetLength; + TruthTableEntry *truthTable; +} TestData; + +/** + * Wraps the given (packet, length) in a PARCBuffer where the data->memoryRegion member will be set + * to a given extent within that PARCBuffer. The function will locate the truthTableKey in the truthTable and + * use it's extent as the bounds for the wrapped packet. + * + * For example, if the key V1_INT_NAME has the extent {32, 12}, then the PARCBuffer will wrap the packet + * memory and it will have and offset of 32, position 0, and a limit of 12. + */ +TestData * +commonSetup(uint8_t *packet, size_t length, TruthTableEntry *truthTable, int truthTableKey) +{ + TestData *data = parcMemory_AllocateAndClear(sizeof(TestData)); + assertNotNull(data, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(TestData)); + + TlvExtent extent = getTruthTableExtent(truthTable, truthTableKey); + + data->interest = parcBuffer_Wrap(packet, length, extent.offset, extent.offset + extent.length); + data->decoder = ccnxCodecTlvDecoder_Create(data->interest); + + // content objects have more fields than interests, so use that + data->dictionary = ccnxTlvDictionary_Create(CCNxCodecSchemaV1TlvDictionary_MessageFastArray_END, CCNxCodecSchemaV1TlvDictionary_Lists_END); + + data->packet = packet; + data->packetLength = length; + data->truthTable = truthTable; + return data; +} + +void +commonSetupWholePacket(LongBowTestCase *testCase, uint8_t *packet, size_t length, TruthTableEntry *truthTable) +{ + TestData *data = parcMemory_AllocateAndClear(sizeof(TestData)); + assertNotNull(data, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(TestData)); + + data->interest = parcBuffer_Wrap(packet, length, 0, length); + data->decoder = ccnxCodecTlvDecoder_Create(data->interest); + data->dictionary = ccnxTlvDictionary_Create(CCNxCodecSchemaV1TlvDictionary_MessageFastArray_END, CCNxCodecSchemaV1TlvDictionary_Lists_END); + + data->packet = packet; + data->packetLength = length; + data->truthTable = truthTable; + + longBowTestCase_SetClipBoardData(testCase, data); +} + +void +commonTeardown(TestData *data) +{ + ccnxTlvDictionary_Release(&data->dictionary); + ccnxCodecTlvDecoder_Destroy(&data->decoder); + parcBuffer_Release(&data->interest); + parcMemory_Deallocate((void **) &data); +} + +/** + * Tests that an int32_t getter returns the right value + * + * Given a packet byte array and a truth table (see transport/test_tools/testdata/testrig_truthTable.h), + * check that the buffer the decoder parsed is the right buffer. + * + * The function will run the specified decoder on the TestData's packet and put the results in the + * TestData's dictionary. It will then call the specified getter and make sure its value is equal + * to the truth table's value. + * + * The function will assert if the test fails. + * + * @param [in] testCase Used to get the clipboard data with the TestData member + * @param [in] truthTableKey Used to match the .indexOrKey truth table member + * @param [in] containerDecoder The decoder to use on the packet contained in TestData + * @param [in] getter The function to call to fetch a decoded value + * + * Example: + * @code + * + * static uint8_t object_nameC_keyid3_protoinfo[] = { + * 0x00, 0x02, 0x00, 110, // ver = 0, type = object, length = 110 + * 0x00, 0x00, 0x00, 5, // reserved = 0, header length = 5 + * // --------------------------- + * // snip middle of packet + * // ------------------------ + * // byte offset 76 + * 0x00, 0x03, 0x00, 26, // Protocol Information, length = 26 + * 0x00, 0x0B, 0x00, 17, // Object Metadata, length = 17 + * 0x00, 0x0C, 0x00, 0x01, // Object Type, length = 1 + * 0x04, // LINK + * 0x00, 0x0D, 0x00, 8, // Creation Time + * 0x00, 0x00, 0x01, 0x43, // 1,388,534,400,000 msec + * 0x4B, 0x19, 0x84, 0x00, + * 0x00, 0x19, 0x00, 0x01, // EndSegment, length = 1 + * 42, + * // --------------------------- + * // snip to end of packet + * // --------------------------- + * }; + * + * static TruthTableEntry TRUTHTABLENAME(object_nameC_keyid3_protoinfo)[] = { + * { .wellKnownType = true, .indexOrKey = MANIFEST_OBJ_METADATA, .bodyManifest=true, .extent = { 80, 17} }, + * { .wellKnownType = true, .indexOrKey = MANIFEST_OBJ_OBJ_TYPE, .bodyManifest=true, .extent = { 84, 1} }, + * { .wellKnownType = false, .indexOrKey = T_INVALID, .extent = { 0, 0} }, + * }; + * + * LONGBOW_TEST_FIXTURE_SETUP(Global) + * { + * commonSetup(testCase, object_nameC_keyid3_protoinfo, sizeof(object_nameC_keyid3_protoinfo), object_nameC_keyid3_protoinfo_truthTableEntries, MANIFEST_OBJ_METADATA); + * return LONGBOW_STATUS_SUCCEEDED; + * } + * + * LONGBOW_TEST_CASE(Global, ccnxCodecSchemaV0ProtoInfo_GetEndSegmentNumber) + * { + * testInt32Getter(testCase, MANIFEST_OBJ_OBJ_TYPE, ccnxCodecSchemaV0Metadata_Decode, ccnxCodecSchemaV0Metadata_GetContentType); + * } + * * @endcode + */ +void +testInt32Getter(LongBowTestCase *testCase, int truthTableKey, bool containerDecoder(CCNxCodecTlvDecoder *, CCNxTlvDictionary *), + int32_t (*getter)(CCNxTlvDictionary *)) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + containerDecoder(data->decoder, data->dictionary); + int32_t testvalue = getter(data->dictionary); + + // look up the true name buffer from the truth table + TlvExtent extent = getTruthTableExtent(data->truthTable, truthTableKey); + PARCBuffer *truthbuffer = parcBuffer_Wrap(data->packet, data->packetLength, extent.offset, extent.offset + extent.length); + uint64_t truthvalue = -2; + ccnxCodecTlvUtilities_GetVarInt(truthbuffer, parcBuffer_Remaining(truthbuffer), &truthvalue); + + parcBuffer_Release(&truthbuffer); + + assertTrue(testvalue == (int32_t) truthvalue, "Wrong value, got %d expected %d", testvalue, (int32_t) truthvalue); +} + +/** + * Tests that an int64_t getter returns the right value + * + * Given a packet byte array and a truth table (see transport/test_tools/testdata/testrig_truthTable.h), + * check that the buffer the decoder parsed is the right buffer. + * + * The function will run the specified decoder on the TestData's packet and put the results in the + * TestData's dictionary. It will then call the specified getter and make sure its value is equal + * to the truth table's value. + * + * The function will assert if the test fails. + * + * @param [in] testCase Used to get the clipboard data with the TestData member + * @param [in] truthTableKey Used to match the .indexOrKey truth table member + * @param [in] containerDecoder The decoder to use on the packet contained in TestData + * @param [in] getter The function to call to fetch a decoded value + * + * Example: + * @code + * + * static uint8_t object_nameC_keyid3_protoinfo[] = { + * 0x00, 0x02, 0x00, 110, // ver = 0, type = object, length = 110 + * 0x00, 0x00, 0x00, 5, // reserved = 0, header length = 5 + * // --------------------------- + * // snip middle of packet + * // ------------------------ + * // byte offset 76 + * 0x00, 0x03, 0x00, 26, // Protocol Information, length = 26 + * 0x00, 0x0B, 0x00, 17, // Object Metadata, length = 17 + * 0x00, 0x0C, 0x00, 0x01, // Object Type, length = 1 + * 0x04, // LINK + * 0x00, 0x0D, 0x00, 8, // Creation Time + * 0x00, 0x00, 0x01, 0x43, // 1,388,534,400,000 msec + * 0x4B, 0x19, 0x84, 0x00, + * 0x00, 0x19, 0x00, 0x01, // EndSegment, length = 1 + * 42, + * // --------------------------- + * // snip to end of packet + * // --------------------------- + * }; + * + * static TruthTableEntry TRUTHTABLENAME(object_nameC_keyid3_protoinfo)[] = { + * { .wellKnownType = true, .indexOrKey = MANIFEST_OBJ_PROTOINFO, .bodyManifest=true, .extent = { 76, 26} }, + * { .wellKnownType = true, .indexOrKey = MANIFEST_OBJ_ENDSEGMENT, .bodyManifest=true, .extent = {101, 1} }, + * { .wellKnownType = false, .indexOrKey = T_INVALID, .extent = { 0, 0} }, + * }; + * + * LONGBOW_TEST_FIXTURE_SETUP(Global) + * { + * commonSetup(testCase, object_nameC_keyid3_protoinfo, sizeof(object_nameC_keyid3_protoinfo), object_nameC_keyid3_protoinfo_truthTableEntries, MANIFEST_OBJ_PROTOINFO); + * return LONGBOW_STATUS_SUCCEEDED; + * } + * + * LONGBOW_TEST_CASE(Global, ccnxCodecSchemaV0ProtoInfo_GetEndSegmentNumber) + * { + * testInt64Getter(testCase, MANIFEST_OBJ_ENDSEGMENT, ccnxCodecSchemaV0ProtoInfo_Decode, ccnxCodecSchemaV0ProtoInfo_GetEndSegmentNumber); + * } + * + * @endcode + */ +void +testInt64Getter(TestData *data, int truthTableKey, bool containerDecoder(CCNxCodecTlvDecoder *, CCNxTlvDictionary *), + int64_t (*getter)(CCNxTlvDictionary *)) +{ + containerDecoder(data->decoder, data->dictionary); + int64_t testvalue = getter(data->dictionary); + + // look up the true name buffer from the truth table + TlvExtent extent = getTruthTableExtent(data->truthTable, truthTableKey); + PARCBuffer *truthbuffer = parcBuffer_Wrap(data->packet, data->packetLength, extent.offset, extent.offset + extent.length); + uint64_t truthvalue = -2; + ccnxCodecTlvUtilities_GetVarInt(truthbuffer, parcBuffer_Remaining(truthbuffer), &truthvalue); + + parcBuffer_Release(&truthbuffer); + + assertTrue(testvalue == (int64_t) truthvalue, "Wrong value, got %" PRId64 " expected %" PRId64, testvalue, (int64_t) truthvalue); +} + +/** + * Tests that a buffer getter returns the right buffer + * + * Given a packet byte array and a truth table (see transport/test_tools/testdata/testrig_truthTable.h), + * check that the buffer the decoder parsed is the right buffer. + * + * The function will run the specified decoder on the TestData's packet and put the results in the + * TestData's dictionary. It will then call the specified getter and make sure its value is equal + * to the truth table's value. + * + * The function will assert if the test fails. + * + * @param [in] testCase Used to get the clipboard data with the TestData member + * @param [in] truthTableKey Used to match the .indexOrKey truth table member + * @param [in] containerDecoder The decoder to use on the packet contained in TestData + * @param [in] getter The function to call to fetch a decoded value + * + * Example: + * @code + * + * static uint8_t object_nameC_keyid3_protoinfo[] = { + * 0x00, 0x02, 0x00, 110, // ver = 0, type = object, length = 110 + * 0x00, 0x00, 0x00, 5, // reserved = 0, header length = 5 + * // --------------------------- + * // snip middle of packet + * // ------------------------ + * // byte offset = 117 + * 0x00, 0x05, 0x00, 0x06, // signature block, length = 6 + * 0x00, 0x0E, 0x00, 0x02, // signature bits, length = 2 + * 0xBE, 0xEF // value = 0xBEEF + * }; + * + * static TruthTableEntry TRUTHTABLENAME(object_nameC_keyid3_protoinfo)[] = { + * { .wellKnownType = true, .indexOrKey = MANIFEST_OBJ_SIGBLOCK, .bodyManifest=true, .extent = {117, 6} }, + * { .wellKnownType = true, .indexOrKey = MANIFEST_OBJ_SIGBITS, .bodyManifest=true, .extent = {121, 2} }, + * { .wellKnownType = false, .indexOrKey = T_INVALID, .extent = { 0, 0} }, + * }; + * + * LONGBOW_TEST_FIXTURE_SETUP(Global) + * { + * commonSetup(testCase, object_nameC_keyid3_protoinfo, sizeof(object_nameC_keyid3_protoinfo), object_nameC_keyid3_protoinfo_truthTableEntries, MANIFEST_OBJ_SIGBLOCK); + * return LONGBOW_STATUS_SUCCEEDED; + * } + * + * LONGBOW_TEST_CASE(Global, ccnxCodecSchemaV0SigBlock_GetSignatureBits) + * { + * testBufferGetter(testCase, MANIFEST_OBJ_SIGBITS, ccnxCodecSchemaV0SigBlock_Decode, ccnxCodecSchemaV0SigBlock_GetSignatureBits); + * } + * + * @endcode + */ +void +testBufferGetter(TestData *data, int truthTableKey, bool containerDecoder(CCNxCodecTlvDecoder *, CCNxTlvDictionary *), + PARCBuffer *(*getter)(const CCNxTlvDictionary *)) +{ + containerDecoder(data->decoder, data->dictionary); + PARCBuffer *test = getter(data->dictionary); + + // look up the true name buffer from the truth table + TlvExtent extent = getTruthTableExtent(data->truthTable, truthTableKey); + PARCBuffer *truth = parcBuffer_Wrap(data->packet, data->packetLength, extent.offset, extent.offset + extent.length); + + assertTrue(parcBuffer_Equals(test, truth), "Buffers not equal") + { + printf("Expected:\n"); + parcBuffer_Display(truth, 3); + printf("Got:\n"); + parcBuffer_Display(test, 3); + } + + parcBuffer_Release(&truth); +} + +void +testHashGetter(TestData *data, int truthTableKey, bool containerDecoder(CCNxCodecTlvDecoder *, CCNxTlvDictionary *), + PARCCryptoHash *(*getter)(CCNxTlvDictionary *)) +{ + containerDecoder(data->decoder, data->dictionary); + PARCCryptoHash *testHash = getter(data->dictionary); + + // look up the true hash buffer from the truth table + TlvExtent extent = getTruthTableExtent(data->truthTable, truthTableKey); + PARCBuffer *truthBuffer = parcBuffer_Wrap(data->packet, data->packetLength, extent.offset, extent.offset + extent.length); + + // decode the hash value + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(truthBuffer); + PARCCryptoHash *truthHash = ccnxCodecSchemaV1HashCodec_DecodeValue(decoder, extent.length); + ccnxCodecTlvDecoder_Destroy(&decoder); + + // compare the decoded value against the expected value + assertTrue(parcCryptoHash_Equals(testHash, truthHash), "Hashes not equal") + { + printf("Expected:\n"); + printf(" %s\n", parcBuffer_ToHexString(parcCryptoHash_GetDigest(truthHash))); + printf("Got:\n"); + printf(" %s\n", parcBuffer_ToHexString(parcCryptoHash_GetDigest(testHash))); + } + + parcCryptoHash_Release(&truthHash); + parcBuffer_Release(&truthBuffer); +} + +void +testNameGetter(TestData *data, int truthTableKey, bool containerDecoder(CCNxCodecTlvDecoder *, CCNxTlvDictionary *), + CCNxName *(*getter)(CCNxTlvDictionary *)) +{ + containerDecoder(data->decoder, data->dictionary); + CCNxName *test = getter(data->dictionary); + + // look up the true name buffer from the truth table + TlvExtent extent = getTruthTableExtent(data->truthTable, truthTableKey); + + // we need to backup 4 bytes to get the TLV container + PARCBuffer *truthBuffer = + parcBuffer_Wrap(data->packet, data->packetLength, extent.offset - 4, extent.offset + extent.length); + + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(truthBuffer); + CCNxName *truthName = ccnxCodecSchemaV1NameCodec_Decode(decoder, CCNxCodecSchemaV1Types_CCNxMessage_Name); + ccnxCodecTlvDecoder_Destroy(&decoder); + + assertTrue(ccnxName_Equals(test, truthName), "Names not equal") + { + printf("Expected:\n"); + ccnxName_Display(truthName, 3); + printf("Got:\n"); + ccnxName_Display(test, 3); + } + + ccnxName_Release(&truthName); + parcBuffer_Release(&truthBuffer); +} + +void +testMissingInt32Getter(LongBowTestCase *testCase, int32_t (*getter)(CCNxTlvDictionary *)) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + int32_t test = getter(data->dictionary); + + assertTrue(test == -1, "Wrong value, got %d expected %d", test, -1); +} + +/** + * Tried to retrieve execute the getter on the dictionary and ensures that + * the field is missing. + */ +void +testMissingInt64Getter(LongBowTestCase *testCase, int64_t (*getter)(CCNxTlvDictionary *)) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + int64_t test = getter(data->dictionary); + + assertTrue(test == -1, "Wrong value, got %" PRId64 " expected %d", test, -1); +} + +/** + * Tried to retrieve execute the getter on the dictionary and ensures that + * the field is missing. + */ +void +testMissingNameGetter(LongBowTestCase *testCase, CCNxName *(*getter)(CCNxTlvDictionary *)) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + CCNxName *test = getter(data->dictionary); + + assertNull(test, "Should have gotten null for missing field, got %p", (void *) test); +} + +/** + * Tried to retrieve execute the getter on the dictionary and ensures that + * the field is missing. + */ +void +testMissingBufferGetter(LongBowTestCase *testCase, PARCBuffer *(*getter)(CCNxTlvDictionary *)) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCBuffer *test = getter(data->dictionary); + + assertNull(test, "Should have gotten null for missing field, got %p", (void *) test); +} + +/** + * Tried to retrieve execute the getter on the dictionary and ensures that + * the field is missing. + */ +void +testMissingDictionaryGetter(LongBowTestCase *testCase, CCNxTlvDictionary *(*getter)(CCNxTlvDictionary *)) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + CCNxTlvDictionary *test = getter(data->dictionary); + + assertNull(test, "Should have gotten null for missing field, got %p", (void *) test); +} diff --git a/libccnx-common/ccnx/common/codec/schema_v1/testdata/CMakeLists.txt b/libccnx-common/ccnx/common/codec/schema_v1/testdata/CMakeLists.txt new file mode 100644 index 00000000..c5c70ce4 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/testdata/CMakeLists.txt @@ -0,0 +1,27 @@ +# Enable gcov output for the tests +add_definitions(--coverage) +set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} " --coverage") + +set(TestsExpectedToPass + test_ccnxCodecSchemaV1_CryptoSuite + test_ccnxCodecSchemaV1_FixedHeaderDecoder + test_ccnxCodecSchemaV1_FixedHeaderEncoder + test_ccnxCodecSchemaV1_LinkCodec + test_ccnxCodecSchemaV1_MessageDecoder + test_ccnxCodecSchemaV1_MessageEncoder + test_ccnxCodecSchemaV1_NameCodec + test_ccnxCodecSchemaV1_NameSegmentCodec + test_ccnxCodecSchemaV1_OptionalHeadersDecoder + test_ccnxCodecSchemaV1_OptionalHeadersEncoder + test_ccnxCodecSchemaV1_PacketDecoder + test_ccnxCodecSchemaV1_PacketEncoder + test_ccnxCodecSchemaV1_TlvDictionary + test_ccnxCodecSchemaV1_ValidationDecoder + test_ccnxCodecSchemaV1_ValidationEncoder +) + + +foreach(test ${TestsExpectedToPass}) + AddTest(${test}) +endforeach() + diff --git a/libccnx-common/ccnx/common/codec/schema_v1/testdata/v1_CPISchema.h b/libccnx-common/ccnx/common/codec/schema_v1/testdata/v1_CPISchema.h new file mode 100644 index 00000000..70899d13 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/testdata/v1_CPISchema.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Defines the truthtable manifest constants used by the version 1 test vectors. + * + */ + +/** + * Schema for Control Plane Interface packets. These packets are a TLV wrapping + * the control JSON. + * + * Example: + * @code + * <#example#> + * @endcode + */ + +#ifndef Libccnx_v1_CPISchema_h +#define Libccnx_v1_CPISchema_h + +#include <ccnx/common/codec/testdata/tlv_Schema.h> + +// ----------------------------------------------------- +// these are the array indicies used to store the TlvExtent for the item +typedef enum { + V1_MANIFEST_CPI_PAYLOAD = 0, + V1_MANIFEST_CPI_SIGBITS = 5, // the payload of the signature + V1_MANIFEST_CPI_ValidationAlg = 6, // start of validation algorithm + V1_MANIFEST_CPI_ValidationPayload = 7, // start of validation payload + V1_MANIFEST_CPI_BODYEND = 8 +} V1_ManifestCPIBody; + +typedef enum { + V1_MANIFEST_CPI_HEADEND = 0 +} V1_ManifestCPIHeaders; + +#endif // Libccnx_tlv_CPISchema_h diff --git a/libccnx-common/ccnx/common/codec/schema_v1/testdata/v1_ContentObjectSchema.h b/libccnx-common/ccnx/common/codec/schema_v1/testdata/v1_ContentObjectSchema.h new file mode 100644 index 00000000..cb59d937 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/testdata/v1_ContentObjectSchema.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * This is from the version 1 codec. All the test vectors in this directory (e.g. interest_nameA.h) + * are encoded using these constants. These are no longer used for any functional code, only to interpret the test vectors. + * + */ + +#ifndef Libccnx_v1_ContentObjectSchema_h +#define Libccnx_v1_ContentObjectSchema_h + +#include <ccnx/common/codec/testdata/tlv_Schema.h> + +#define T_CONTENTOBJECT 0x0002 + +// these are the array indicies used to store the TlvExtent for the item +typedef enum { + // top level entities + V1_MANIFEST_OBJ_NAME = 0, + V1_MANIFEST_OBJ_CONTENTOBJECT = 1, // the top container + V1_MANIFEST_OBJ_NAMEAUTH = 2, + V1_MANIFEST_OBJ_PAYLOADTYPE = 3, + V1_MANIFEST_OBJ_PAYLOAD = 4, + V1_MANIFEST_OBJ_SIGBITS = 5, + + // inside the name authenticator + V1_MANIFEST_OBJ_KEYID = 6, + V1_MANIFEST_OBJ_CRYPTO_SUITE = 7, + V1_MANIFEST_OBJ_KEY = 8, + V1_MANIFEST_OBJ_CERT = 9, + V1_MANIFEST_OBJ_KEYNAME = 10, + V1_MANIFEST_OBJ_KEYNAME_NAME = 11, + V1_MANIFEST_OBJ_KEYNAME_OBJHASH = 12, + + // inside the protocol information + V1_MANIFEST_OBJ_METADATA = 13, + + // inside metadata + V1_MANIFEST_OBJ_OBJ_TYPE = 14, + V1_MANIFEST_OBJ_CREATE_TIME = 15, + V1_MANIFEST_OBJ_EXPIRY_TIME = 16, + + // inside signature block + V1_MANIFEST_OBJ_ValidationPayload = 17, + V1_MANIFEST_OBJ_ENDSEGMENT = 18, + V1_MANIFEST_OBJ_PUBKEY = 19, + + V1_MANIFEST_OBJ_ValidationAlg = 20, + V1_MANIFEST_OBJ_SigningTime = 21, + V1_MANIFEST_OBJ_BODYEND = 22 +} SchemaV1ManifestContentObjectBody; + +typedef enum { + V1_MANIFEST_OBJ_OPTHEAD = 0, + V1_MANIFEST_OBJ_E2EFRAG = 2, + V1_MANIFEST_OBJ_FIXEDHEADER = 3, + V1_MANIFEST_OBJ_RecommendedCacheTime = 4, + V1_MANIFEST_OBJ_HEADEND = 5 +} SchemaV1ManifestContentObjectHeaders; + +#endif // Libccnx_tlv_ContentObjectSchema_h diff --git a/libccnx-common/ccnx/common/codec/schema_v1/testdata/v1_InterestSchema.h b/libccnx-common/ccnx/common/codec/schema_v1/testdata/v1_InterestSchema.h new file mode 100644 index 00000000..894b3cc7 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/testdata/v1_InterestSchema.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * This is from the version 0 codec. All the test vectors in this directory (e.g. interest_nameA.h) + * are encoded using these constants. These are no longer used for any functional code, only to interpret the test vectors. + * + */ + +#ifndef Libccnx_v1_InterestSchema_h +#define Libccnx_v1_InterestSchema_h + +#include <ccnx/common/codec/testdata/tlv_Schema.h> + +// ----------------------------------------------------- +// these are the array indicies used to store the TlvExtent for the item +typedef enum { + V1_MANIFEST_INT_INTEREST = 0, // start of Interest body to end + + V1_MANIFEST_INT_NAME = 1, + V1_MANIFEST_INT_KEYID = 2, + V1_MANIFEST_INT_OBJHASH = 3, + V1_MANIFEST_INT_PAYLOAD = 4, + V1_MANIFEST_INT_IPIDM = 5, + + V1_MANIFEST_INT_ValidationAlg = 6, // start of validation algorithm + V1_MANIFEST_INT_ValidationPayload = 7, // start of validation payload + + V1_MANIFEST_INT_BODYEND = 7 +} ScheamV1ManifestInterestBody; + +typedef enum { + V1_MANIFEST_INT_OPTHEAD = 0, // the start of the optional headers + V1_MANIFEST_INT_LIFETIME = 1, + V1_MANIFEST_INT_E2EFRAG = 2, + V1_MANIFEST_INT_HEADEND = 3, +} ScheamV1ManifestInterestHeaders; + + +#define T_INTEREST 0x0001 + +#endif // Libccnx_tlv_InterestSchema_h diff --git a/libccnx-common/ccnx/common/codec/schema_v1/testdata/v1_content_nameA_crc32c.h b/libccnx-common/ccnx/common/codec/schema_v1/testdata/v1_content_nameA_crc32c.h new file mode 100644 index 00000000..e4695849 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/testdata/v1_content_nameA_crc32c.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file interest_nameA_crc32c.h + * @brief Interest with CRC validation + * + * Ground truth set derived from CRC RevEng http://reveng.sourceforge.net + * e.g. reveng -c -m CRC-32C 313233343536373839 gives the canonical check value 0xe306928e + * + * You can also calcaulate CRC32C online at http://www.zorc.breitbandkatze.de/crc.html using + * CRC polynomial 0x1EDC6F41, init 0xFFFFFFFF, final 0xFFFFFFFF, reverse data bytes (check), + * and reverse CRC result before final XOR (check). + * + * you can get the packet dump from the "write_packets" command. here's the detailed steps. + * The -c size of 4 in steps 4 and 7 are chosen to make it easy to delete the right number of lines. + * there's nothing magic about the "4". + * + * 1) execute ./write_packets + * 2) xxd -r -c 8 v1_content_nameA_crc32c.txt > y + * 3) vim -b y + * 4) :%!xxd -p -c 4 + * 5) Delete the frist 44 bytes (11 lines). The first line should now be: + * 00020015 + * 6) Delete the last 8 bytes + * The last line two lines should be: + * 04000200 + * 00 + * What's left is the part to be signed. + * 7) :%!xxd -r -p -c 4 + * 8) :wq + * 9) dump the file to one long URL-escaped hex string with + * xxd -p -c 256 y | sed 's/[0-9a-f]\{2\}/%&/g' + * 10) Copy the hex string to the website and use the settings specified above (don't use 0x in front + * of any hex strings). Click "compute!" + * 11) The answer should be 2C3CC0Af + * 12) Put the byte array from (11) in the Validation Payload. + * + */ + +#ifndef v1_content_nameA_crc32c_h +#define v1_content_nameA_crc32c_h + +#include <ccnx/common/codec/testdata/testdata_common.h> +#include <ccnx/common/codec/schema_v1/testdata/v1_ContentObjectSchema.h> + +/** + * A well formed interest with only a name + */ + +__attribute__((unused)) +static uint8_t v1_content_nameA_crc32c[] = { + 0x01, 0x01, 0x00, 85, // ver = 1, type = content object, length = 85 + 0x00, 0x00, 0x00, 44, // HopLimit = 31, reserved = 0, header length = 44 + // ------------------------ + 0x00, 0x04, 0x00, 20, // ContentObject Fragment, length = 20 + 0x12, 0x23, 0x34, 0x45, + 0x56, 0x67, 0x78, 0x89, // fragid 0x1223344556677889 + 0x05, 0xDC, 0x01, 0x00, // MTU 1500, fragcnt 1, fragnum 0 + 0x01, 0x02, 0x03, 0x04, + 0x05, 0x06, 0x07, 0x08, // interest fragment 0x0102030405060708 + // ------------------------ + 0x00, 0x02, 0x00, 8, // Recommended Cache Time + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x6D, 0xDD, 0x00, // 2 hours (0x6DDD00 milli seconds) + // ------------------------ + 0x00, 0x02, 0x00, 21, // type = content object, length = 21 + // ------------------------ + 0x00, 0x00, 0x00, 0x11, // type = name, length = 17 + 0x00, 0x03, 0x00, 0x05, // type = binary, length = 5 + 'h', 'e', 'l', 'l', // "hello" + 'o', + 0xF0, 0x00, 0x00, 0x04, // type = app, length = 4 + 'o', 'u', 'c', 'h', // "ouch" + // ------------------------ + 0x00, 0x03, 0x00, 4, // validation alg, length = 4 + 0x00, 0x02, 0x00, 0x00, // CRC32C + // ------------------------ + 0x00, 0x04, 0x00, 4, // validation payload + 0x2C, 0x3C, 0xC0, 0xAF // 2C3CC0AF +}; + +__attribute__((unused)) +static TruthTableEntry +TRUTHTABLENAME(v1_content_nameA_crc32c)[] = +{ + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_OBJ_E2EFRAG, .bodyManifest = false, .extent = { 12, 20 } }, + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_OBJ_RecommendedCacheTime, .bodyManifest = false, .extent = { 36, 8 } }, + + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_OBJ_CONTENTOBJECT, .bodyManifest = true, .extent = { 48, 21 } }, + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_OBJ_NAME, .bodyManifest = true, .extent = { 52, 17 } }, + + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_OBJ_ValidationAlg, .bodyManifest = true, .extent = { 73, 4 } }, + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_OBJ_SIGBITS, .bodyManifest = true, .extent = { 81, 4 } }, + { .wellKnownType = false, .indexOrKey = T_INVALID, .extent = { 0, 0 } }, +}; + +#define v1_content_nameA_crc32c_truthTable TABLEENTRY(v1_content_nameA_crc32c, TLV_ERR_NO_ERROR) + +#define v1_content_nameA_crc32c_URI "lci:/3=hello/0xf000=ouch" + +#endif diff --git a/libccnx-common/ccnx/common/codec/schema_v1/testdata/v1_content_nameA_keyid1_rsasha256.h b/libccnx-common/ccnx/common/codec/schema_v1/testdata/v1_content_nameA_keyid1_rsasha256.h new file mode 100644 index 00000000..ef37fb5e --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/testdata/v1_content_nameA_keyid1_rsasha256.h @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file interest_nameA_crc32c.h + * @brief Interest with CRC validation + * + * Signature generated using "openssl sha -sign test_rsa_key.pem -sha256 -binary y > sig", where "y" is + * generated from the "xxd -r" of the message hex dump. You need to manually edit "y" so it only + * contains the parts begin signed. Then use "xxd -i sig" to get the byte array. + * + * you can get the dump from the "write_packets" command. here's the detailed steps: + * + * 1) execute ./write_packets + * 2) xxd -r -c 8 v1_content_nameA_keyid1_rsasha256.txt > y + * 3) vim -b y + * 4) :%!xxd -p -c 16 + * 5) Delete the frist 32 bytes (2 lines). The first line should now be: + * 0002003a000000110002000568656c6c + * 6) Delete the last 132 bytes (9 lines, 8 full lines plus the last 4 byte line) + * The last line in the file should now be: + * 0f3592ae7027fbd2e41e270203010001 + * What's left is the part to be signed. + * 7) :%!xxd -r -p -c 16 + * 8) :wq + * 9) Copy the PEM blocks below and put them in the file "key.pem". Make sure to remove + * any leading whitespace. If you get an error in the next command like + * "53999:error:0906D06C:PEM routines:PEM_read_bio:no start ... 648:Expecting: ANY PRIVATE KEY" then + * you most likely have leading whitespace. Make sure all lines are flush left. + * 10) openssl sha -sign key.pem -sha256 -binary y > sig + * 11) xxd -i sig + * 12) Put the byte array from (11) in the Validation Payload. Verify the length, it should be + * 128 bytes. If not, fixup the length of the ValidatonPayload and the PacketLength. + * + */ + +/* + * -----BEGIN RSA PRIVATE KEY----- + * MIICXAIBAAKBgQCn1pPF8XPGErX6ecXvGIvvqs0EAY+Ddz+xZqFauTkqsj4w+xH8 + * V/yd0S938Kt3rWYsJsibUW9pvyYQBinuy7AsXpEOJEKN5nWgTgRDDD5MBnRnrYTD + * 6PTFlHPEnyWoQga/RRnimBw2oUNNm3EI4YLf4k8qPD0PNZKucCf70uQeJwIDAQAB + * AoGAVOYPA/7aIGSQlu4IOKTDDG3qnM8pSEgG+PbAQgMVrspQ+TfXZj0ftLj++P3N + * zpDw8P6BVUfBQs2FNG/ZwEhaiZVgJAl7cIAxJ9Ac+1oZYSgGyJfb3u9iWvkbMOoj + * 83Inx5yyN+Qmk5zceH4pOC5D5cDAuGGZ740Euv4o2/2O3qECQQDTmWZw021PvEbA + * r18O1YfZGxO3zFCwFXCpnHvtSMbP+MXAG5Gt47wZt4Vx1rX9k78beeCUitwqp3d3 + * ZI+YlUu3AkEAyw5wssQsJty/n2FL8DbJN3UzUhkcaCFYrKz3RtFye9wu+Bw0TxPC + * 3jhFVcynm3nH3ZJN0JsnsPnHXuoQToShEQJATXC51hb6zZC5UDGel348fo9zUvP6 + * n8bo+ZoknL3izSBdtyYf1cUgBUVuGDCdYFWfPn4HXDXJx+6MQWzTRON21wJBAMZL + * U8M/z94jtP3wBjiPR/Dggz2pSBRofDAkuVZvM13BqByjbnHK2oIocY1YTlWGl6fJ + * ODR/UEODqS8HZOVIoAECQANcuvVnqDixSIl2ySZvydQytv4DKTbvE0nYSRroYIlJ + * PTOBPy8ynIUkJwc2E1BsLl7V8gO62a5O0ntTwBMnPSQ= + * -----END RSA PRIVATE KEY----- + * + * -----BEGIN PUBLIC KEY----- + * MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCn1pPF8XPGErX6ecXvGIvvqs0E + * AY+Ddz+xZqFauTkqsj4w+xH8V/yd0S938Kt3rWYsJsibUW9pvyYQBinuy7AsXpEO + * JEKN5nWgTgRDDD5MBnRnrYTD6PTFlHPEnyWoQga/RRnimBw2oUNNm3EI4YLf4k8q + * PD0PNZKucCf70uQeJwIDAQAB + * -----END PUBLIC KEY----- + */ + +#ifndef v1_content_nameA_keyid1_rsasha256_h +#define v1_content_nameA_keyid1_rsasha256_h + +#include <ccnx/common/codec/testdata/testdata_common.h> +#include <ccnx/common/codec/schema_v1/testdata/v1_ContentObjectSchema.h> + +/** + * A well formed interest with only a name + */ + + +__attribute__((unused)) +static uint8_t v1_content_nameA_keyid1_rsasha256[] = { + 0x01, 0x01, 0x01, 0xB4,// ver = 1, type = content object, length = 436 + 0x00, 0x00, 0x00, 32, // HopLimit = 0, reserved = 0, header length = 32 + // ------------------------ + 0x00, 0x04, 0x00, 20, // ContentObject Fragment, length = 20 + 0x12, 0x23, 0x34, 0x45, + 0x56, 0x67, 0x78, 0x89,// fragid 0x1223344556677889 + 0x05, 0xDC, 0x01, 0x00,// MTU 1500, fragcnt 1, fragnum 0 + 0x01, 0x02, 0x03, 0x04, + 0x05, 0x06, 0x07, 0x08,// interest fragment 0x0102030405060708 + // ------------------------ + 0x00, 0x02, 0x00, 58, // type = content object, length = 58 + // ------------------------ + 0x00, 0x00, 0x00, 17, // type = name, length = 17 + 0x00, 0x03, 0x00, 0x05,// type = binary, length = 5 + 'h', 'e', 'l', 'l', // "hello" + 'o', + 0xF0, 0x00, 0x00, 0x04,// type = app, length = 4 + 'o', 'u', 'c', 'h', // "ouch" + // ------------------------ + 0x00, 0x05, 0x00, 1, // PayloadType + 1, // type 1 = key + 0x00, 0x06, 0x00, 0x08,// expiry time in msec + 0x00, 0x00, 0x01, 0x43,// 1,388,534,400,000 msec + 0x4B, 0x19, 0x84, 0x00, + 0x00, 0x19, 0x00, 4, // end chunk number + 0x06, 0x05, 0x04, 0x03, + // ------------------------ + 0x00, 0x01, 0x00, 8, // payload, length = 8 + 0x73, 0x75, 0x72, 0x70, + 0x72, 0x69, 0x73, 0x65, + // ------------------------ + 0x00, 0x03, 0x00, 206, // validation alg, length = 206 + 0x00, 0x06, 0x00, 202, // RSA-SHA256, length = 162 + 4 + 32 + 4 = 202 + 0x00, 0x09, 0x00, 32, // type = keyid, length = 32 + 0x5c, 0x23, 0x4c, 0x28, + 0x50, 0xda, 0x20, 0x7b, + 0x88, 0x25, 0x8b, 0xf3, + 0x62, 0x61, 0x96, 0xd8, + 0xf0, 0x60, 0x76, 0x38, + 0xa2, 0xd4, 0xe0, 0xe2, + 0x49, 0xb2, 0xa9, 0xaf, + 0xce, 0xb8, 0x85, 0x59, + 0x00, 0x0B, 0x00, 162, // public key, length = 162 + 0x30, 0x81, 0x9f, 0x30,0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x01,0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30, 0x81, + 0x89, 0x02, 0x81, 0x81,0x00, 0xa7, 0xd6, 0x93, 0xc5, 0xf1, 0x73, 0xc6, + 0x12, 0xb5, 0xfa, 0x79,0xc5, 0xef, 0x18, 0x8b, 0xef, 0xaa, 0xcd, 0x04, + 0x01, 0x8f, 0x83, 0x77,0x3f, 0xb1, 0x66, 0xa1, 0x5a, 0xb9, 0x39, 0x2a, + 0xb2, 0x3e, 0x30, 0xfb,0x11, 0xfc, 0x57, 0xfc, 0x9d, 0xd1, 0x2f, 0x77, + 0xf0, 0xab, 0x77, 0xad,0x66, 0x2c, 0x26, 0xc8, 0x9b, 0x51, 0x6f, 0x69, + 0xbf, 0x26, 0x10, 0x06,0x29, 0xee, 0xcb, 0xb0, 0x2c, 0x5e, 0x91, 0x0e, + 0x24, 0x42, 0x8d, 0xe6,0x75, 0xa0, 0x4e, 0x04, 0x43, 0x0c, 0x3e, 0x4c, + 0x06, 0x74, 0x67, 0xad,0x84, 0xc3, 0xe8, 0xf4, 0xc5, 0x94, 0x73, 0xc4, + 0x9f, 0x25, 0xa8, 0x42,0x06, 0xbf, 0x45, 0x19, 0xe2, 0x98, 0x1c, 0x36, + 0xa1, 0x43, 0x4d, 0x9b,0x71, 0x08, 0xe1, 0x82, 0xdf, 0xe2, 0x4f, 0x2a, + 0x3c, 0x3d, 0x0f, 0x35,0x92, 0xae, 0x70, 0x27, 0xfb, 0xd2, 0xe4, 0x1e, + 0x27, 0x02, 0x03, 0x01,0x00, 0x01, + // ------------------------ + 0x00, 0x04, 0x00, 128, // validation payload, length = 128 + 0x03, 0x46, 0xee, 0xb7,0x30, 0x1c, 0xea, 0x13, 0x0c, 0xce, 0x83, 0x5b, + 0x7b, 0x4f, 0xf5, 0x83,0x37, 0x08, 0x7f, 0xe0, 0xe1, 0xc9, 0x70, 0x09, + 0x5e, 0xc2, 0x1c, 0xd3,0x74, 0xbb, 0xbd, 0x72, 0x35, 0xa4, 0x1b, 0x0f, + 0x3d, 0x04, 0x5e, 0xf7,0xc1, 0xdf, 0xea, 0xc3, 0x50, 0x47, 0x14, 0xf9, + 0xb7, 0xbb, 0x42, 0xf9,0x3e, 0xaa, 0x49, 0xd2, 0x9f, 0xd1, 0xab, 0xf6, + 0xda, 0x32, 0x4a, 0xb1,0xb9, 0x69, 0x91, 0x57, 0x43, 0x5d, 0x06, 0xcf, + 0x1d, 0x9f, 0x7c, 0x28,0xee, 0x35, 0xaa, 0xd0, 0xb2, 0x8d, 0x34, 0x09, + 0xcd, 0xdb, 0x01, 0xf7,0xda, 0xe8, 0x59, 0x98, 0x4e, 0x59, 0xfa, 0x13, + 0xd0, 0xd1, 0x54, 0x8e,0x64, 0x8c, 0xc6, 0xd7, 0x6b, 0xc5, 0x89, 0xeb, + 0x37, 0x8f, 0x53, 0x04,0xba, 0x03, 0x05, 0xb4, 0x67, 0x73, 0xe1, 0x51, + 0x59, 0x12, 0xbc, 0x25,0xaa, 0xa2, 0xc1, 0x18 +}; + +__attribute__((unused)) +static TruthTableEntry +TRUTHTABLENAME(v1_content_nameA_keyid1_rsasha256)[] = +{ + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_OBJ_E2EFRAG, .bodyManifest = false, .extent = { 12, 20 } }, + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_OBJ_CONTENTOBJECT, .bodyManifest = true, .extent = { 36, 58 } }, + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_OBJ_NAME, .bodyManifest = true, .extent = { 40, 17 } }, + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_OBJ_PAYLOADTYPE, .bodyManifest = true, .extent = { 61, 1 } }, + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_OBJ_EXPIRY_TIME, .bodyManifest = true, .extent = { 66, 8 } }, + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_OBJ_ENDSEGMENT, .bodyManifest = true, .extent = { 78, 4 } }, + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_OBJ_PAYLOAD, .bodyManifest = true, .extent = { 86, 8 } }, + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_OBJ_ValidationAlg, .bodyManifest = true, .extent = { 94, 206 } }, + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_OBJ_KEYID, .bodyManifest = true, .extent = { 106, 32 } }, + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_OBJ_PUBKEY, .bodyManifest = true, .extent = { 142, 162 } }, + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_OBJ_SIGBITS, .bodyManifest = true, .extent = { 308, 128 } }, + { .wellKnownType = false, .indexOrKey = T_INVALID, .extent = { 0, 0 } }, +}; + +#define v1_content_nameA_keyid1_rsasha256_truthTable TABLEENTRY(v1_content_nameA_keyid1_rsasha256, TLV_ERR_NO_ERROR) + +#define v1_content_nameA_keyid1_rsasha256_URI "lci:/3=hello/0xf000=ouch" + +#endif diff --git a/libccnx-common/ccnx/common/codec/schema_v1/testdata/v1_content_nameless_nosig.h b/libccnx-common/ccnx/common/codec/schema_v1/testdata/v1_content_nameless_nosig.h new file mode 100644 index 00000000..f1fc59cb --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/testdata/v1_content_nameless_nosig.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file v1_content_nameless_nosig.h + * @brief A v1 content object without a name + * + */ + +#ifndef v1_content_nameless_nosig_h +#define v1_content_nameless_nosig_h + +#include <ccnx/common/codec/testdata/testdata_common.h> +#include <ccnx/common/codec/schema_v1/testdata/v1_ContentObjectSchema.h> + +/** + * A well formed nameless V1 Content Object + */ +__attribute__((unused)) +static uint8_t v1_content_nameless_nosig[] = { + 0x01, 0x01, 0x00, 0x31, // ver = 1, type = content object, length = 0x31 + 0x00, 0x00, 0x00, 0x08, // HopLimit = 0, reserved = 0, header length = 8 + // ------------------------ + 0x00, 0x02, 0x00, 37, // type = content object, length = 37 + // ------------------------ + 0x00, 0x05, 0x00, 1, // PayloadType + 1, // type 1 = key + 0x00, 0x06, 0x00, 0x08, // expiry time in msec + 0x00, 0x00, 0x01, 0x43, // 1,388,534,400,000 msec + 0x4B, 0x19, 0x84, 0x00, + 0x00, 0x19, 0x00, 4, // end chunk number + 0x06, 0x05, 0x04, 0x03, + // ------------------------ + 0x00, 0x01, 0x00, 8, // payload, length = 8 + 0x73, 0x75, 0x72, 0x70, + 0x72, 0x69, 0x73, 0x65, +}; + +__attribute__((unused)) +static TruthTableEntry +TRUTHTABLENAME(v1_content_nameless_nosig)[] = +{ + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_OBJ_CONTENTOBJECT, .bodyManifest = true, .extent = { 12, 37 } }, + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_OBJ_PAYLOADTYPE, .bodyManifest = true, .extent = { 17, 1 } }, + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_OBJ_EXPIRY_TIME, .bodyManifest = true, .extent = { 22, 8 } }, + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_OBJ_ENDSEGMENT, .bodyManifest = true, .extent = { 30, 4 } }, + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_OBJ_PAYLOAD, .bodyManifest = true, .extent = { 41, 8 } }, + { .wellKnownType = false, .indexOrKey = T_INVALID, .extent = { 0, 0 } }, +}; + +#define v1_content_nameless_nosig_truthTable TABLEENTRY(v1_content_nameless_nosig, TLV_ERR_NO_ERROR) + +#endif // v1_content_nameless_nosig_h diff --git a/libccnx-common/ccnx/common/codec/schema_v1/testdata/v1_content_no_payload.h b/libccnx-common/ccnx/common/codec/schema_v1/testdata/v1_content_no_payload.h new file mode 100644 index 00000000..ae439bf5 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/testdata/v1_content_no_payload.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file v1_content_no_payload.h + * @brief Content Object without a payload TLV + * + * + */ + + +#ifndef CCNx_Common_v1_content_no_payload_h +#define CCNx_Common_v1_content_no_payload_h + +#include <ccnx/common/codec/testdata/testdata_common.h> +#include <ccnx/common/codec/schema_v1/testdata/v1_ContentObjectSchema.h> + +__attribute__((unused)) +static uint8_t v1_content_no_payload[] = { + 0x01, 0x01, 0x00, 0x21, + 0x00, 0x00, 0x00, 0x08, + // -- content object + 0x00, 0x02, 0x00, 0x15, + // -- name + 0x00, 0x00, 0x00, 0x11, + 0x00, 0x03, 0x00, 0x02, + 0x6e, 0x6f, + 0x00, 0x03, 0x00, 0x07, + 0x70, 0x61, 0x79, 0x6c, + 0x6f, 0x61, 0x64 +}; + +__attribute__((unused)) +static TruthTableEntry +TRUTHTABLENAME(v1_content_no_payload)[] = +{ + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_OBJ_CONTENTOBJECT, .bodyManifest = true, .extent = { 12, 21 } }, + { .wellKnownType = false, .indexOrKey = T_INVALID, .extent = { 0, 0 } }, +}; + +#define v1_content_no_payload_truthTable TABLEENTRY(v1_content_no_payload, TLV_ERR_NO_ERROR) + +#endif diff --git a/libccnx-common/ccnx/common/codec/schema_v1/testdata/v1_content_zero_payload.h b/libccnx-common/ccnx/common/codec/schema_v1/testdata/v1_content_zero_payload.h new file mode 100644 index 00000000..01f7a8b4 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/testdata/v1_content_zero_payload.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file v1_content_zero_payload.h + * @brief Content Object with zero length payload + * + * + */ + + +#ifndef CCNx_Common_v1_content_zero_payload_h +#define CCNx_Common_v1_content_zero_payload_h + +#include <ccnx/common/codec/testdata/testdata_common.h> +#include <ccnx/common/codec/schema_v1/testdata/v1_ContentObjectSchema.h> + +__attribute__((unused)) +static uint8_t v1_content_zero_payload[] = { + 0x01, 0x01, 0x00, 0x25, + 0x00, 0x00, 0x00, 0x08, + // -- content object + 0x00, 0x02, 0x00, 0x19, + // -- name + 0x00, 0x00, 0x00, 0x11, + 0x00, 0x03, 0x00, 0x02, + 0x6e, 0x6f, + 0x00, 0x03, 0x00, 0x07, + 0x70, 0x61, 0x79, 0x6c, + 0x6f, 0x61, 0x64, + // -- payload + 0x00, 0x01, 0x00, 0x00, +}; + +__attribute__((unused)) +static TruthTableEntry +TRUTHTABLENAME(v1_content_zero_payload)[] = +{ + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_OBJ_CONTENTOBJECT, .bodyManifest = true, .extent = { 12, 25 } }, + { .wellKnownType = false, .indexOrKey = T_INVALID, .extent = { 0, 0 } }, +}; + +#define v1_content_zero_payload_truthTable TABLEENTRY(v1_content_zero_payload, TLV_ERR_NO_ERROR) + +#endif diff --git a/libccnx-common/ccnx/common/codec/schema_v1/testdata/v1_cpi_add_route.h b/libccnx-common/ccnx/common/codec/schema_v1/testdata/v1_cpi_add_route.h new file mode 100644 index 00000000..9bcf7dfc --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/testdata/v1_cpi_add_route.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file v1_cpi_add_route.h + * @brief A hand-encoded CPI packet to add a route + * + * The v1 old-style control packet is a fixed header plus a tlv container 0xBEEF with a "value" of the CPI JSON string. + * The packet type is 0xA4. + * + */ + +#ifndef TransportRTA_v1_cpi_AddRoute_h +#define TransportRTA_v1_cpi_AddRoute_h + +#include <ccnx/common/codec/testdata/testdata_common.h> +#include <ccnx/common/codec/schema_v1/testdata/v1_CPISchema.h> + +__attribute__((unused)) +static uint8_t v1_cpi_add_route[] = "\x01\xA4\x00\xA7" + "\x00\x00\x00\x08" + "\xBE\xEF\x00\x9A" + "{\"CPI_REQUEST\":{\"SEQUENCE\":22,\"REGISTER\":{\"PREFIX\":\"lci:/howdie/stranger\",\"INTERFACE\":55,\"FLAGS\":0,\"PROTOCOL\":\"STATIC\",\"ROUTETYPE\":\"LONGEST\",\"COST\":200}}}"; + +__attribute__((unused)) +static TruthTableEntry +TRUTHTABLENAME(v1_cpi_add_route)[] = +{ + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_CPI_PAYLOAD, .bodyManifest = true, .extent = { 12, 155 } }, + { .wellKnownType = false, .indexOrKey = T_INVALID, .extent = { 0, 0 } }, +}; + +#define v1_cpi_add_route_truthTable TABLEENTRY(v1_cpi_add_route, TLV_ERR_NO_ERROR) + +#define v1_cpi_add_route_PrefixUri "lci:/howdie/stranger" +#define v1_cpi_add_route_Sequence 22 +#define v1_cpi_add_route_Interface 55 +#endif // TransportRTA_cpi_AddRoute_h diff --git a/libccnx-common/ccnx/common/codec/schema_v1/testdata/v1_cpi_add_route_crc32c.h b/libccnx-common/ccnx/common/codec/schema_v1/testdata/v1_cpi_add_route_crc32c.h new file mode 100644 index 00000000..e31cfc85 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/testdata/v1_cpi_add_route_crc32c.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file v1_cpi_add_route.h + * @brief A hand-encoded CPI packet to add a route + * + * The v1 old-style control packet is a fixed header plus a tlv container 0xBEEF with a "value" of the CPI JSON string. + * The packet type is 0xA4. + * + * This control packet has a CRC32C MIC on it. Otherwise, same as v1_cpi_add_route.h + * + * Ground truth set derived from CRC RevEng http://reveng.sourceforge.net + * e.g. reveng -c -m CRC-32C 313233343536373839 gives the canonical check value 0xe306928e + * + * You can also calcaulate CRC32C online at http://www.zorc.breitbandkatze.de/crc.html using + * CRC polynomial 0x1EDC6F41, init 0xFFFFFFFF, final 0xFFFFFFFF, reverse data bytes (check), + * and reverse CRC result before final XOR (check). + * + * you can get the packet dump from the "write_packets" command. here's the detailed steps. + * The -c size of 4 in steps 4 and 7 are chosen to make it easy to delete the right number of lines. + * there's nothing magic about the "4". + * + * 1) execute ./write_packets + * 2) xxd -r -c 8 v1_cpi_add_route_crc32c.txt > y + * 3) Delete the first 8 bytes and last 16 bytes and display has a hex string + * tail -c +9 y | head -c 158 | xxd -p -c 256 + * The string should be "beef...077d7d7d" + * 4) The string for this packet is too long for the website. Use another tool such as reveng. + * 5) The answer should be 78fd926a (the reveng answer will be byte reversed) + * 6) Put the byte array from (5) in the Validation Payload. + * + */ + +#ifndef TransportRTA_v1_v1_cpi_AddRoute_crc32c_h +#define TransportRTA_v1_v1_cpi_AddRoute_crc32c_h + +#include <ccnx/common/codec/testdata/testdata_common.h> +#include <ccnx/common/codec/schema_v1/testdata/v1_CPISchema.h> + +__attribute__((unused)) +static uint8_t v1_cpi_add_route_crc32c[] = "\x01\xA4\x00\xB7" + "\x00\x00\x00\x08" + "\xBE\xEF\x00\x9A" + "{\"CPI_REQUEST\":{\"SEQUENCE\":22,\"REGISTER\":{\"PREFIX\":\"lci:/howdie/stranger\",\"INTERFACE\":55,\"FLAGS\":0,\"PROTOCOL\":\"STATIC\",\"ROUTETYPE\":\"LONGEST\",\"COST\":200}}}" + "\x00\x03\x00\x04" + "\x00\x02\x00\x00" + "\x00\x04\x00\x04" + "\x78\xfd\x92\x6a"; + +__attribute__((unused)) +static TruthTableEntry +TRUTHTABLENAME(v1_cpi_add_route_crc32c)[] = +{ + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_CPI_PAYLOAD, .bodyManifest = true, .extent = { 12, 155 } }, + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_CPI_ValidationAlg, .bodyManifest = true, .extent = { 171, 4 } }, + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_CPI_SIGBITS, .bodyManifest = true, .extent = { 178, 4 } }, + { .wellKnownType = false, .indexOrKey = T_INVALID, .extent = { 0, 0 } }, +}; + +#define v1_cpi_add_route_crc32c_truthTable TABLEENTRY(v1_cpi_add_route_crc32c, TLV_ERR_NO_ERROR) + +#define v1_cpi_add_route_crc32c_PrefixUri "lci:/howdie/stranger" +#define v1_cpi_add_route_crc32c_Sequence 22 +#define v1_cpi_add_route_crc32c_Interface 55 +#endif // TransportRTA_cpi_AddRoute_h diff --git a/libccnx-common/ccnx/common/codec/schema_v1/testdata/v1_interest_all_fields.h b/libccnx-common/ccnx/common/codec/schema_v1/testdata/v1_interest_all_fields.h new file mode 100755 index 00000000..f6524ad1 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/testdata/v1_interest_all_fields.h @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file v1_interest_all_fields.h + * @brief A hand-encoded v1 interest in wireformat with all Interest fields. + * + * <#Detailed Description#> + * + */ + +#ifndef CCNx_Common_v1_interest_all_fields_h +#define CCNx_Common_v1_interest_all_fields_h + +#include <ccnx/common/codec/testdata/testdata_common.h> +#include <ccnx/common/codec/schema_v1/testdata/v1_InterestSchema.h> + +/** + * A well formed interest with all allowed Interest fields + */ +__attribute__((unused)) +static uint8_t v1_interest_all_fields[] = { + 0x01, 0x00, 0x00, 156, // ver = 1, type = interest, length = 156 + 0x20, 0x00, 0x11, 14, // HopLimit = 32, reserved = 0, flags = 0x11, header length = 14 + // ------------------------ + 0x00, 0x01, 0x00, 2, // Interest Lifetime (2 bytes) + 0xEA, 0xEB, + // ------------------------ + 0x00, 0x01, 0x00, 138, // type = interest, length = 138 + // ------------------------ + 0x00, 0x00, 0x00, 45, // type = name, length = 45 + 0x00, 0x03, 0x00, 4, // type = binary, length = 4 + 'c', 'o', 'o', 'l', // "cool" + // ----- segment 2 -------- + 0x00, 0x02, 0x00, 33, // type = payload id, length = 33 + 0x01, // payloadID type = sha256 + 0x89, 0x87, 0x69, 0xfc, // hash bytes based on payload + 0x8c, 0xff, 0x16, 0xff, + 0x3d, 0xfc, 0xe7, 0xfa, + 0x02, 0xd2, 0x6d, 0x26, + 0xf0, 0x91, 0x86, 0x27, + 0xcf, 0x18, 0xc1, 0x9b, + 0x0b, 0x5f, 0xe3, 0x93, + 0xce, 0x1a, 0xa3, 0x56, + // ------------------------ + 0x00, 0x02, 0x00, 36, // type = keyid restriction, length = 36 + 0x00, 0x01, 0x00, 0x20, // SHA256 hash, length 32 + 0xa0, 0xa1, 0xa2, 0xa3, + 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, + 0xac, 0xad, 0xae, 0xaf, + 0xa0, 0xa1, 0xa2, 0xa3, + 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, + 0xac, 0xad, 0xae, 0xaf, + // ------------------------ + 0x00, 0x03, 0x00, 36, // type = hash restriction, length = 36 + 0x00, 0x01, 0x00, 0x20, // SHA256 hash, length 32 + 0xb0, 0xb1, 0xb2, 0xb3, + 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, + 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, + 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, + 0xcc, 0xcd, 0xce, 0xcf, + // ------------------------ + 0x00, 0x01, 0x00, 5, // type = payload, length = 5 + 0xD0, 0xD1, 0xD2, 0xD3, + 0xD4, +}; + +__attribute__((unused)) +static TruthTableEntry +TRUTHTABLENAME(v1_interest_all_fields)[] = +{ + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_INT_LIFETIME, .bodyManifest = false, .extent = { 12, 2 } }, + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_INT_INTEREST, .bodyManifest = true, .extent = { 18, 138 } }, + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_INT_NAME, .bodyManifest = true, .extent = { 22, 45 } }, + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_INT_KEYID, .bodyManifest = true, .extent = { 71, 36 } }, + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_INT_OBJHASH, .bodyManifest = true, .extent = { 111, 36 } }, + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_INT_PAYLOAD, .bodyManifest = true, .extent = { 151, 5 } }, + { .wellKnownType = false, .indexOrKey = T_INVALID, .extent = { 0, 0 } }, +}; + +#define v1_interest_all_fields_truthTable TABLEENTRY(v1_interest_all_fields, TLV_ERR_NO_ERROR) + +#define v1_interest_all_fields_URI "lci:/3=cool/2=\x01\x89\x87\x69\xfc\x8c\xff\x16\xff\x3d\xfc\xe7\xfa\x02\xd2\x6d\x26\xf0\x91\x86\x27\xcf\x18\xc1\x9b\x0b\x5f\xe3\x93\xce\x1a\xa3\x56" +#define v1_interest_all_fields_Lifetime 0xEAEB +#endif diff --git a/libccnx-common/ccnx/common/codec/schema_v1/testdata/v1_interest_bad_message_length.h b/libccnx-common/ccnx/common/codec/schema_v1/testdata/v1_interest_bad_message_length.h new file mode 100644 index 00000000..650c770e --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/testdata/v1_interest_bad_message_length.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file v1_interest_all_fields.h + * @brief A hand-encoded v1 interest in wireformat with all Interest fields. + * + * The Interest TLV length goes beyond the end of the packet + * + */ + +#ifndef CCNx_Common_v1_interest_bad_message_length_h +#define CCNx_Common_v1_interest_bad_message_length_h + +#include <ccnx/common/codec/testdata/testdata_common.h> +#include <ccnx/common/codec/schema_v1/testdata/v1_InterestSchema.h> + +/** + * A well formed interest with all allowed Interest fields + */ +__attribute__((unused)) +static uint8_t v1_interest_bad_message_length[] = { + 0x01, 0x00, 0x00, 30, // ver = 1, type = interest, length = 30 + 0x20, 0x00, 0x11, 14, // HopLimit = 31, reserved = 0, flags = 0x11, header length = 14 + // ------------------------ + 0x00, 0x01, 0x00, 2, // Interest Lifetime (2 bytes) + 0xEA, 0xEB, + // ------------------------ + 0x00, 0x01, 0x00, 13, // type = interest, length = 13 (1 byte too far) + // ------------------------ + 0x00, 0x00, 0x00, 8, // type = name, length = 8 + 0x00, 0x03, 0x00, 4, // type = binary, length = 4 + 'c', 'o', 'o', 'l', // "cool" +}; + +__attribute__((unused)) +static TruthTableEntry +TRUTHTABLENAME(v1_interest_bad_message_length)[] = +{ + { .wellKnownType = false, .indexOrKey = T_INVALID, .extent = { 0, 0 } }, +}; + +#define v1_interest_bad_message_length_truthTable TABLEENTRY(v1_interest_bad_message_length, TLV_ERR_TOO_LONG) + +#endif diff --git a/libccnx-common/ccnx/common/codec/schema_v1/testdata/v1_interest_bad_validation_alg.h b/libccnx-common/ccnx/common/codec/schema_v1/testdata/v1_interest_bad_validation_alg.h new file mode 100644 index 00000000..9c4c3117 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/testdata/v1_interest_bad_validation_alg.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file v1_interest_bad_validation_alg.h + * @brief Interest with CRC validation + * + * This is an error packet, the ValidationAlg TLV does not have the correct type + * + */ + +#ifndef testdata_v1_interest_bad_validation_alg +#define testdata_v1_interest_bad_validation_alg + +/** + * A well formed interest with only a name + */ + +#include <ccnx/common/codec/testdata/testdata_common.h> +#include <ccnx/common/codec/schema_v1/testdata/v1_InterestSchema.h> + +__attribute__((unused)) +static uint8_t v1_interest_bad_validation_alg[] = { + 0x01, 0x00, 0x00, 65, // ver = 1, type = interest, length = 65 + 0x20, 0x00, 0x00, 24, // HopLimit = 32, reserved = 0, header length = 24 + // ------------------------ + 0x00, 0x03, 0x00, 12, // Interest Fragment + 0x01, 0x02, 0x03, 0x04, + 0x05, 0x06, 0x07, 0x08, // fragment 0x0102030405060708 + 0x05, 0xDC, 0x00, 0x00, // MTU 1500, fragcnt 0, fragnum 0 + // ------------------------ + 0x00, 0x01, 0x00, 0x15, // type = interest, length = 21 + // ------------------------ + 0x00, 0x00, 0x00, 0x11, // type = name, length = 17 + 0x00, 0x03, 0x00, 0x05, // type = binary, length = 5 + 'h', 'e', 'l', 'l', // "hello" + 'o', + 0xF0, 0x00, 0x00, 0x04, // type = app, length = 4 + 'o', 'u', 'c', 'h', // "ouch" + // ------------------------ + 0x00, 0xFF, 0x00, 4, // Not "0x03" validation alg length = 4 + 0x00, 0xFF, 0x00, 0x00, // unknown validation alg + // ------------------------ + 0x00, 0x04, 0x00, 4, // validation payload + 0x6A, 0xD7, 0xB1, 0xF2 // 6AD7B1F2 +}; + +__attribute__((unused)) +static TruthTableEntry +TRUTHTABLENAME(v1_interest_bad_validation_alg)[] = +{ + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_INT_E2EFRAG, .bodyManifest = false, .extent = { 12, 12 } }, + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_INT_INTEREST, .bodyManifest = true, .extent = { 24, 25 } }, + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_INT_NAME, .bodyManifest = true, .extent = { 32, 17 } }, + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_INT_ValidationAlg, .bodyManifest = true, .extent = { 53, 4 } }, + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_INT_ValidationPayload, .bodyManifest = true, .extent = { 61, 4 } }, + { .wellKnownType = false, .indexOrKey = T_INVALID, .extent = { 0, 0 } }, +}; + +#define v1_interest_bad_validation_alg_truthTable TABLEENTRY(v1_interest_bad_validation_alg, TLV_ERR_NO_ERROR) + +#endif diff --git a/libccnx-common/ccnx/common/codec/schema_v1/testdata/v1_interest_nameA.h b/libccnx-common/ccnx/common/codec/schema_v1/testdata/v1_interest_nameA.h new file mode 100644 index 00000000..4bee1e40 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/testdata/v1_interest_nameA.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file v1_interest_nameA.h + * @brief A basic interest + * + * A basic Interest with a fragmentation header and Name. + * + */ + +#ifndef testdata_interest_nameA_h +#define testdata_interest_nameA_h + +#include <ccnx/common/codec/testdata/testdata_common.h> +#include <ccnx/common/codec/schema_v1/testdata/v1_InterestSchema.h> + +/** + * A well formed interest with only a name + */ + + +__attribute__((unused)) +static uint8_t v1_interest_nameA[] = { + 0x01, 0x00, 0x00, 61, // ver = 1, type = interest, length = 61 + 0x20, 0x00, 0x00, 36, // HopLimit = 31, reserved = 0, header length = 36 + // ------------------------ + // ------------------------ + 0x00, 0x03, 0x00, 12, // Interest Fragment + 0x01, 0x02, 0x03, 0x04, + 0x05, 0x06, 0x07, 0x08, // fragment 0x0102030405060708 + 0x05, 0xDC, 0x00, 0x00, // MTU 1500, fragcnt 0, fragnum 0 + // ------------------------ + 0x00, 0x01, 0x00, 8, // Interest Lifetime + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0F, 0xA0, // 4000 milli-seconds + // ------------------------ + 0x00, 0x01, 0x00, 0x15, // type = interest, length = 21 + // ------------------------ + 0x00, 0x00, 0x00, 0x11, // type = name, length = 17 + 0x00, 0x03, 0x00, 0x05, // type = binary, length = 5 + 'h', 'e', 'l', 'l', // "hello" + 'o', + 0xF0, 0x00, 0x00, 0x04, // type = app, length = 4 + 'o', 'u', 'c', 'h', // "ouch" +}; + +__attribute__((unused)) +static TruthTableEntry +TRUTHTABLENAME(v1_interest_nameA)[] = +{ + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_INT_E2EFRAG, .bodyManifest = false, .extent = { 12, 12 } }, + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_INT_LIFETIME, .bodyManifest = false, .extent = { 28, 8 } }, + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_INT_INTEREST, .bodyManifest = false, .extent = { 40, 21 } }, + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_INT_NAME, .bodyManifest = true, .extent = { 44, 17 } }, + { .wellKnownType = false, .indexOrKey = T_INVALID, .extent = { 0, 0 } }, +}; + +#define v1_interest_nameA_truthTable TABLEENTRY(v1_interest_nameA, TLV_ERR_NO_ERROR) + +#define v1_interest_nameA_URI "lci:/3=hello/0xf000=ouch" + +#endif diff --git a/libccnx-common/ccnx/common/codec/schema_v1/testdata/v1_interest_nameA_badcrc32c.h b/libccnx-common/ccnx/common/codec/schema_v1/testdata/v1_interest_nameA_badcrc32c.h new file mode 100644 index 00000000..6af04f42 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/testdata/v1_interest_nameA_badcrc32c.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file interest_nameA_badcrc32c.h + * @brief Interest with CRC validation + * + * Has incorrect CRC32C payload + * + */ + +#ifndef testdata_interest_nameA_badcrc32c_h +#define testdata_interest_nameA_badcrc32c_h + +#include <ccnx/common/codec/testdata/testdata_common.h> +#include <ccnx/common/codec/schema_v1/testdata/v1_InterestSchema.h> + +/** + * A well formed interest with only a name + */ + +__attribute__((unused)) +static uint8_t v1_interest_nameA_badcrc32c[] = { + 0x01, 0x00, 0x00, 41, // ver = 1, type = interest, length = 65 + 0x20, 0x00, 0x00, 24, // HopLimit = 31, reserved = 0, header length = 24 + // ------------------------ + 0x00, 0x03, 0x00, 0x0C, // Interest Fragment + 0x01, 0x02, 0x03, 0x04, + 0x05, 0x06, 0x07, 0x08, // fragment 0x0102030405060708 + 0x05, 0xDC, 0x00, 0x00, // MTU 1500, fragcnt 0, fragnum 0 + // ------------------------ + 0x00, 0x01, 0x00, 0x15, // type = interest, length = 21 + // ------------------------ + 0x00, 0x00, 0x00, 0x11, // type = name, length = 17 + 0x00, 0x03, 0x00, 0x05, // type = binary, length = 5 + 'h', 'e', 'l', 'l', // "hello" + 'o', + 0xF0, 0x00, 0x00, 0x04, // type = app, length = 4 + 'o', 'u', 'c', 'h', // "ouch" + // ------------------------ + 0x00, 0x03, 0x00, 4, // validation alg, length = 4 + 0x00, 0x02, 0x00, 0x00, // CRC32C + // ------------------------ + 0x00, 0x04, 0x00, 4, // validation payload + 0x00, 0x00, 0x00, 0x00 // invalid CRC32C +}; + +__attribute__((unused)) +static TruthTableEntry +TRUTHTABLENAME(v1_interest_nameA_badcrc32c)[] = +{ + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_INT_E2EFRAG, .bodyManifest = false, .extent = { 12, 12 } }, + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_INT_INTEREST, .bodyManifest = true, .extent = { 20, 21 } }, + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_INT_NAME, .bodyManifest = true, .extent = { 24, 17 } }, + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_INT_ValidationAlg, .bodyManifest = true, .extent = { 45, 4 } }, + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_INT_ValidationPayload, .bodyManifest = true, .extent = { 53, 4 } }, + { .wellKnownType = false, .indexOrKey = T_INVALID, .extent = { 0, 0 } }, +}; + +#define v1_interest_nameA_badcrc32c_truthTable TABLEENTRY(v1_interest_nameA_badcrc32c, TLV_ERR_NO_ERROR) + +#define v1_interest_nameA_badcrc32c_URI "lci:/3=hello/0xf000=ouch" + +#endif diff --git a/libccnx-common/ccnx/common/codec/schema_v1/testdata/v1_interest_nameA_crc32c.h b/libccnx-common/ccnx/common/codec/schema_v1/testdata/v1_interest_nameA_crc32c.h new file mode 100644 index 00000000..64edf560 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/testdata/v1_interest_nameA_crc32c.h @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file interest_nameA_crc32c.h + * @brief Interest with CRC validation + * + * + * Ground truth set derived from CRC RevEng http://reveng.sourceforge.net + * e.g. reveng -c -m CRC-32C 313233343536373839 gives the canonical check value 0xe306928e (the output will be backwards) + * + * You can also calcaulate CRC32C online at http://www.zorc.breitbandkatze.de/crc.html using + * CRC polynomial 0x1EDC6F41, init 0xFFFFFFFF, final 0xFFFFFFFF, reverse data bytes (check), + * and reverse CRC result before final XOR (check). + * + * you can get the packet dump from the "write_packets" command. here's the detailed steps. + * The -c size of 8 in steps 4 and 7 are chosen to make it easy to delete the right number of lines. + * there's nothing magic about the "8". + * + * 1) execute ./write_packets + * 2) xxd -r -c 8 v1_interest_nameA_crc32c.txt > y + * 3) Delete the first 24 bytes and last 8 bytes and display as a URI-escaped hex string + * head -c 57 y | tail -c +25 | xxd -p -c 256 | sed 's/[0-9a-f]\{2\}/%&/g' + * The string should be "00010015...00020000" + * 4) Copy the hex string to the website and use the settings specified above (don't use 0x in front + * of any hex strings). IMPORTANT: you need to %-escape each hex byte!! Click "compute!" + * 5) The answer should be 6AD7B1F2 + * 6) Put the byte array from (5) in the Validation Payload. + * + */ + +#ifndef testdata_interest_nameA_crc32c_h +#define testdata_interest_nameA_crc32c_h + +/** + * A well formed interest with only a name + */ + +#include <ccnx/common/codec/testdata/testdata_common.h> +#include <ccnx/common/codec/schema_v1/testdata/v1_InterestSchema.h> + +#define NAME_A_CRC32_PACKET(_type, _code) \ + { \ + 0x01, _type, 0x00, 65, /* ver = 1, type = interest, length = 65 */ \ + 0x20, _code, 0x00, 24, /* HopLimit = 32, reserved = 0, header length = 24*/ \ + /* ------------------------ */ \ + 0x00, 0x03, 0x00, 12, /* Interest Fragment */ \ + 0x01, 0x02, 0x03, 0x04, \ + 0x05, 0x06, 0x07, 0x08, /* fragment 0x0102030405060708 */ \ + 0x05, 0xDC, 0x00, 0x00, /* MTU 1500, fragcnt 0, fragnum 0 */ \ + /* ------------------------ */ \ + 0x00, 0x01, 0x00, 0x15, /* type = interest, length = 21 */ \ + /* ------------------------ */ \ + 0x00, 0x00, 0x00, 0x11, /* type = name, length = 17 */ \ + 0x00, 0x03, 0x00, 0x05, /* type = binary, length = 5 */ \ + 'h', 'e', 'l', 'l', /* "hello" */ \ + 'o', \ + 0xF0, 0x00, 0x00, 0x04, /* type = app, length = 4 */ \ + 'o', 'u', 'c', 'h', /* "ouch" */ \ + /* ------------------------ */ \ + 0x00, 0x03, 0x00, 4, /* validation alg, length = 4 */ \ + 0x00, 0x02, 0x00, 0x00, /* CRC32C */ \ + /* ------------------------ */ \ + 0x00, 0x04, 0x00, 4, /* validation payload */ \ + 0xD0, 0x98, 0x73, 0x7C, /* D098737C */ \ + } + +__attribute__((unused)) +static uint8_t v1_interest_nameA_crc32c[] = NAME_A_CRC32_PACKET(0x00, 0x00); + +__attribute__((unused)) +static TruthTableEntry +TRUTHTABLENAME(v1_interest_nameA_crc32c)[] = +{ + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_INT_E2EFRAG, .bodyManifest = false, .extent = { 12, 12 } }, + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_INT_INTEREST, .bodyManifest = true, .extent = { 24, 25 } }, + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_INT_NAME, .bodyManifest = true, .extent = { 32, 17 } }, + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_INT_ValidationAlg, .bodyManifest = true, .extent = { 53, 4 } }, + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_INT_ValidationPayload, .bodyManifest = true, .extent = { 61, 4 } }, + { .wellKnownType = false, .indexOrKey = T_INVALID, .extent = { 0, 0 } }, +}; + +__attribute__((unused)) +static uint8_t v1_interest_nameA_crc32c_returned[] = NAME_A_CRC32_PACKET(0x02, 0x03); //InterestReturn & NoResource + +#define v1_interest_nameA_crc32c_truthTable TABLEENTRY(v1_interest_nameA_crc32c, TLV_ERR_NO_ERROR) + +#define v1_interest_nameA_crc32c_URI "lci:/3=hello/0xf000=ouch" + +#endif diff --git a/libccnx-common/ccnx/common/codec/schema_v1/testdata/v1_interest_validation_alg_overrun.h b/libccnx-common/ccnx/common/codec/schema_v1/testdata/v1_interest_validation_alg_overrun.h new file mode 100644 index 00000000..8f3b55a5 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/testdata/v1_interest_validation_alg_overrun.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file v1_interest_validation_alg_overrun.h + * @brief Interest with CRC validation + * + * This is an error packet. The length of the Validation TLV runs past the end of the packet. + * + */ + +#ifndef testdata_v1_interest_validation_alg_overrun +#define testdata_v1_interest_validation_alg_overrun + +/** + * A well formed interest with only a name + */ + +#include <ccnx/common/codec/testdata/testdata_common.h> +#include <ccnx/common/codec/schema_v1/testdata/v1_InterestSchema.h> + +__attribute__((unused)) +static uint8_t v1_interest_validation_alg_overrun[] = { + 0x01, 0x00, 0x00, 65, // ver = 1, type = interest, length = 65 + 0x20, 0x00, 0x00, 24, // HopLimit = 32, reserved = 0, header length = 24 + // ------------------------ + 0x00, 0x03, 0x00, 12, // Interest Fragment + 0x01, 0x02, 0x03, 0x04, + 0x05, 0x06, 0x07, 0x08, // fragment 0x0102030405060708 + 0x05, 0xDC, 0x00, 0x00, // MTU 1500, fragcnt 0, fragnum 0 + // ------------------------ + 0x00, 0x01, 0x00, 0x15, // type = interest, length = 21 + // ------------------------ + 0x00, 0x00, 0x00, 0x11, // type = name, length = 17 + 0x00, 0x03, 0x00, 0x05, // type = binary, length = 5 + 'h', 'e', 'l', 'l', // "hello" + 'o', + 0xF0, 0x00, 0x00, 0x04, // type = app, length = 4 + 'o', 'u', 'c', 'h', // "ouch" + // ------------------------ + 0x00, 0x03, 0x00, 255, // Validation Alg, length = 255 + 0x00, 0xFF, 0x00, 0x00, // unknown validation alg + // ------------------------ + 0x00, 0x04, 0x00, 4, // validation payload + 0x6A, 0xD7, 0xB1, 0xF2 // 6AD7B1F2 +}; + +__attribute__((unused)) +static TruthTableEntry +TRUTHTABLENAME(v1_interest_validation_alg_overrun)[] = +{ + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_INT_E2EFRAG, .bodyManifest = false, .extent = { 12, 12 } }, + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_INT_INTEREST, .bodyManifest = true, .extent = { 24, 25 } }, + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_INT_NAME, .bodyManifest = true, .extent = { 32, 17 } }, + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_INT_ValidationAlg, .bodyManifest = true, .extent = { 53, 4 } }, + { .wellKnownType = true, .indexOrKey = V1_MANIFEST_INT_ValidationPayload, .bodyManifest = true, .extent = { 61, 4 } }, + { .wellKnownType = false, .indexOrKey = T_INVALID, .extent = { 0, 0 } }, +}; + +#define v1_interest_validation_alg_overrun_truthTable TABLEENTRY(v1_interest_validation_alg_overrun, TLV_ERR_TOO_LONG) + +#endif diff --git a/libccnx-common/ccnx/common/codec/schema_v1/testdata/v1_testrig_truthSet.h b/libccnx-common/ccnx/common/codec/schema_v1/testdata/v1_testrig_truthSet.h new file mode 100644 index 00000000..f98de878 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/testdata/v1_testrig_truthSet.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Contains tables of all the packets. May be used for automated testing. Also used by write_packets utility. + * + */ + +#include <ccnx/common/codec/testdata/testdata_common.h> + +#include "v1_interest_nameA.h" +#include "v1_interest_nameA_badcrc32c.h" +#include "v1_interest_nameA_crc32c.h" +#include "v1_interest_bad_validation_alg.h" +#include "v1_interest_validation_alg_overrun.h" + +#include "v1_content_nameA_crc32c.h" +#include "v1_content_nameA_keyid1_rsasha256.h" +#include "v1_content_zero_payload.h" +#include "v1_content_no_payload.h" + +#include "v1_cpi_add_route.h" +#include "v1_cpi_add_route_crc32c.h" + +// terminated with NULL packet entry +__attribute__((unused)) +static TruthTable v1_interests_truthSet [] = { + // tests in alphabetical order + v1_interest_nameA_truthTable, + v1_interest_nameA_badcrc32c_truthTable, + v1_interest_nameA_crc32c_truthTable, + v1_interest_bad_validation_alg_truthTable, + v1_interest_validation_alg_overrun_truthTable, + // the end of table marker + { .packet = NULL, .expectedError= 0, .entry = NULL } +}; + +// terminated with NULL packet entry +__attribute__((unused)) +static TruthTable v1_contentObject_truthSet [] = { + v1_content_nameA_crc32c_truthTable, + v1_content_nameA_keyid1_rsasha256_truthTable, + v1_content_zero_payload_truthTable, + v1_content_no_payload_truthTable, + + // the end of table marker + { .packet = NULL, .expectedError= 0, .entry = NULL } +}; + +__attribute__((unused)) +static TruthTable v1_cpi_truthSet [] = { + v1_cpi_add_route_truthTable, + v1_cpi_add_route_crc32c_truthTable, + // the end of table marker + { .packet = NULL, .expectedError= 0, .entry = NULL } +}; diff --git a/libccnx-common/ccnx/common/codec/schema_v1/testdata/v1_testrig_truthTable.h b/libccnx-common/ccnx/common/codec/schema_v1/testdata/v1_testrig_truthTable.h new file mode 100755 index 00000000..32c954b7 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/schema_v1/testdata/v1_testrig_truthTable.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +/* + * testrig_truthTable.h + * TransportRTA + */ + +#ifndef TransportRTA_testrig_truthTable_h +#define TransportRTA_testrig_truthTable_h + +#include <ccnx/common/codec/schema_v1/testdata/v1_InterestSchema.h> +#include <ccnx/common/codec/schema_v1/testdata/v1_ContentObjectSchema.h> +//#include <ccnx/common/codec/schema_v1/testdata/v1_CPISchema.h> + +#include <inttypes.h> +#include <stdbool.h> + +#include <ccnx/common/codec/testdata/testdata_common.h> + +#endif diff --git a/libccnx-common/ccnx/common/codec/test/.gitignore b/libccnx-common/ccnx/common/codec/test/.gitignore new file mode 100644 index 00000000..f0d97bc9 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/test/.gitignore @@ -0,0 +1,7 @@ +test_ccnxCodec_EncodingBuffer +test_ccnxCodec_Error +test_ccnxCodec_NetworkBuffer +test_ccnxCodec_TlvDecoder +test_ccnxCodec_TlvEncoder +test_ccnxCodec_TlvPacket +test_ccnxCodec_TlvUtilities diff --git a/libccnx-common/ccnx/common/codec/test/CMakeLists.txt b/libccnx-common/ccnx/common/codec/test/CMakeLists.txt new file mode 100644 index 00000000..73dd544e --- /dev/null +++ b/libccnx-common/ccnx/common/codec/test/CMakeLists.txt @@ -0,0 +1,24 @@ +# Enable gcov output for the tests +add_definitions(--coverage) +set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} " --coverage") + +configure_file(test_rsa.p12 test_rsa.p12 COPYONLY) +configure_file(test_random_bytes test_random_bytes COPYONLY) +configure_file(test_random_bytes.sig test_random_bytes.sig COPYONLY) +configure_file(test_rsa_key.pem test_rsa_key.pem COPYONLY) + +set(TestsExpectedToPass + test_ccnxCodec_EncodingBuffer + test_ccnxCodec_Error + test_ccnxCodec_NetworkBuffer + test_ccnxCodec_TlvDecoder + test_ccnxCodec_TlvEncoder + test_ccnxCodec_TlvPacket + test_ccnxCodec_TlvUtilities +) + + +foreach(test ${TestsExpectedToPass}) + AddTest(${test}) +endforeach() + diff --git a/libccnx-common/ccnx/common/codec/test/test_ccnxCodec_EncodingBuffer.c b/libccnx-common/ccnx/common/codec/test/test_ccnxCodec_EncodingBuffer.c new file mode 100755 index 00000000..33bdbbc7 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/test/test_ccnxCodec_EncodingBuffer.c @@ -0,0 +1,343 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../ccnxCodec_EncodingBuffer.c" +#include <parc/algol/parc_SafeMemory.h> +#include <LongBow/unit-test.h> + +typedef struct test_data { + CCNxCodecEncodingBuffer *encodingBuffer; +} TestData; + +static TestData * +_commonSetup(void) +{ + TestData *data = parcMemory_AllocateAndClear(sizeof(TestData)); + assertNotNull(data, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(TestData)); + data->encodingBuffer = ccnxCodecEncodingBuffer_Create(); + return data; +} + +static void +_commonTeardown(TestData *data) +{ + ccnxCodecEncodingBuffer_Release(&data->encodingBuffer); + parcMemory_Deallocate((void **) &data); +} + +LONGBOW_TEST_RUNNER(ccnxCodec_EncodingBuffer) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(ccnxCodec_EncodingBuffer) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(ccnxCodec_EncodingBuffer) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecEncodingBuffer_AppendBuffer_FirstAppend); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecEncodingBuffer_AppendBuffer_SameArray); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecEncodingBuffer_AppendBuffer_SecondArray); + + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecEncodingBuffer_Create); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecEncodingBuffer_CreateIOVec); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecEncodingBuffer_CreateIOVec_Empty); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecEncodingBuffer_Display); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecEncodingBuffer_Length); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + longBowTestCase_SetClipBoardData(testCase, _commonSetup()); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + _commonTeardown(longBowTestCase_GetClipBoardData(testCase)); + + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, ccnxCodecEncodingBuffer_AppendBuffer_FirstAppend) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCBuffer *buffer = parcBuffer_Wrap("hello", 5, 0, 5); + size_t position = ccnxCodecEncodingBuffer_AppendBuffer(data->encodingBuffer, buffer); + assertTrue(position == 0, "Wrong position, got %zu expected %d", position, 0); + + _ccnxCodecEncodingBuffer_Validate(data->encodingBuffer); + assertTrue(data->encodingBuffer->totalCount == 1, "Wrong count, got %u expected %u", data->encodingBuffer->totalCount, 1); + assertTrue(data->encodingBuffer->totalBytes == 5, "Wrong bytes, got %zu expected %u", data->encodingBuffer->totalBytes, 5); + + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecEncodingBuffer_AppendBuffer_SameArray) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCBuffer *buffer = parcBuffer_Wrap("hello", 5, 0, 5); + + // do two appends + ccnxCodecEncodingBuffer_AppendBuffer(data->encodingBuffer, buffer); + size_t position = ccnxCodecEncodingBuffer_AppendBuffer(data->encodingBuffer, buffer); + assertTrue(position == 1, "Wrong position, got %zu expected %d", position, 1); + + _ccnxCodecEncodingBuffer_Validate(data->encodingBuffer); + assertTrue(data->encodingBuffer->totalCount == 2, "Wrong count, got %u expected %u", data->encodingBuffer->totalCount, 2); + assertTrue(data->encodingBuffer->totalBytes == 10, "Wrong bytes, got %zu expected %u", data->encodingBuffer->totalBytes, 10); + + // should still be in the first listarray + assertTrue(data->encodingBuffer->head == data->encodingBuffer->tail, "Head != tail") + { + ccnxCodecEncodingBuffer_Display(data->encodingBuffer, 0); + } + + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecEncodingBuffer_AppendBuffer_SecondArray) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCBuffer *buffer = parcBuffer_Wrap("hello", 5, 0, 5); + + ccnxCodecEncodingBuffer_AppendBuffer(data->encodingBuffer, buffer); + + // now fake out the list array so it thinks its full. + // Do this by reducing the capacity so there are no undefined buffers on the list + data->encodingBuffer->head->capacity = data->encodingBuffer->head->count; + + size_t position = ccnxCodecEncodingBuffer_AppendBuffer(data->encodingBuffer, buffer); + assertTrue(position == 1, "Wrong position, got %zu expected %u", position, 1); + + _ccnxCodecEncodingBuffer_Validate(data->encodingBuffer); + assertTrue(data->encodingBuffer->totalCount == 2, "Wrong count, got %u expected %u", data->encodingBuffer->totalCount, 2); + assertTrue(data->encodingBuffer->totalBytes == 10, "Wrong bytes, got %zu expected %u", data->encodingBuffer->totalBytes, 10); + + // should now have different head and tail + assertTrue(data->encodingBuffer->head != data->encodingBuffer->tail, "Head == tail") + { + ccnxCodecEncodingBuffer_Display(data->encodingBuffer, 0); + } + + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecEncodingBuffer_Create) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + assertNotNull(data->encodingBuffer, "Got null buffer from create"); + assertNull(data->encodingBuffer->head, "buffer head is not null"); + assertNull(data->encodingBuffer->tail, "Buffer tail is not null"); + assertTrue(data->encodingBuffer->totalCount == 0, "Buffer itemCount is not 0"); + assertTrue(data->encodingBuffer->totalBytes == 0, "Buffer totalBytes is not 0"); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecEncodingBuffer_CreateIOVec) +{ + char foo[] = "foo"; + char bar[] = "barbar"; + + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCBuffer *buffer_1 = parcBuffer_Wrap(foo, sizeof(foo), 0, sizeof(foo)); + PARCBuffer *buffer_2 = parcBuffer_Wrap(bar, sizeof(bar), 0, sizeof(bar)); + ccnxCodecEncodingBuffer_AppendBuffer(data->encodingBuffer, buffer_2); + ccnxCodecEncodingBuffer_PrependBuffer(data->encodingBuffer, buffer_1); + + CCNxCodecEncodingBufferIOVec *iov = ccnxCodecEncodingBuffer_CreateIOVec(data->encodingBuffer); + assertNotNull(iov, "Got null iov from CreateIOVec"); + assertTrue(iov->iovcnt == 2, "Wrong iovec count, got %d expected %d", iov->iovcnt, 2); + assertTrue(iov->iov[0].iov_base == foo, "WRong iov[0].iov_base, got %p exected %p", iov->iov[0].iov_base, foo); + assertTrue(iov->iov[1].iov_base == bar, "WRong iov[1].iov_base, got %p exected %p", iov->iov[1].iov_base, bar); + assertTrue(iov->iov[0].iov_len == sizeof(foo), "WRong iov[1].iov_base, got %zu exected %zu", iov->iov[0].iov_len, sizeof(foo)); + assertTrue(iov->iov[1].iov_len == sizeof(bar), "WRong iov[1].iov_base, got %zu exected %zu", iov->iov[1].iov_len, sizeof(bar)); + + // Slice crossing two iovec arrays + CCNxCodecEncodingBuffer *bufferSlice = ccnxCodecEncodingBuffer_Slice(data->encodingBuffer, 1, 6); + CCNxCodecEncodingBufferIOVec *iovSlice = ccnxCodecEncodingBuffer_CreateIOVec(bufferSlice); + assertTrue(iovSlice->iovcnt == 2, "Wrong iovec count, got %d expected %d", iovSlice->iovcnt, 2); + assertTrue(iovSlice->iov[0].iov_base == foo + 1, "WRong iovSlice[0].iov_base, got %p exected %p", iovSlice->iov[0].iov_base, foo + 1); + assertTrue(iovSlice->iov[0].iov_len == sizeof(foo) - 1, "WRong iovSlice[1].iov_len, got %zu exected %zu", iovSlice->iov[0].iov_len, sizeof(foo) - 1); + assertTrue(iovSlice->iov[1].iov_len == 6 - (sizeof(foo) - 1), "WRong iovSlice[1].iov_base, got %zu exected %lu", iovSlice->iov[1].iov_len, 6 - (sizeof(foo) - 1)); + ccnxCodecEncodingBufferIOVec_Release(&iovSlice); + ccnxCodecEncodingBuffer_Release(&bufferSlice); + + // Slice within one iovec array + bufferSlice = ccnxCodecEncodingBuffer_Slice(data->encodingBuffer, 1, 1); + iovSlice = ccnxCodecEncodingBuffer_CreateIOVec(bufferSlice); + assertTrue(iovSlice->iovcnt == 1, "Wrong iovec count, got %d expected %d", iovSlice->iovcnt, 1); + assertTrue(iovSlice->iov[0].iov_base == foo + 1, "WRong iovSlice[0].iov_base, got %p exected %p", iovSlice->iov[0].iov_base, foo + 1); + assertTrue(iovSlice->iov[0].iov_len == 1, "WRong iovSlice[1].iov_len, got %zu exected %d", iovSlice->iov[0].iov_len, 1); + ccnxCodecEncodingBufferIOVec_Release(&iovSlice); + ccnxCodecEncodingBuffer_Release(&bufferSlice); + + // Slice beyond contents + bufferSlice = ccnxCodecEncodingBuffer_Slice(data->encodingBuffer, sizeof(foo) + sizeof(bar), 1); + assertNull(bufferSlice, "ccnxCodecEncodingBuffer_Slice returned allocation for slice outside of buffer"); + + // Slice including all and beyond + bufferSlice = ccnxCodecEncodingBuffer_Slice(data->encodingBuffer, 0, sizeof(foo) + sizeof(bar) + 10); + iovSlice = ccnxCodecEncodingBuffer_CreateIOVec(bufferSlice); + assertTrue(iovSlice->iovcnt == 2, "Wrong iovec count, got %d expected %d", iovSlice->iovcnt, 2); + assertTrue(iovSlice->iov[0].iov_base == foo, "WRong iovSlice[0].iov_base, got %p exected %p", iovSlice->iov[0].iov_base, foo); + assertTrue(iovSlice->iov[0].iov_len == sizeof(foo), "WRong iovSlice[1].iov_len, got %zu exected %zu", iovSlice->iov[0].iov_len, sizeof(foo)); + assertTrue(iovSlice->iov[1].iov_len == sizeof(bar), "WRong iovSlice[1].iov_base, got %zu exected %lu", iovSlice->iov[1].iov_len, sizeof(bar)); + ccnxCodecEncodingBufferIOVec_Release(&iovSlice); + ccnxCodecEncodingBuffer_Release(&bufferSlice); + + ccnxCodecEncodingBufferIOVec_Release(&iov); + + parcBuffer_Release(&buffer_1); + parcBuffer_Release(&buffer_2); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecEncodingBuffer_CreateIOVec_Empty) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + CCNxCodecEncodingBufferIOVec *iov = ccnxCodecEncodingBuffer_CreateIOVec(data->encodingBuffer); + assertNotNull(iov, "Got null iov from CreateIOVec"); + assertTrue(iov->iovcnt == 0, "Wrong iovec count, got %d expected %d", iov->iovcnt, 0); + + // single allocation means that the iov will never be null + assertNotNull(iov->iov, "iov->iov should not be null"); + + ccnxCodecEncodingBufferIOVec_Release(&iov); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecEncodingBuffer_Display) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCBuffer *buffer = parcBuffer_Wrap("hello", 5, 0, 5); + ccnxCodecEncodingBuffer_AppendBuffer(data->encodingBuffer, buffer); + parcBuffer_Release(&buffer); + + ccnxCodecEncodingBuffer_Display(data->encodingBuffer, 0); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecEncodingBuffer_Length) +{ + char foo[] = "foo"; + char bar[] = "barbar"; + + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCBuffer *buffer_1 = parcBuffer_Wrap(foo, sizeof(foo), 0, sizeof(foo)); + PARCBuffer *buffer_2 = parcBuffer_Wrap(bar, sizeof(bar), 0, sizeof(bar)); + ccnxCodecEncodingBuffer_AppendBuffer(data->encodingBuffer, buffer_1); + ccnxCodecEncodingBuffer_AppendBuffer(data->encodingBuffer, buffer_2); + + size_t length = ccnxCodecEncodingBuffer_Length(data->encodingBuffer); + size_t truth = sizeof(foo) + sizeof(bar); + assertTrue(length == truth, "Wrong length, got %zu expected %zu", length, truth); + + parcBuffer_Release(&buffer_1); + parcBuffer_Release(&buffer_2); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecEncodingBuffer_Size) +{ + char foo[] = "foo"; + char bar[] = "barbar"; + + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCBuffer *buffer_1 = parcBuffer_Wrap(foo, sizeof(foo), 0, sizeof(foo)); + PARCBuffer *buffer_2 = parcBuffer_Wrap(bar, sizeof(bar), 0, sizeof(bar)); + ccnxCodecEncodingBuffer_AppendBuffer(data->encodingBuffer, buffer_1); + ccnxCodecEncodingBuffer_AppendBuffer(data->encodingBuffer, buffer_2); + + size_t size = ccnxCodecEncodingBuffer_Size(data->encodingBuffer); + assertTrue(size == 2, "Wrong size, got %zu expected %u", size, 2); + + parcBuffer_Release(&buffer_1); + parcBuffer_Release(&buffer_2); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecEncodingBuffer_Example) +{ + PARCBuffer *name = parcBuffer_Wrap("marc", 4, 0, 4); + PARCBuffer *space = parcBuffer_Wrap(" ", 1, 0, 1); + PARCBuffer *email = parcBuffer_Wrap("<marc@example.com>", 18, 0, 18); + + CCNxCodecEncodingBuffer *encodingBuffer = ccnxCodecEncodingBuffer_Create(); + ccnxCodecEncodingBuffer_AppendBuffer(encodingBuffer, name); + ccnxCodecEncodingBuffer_AppendBuffer(encodingBuffer, space); + parcBuffer_Release(&space); + parcBuffer_Release(&name); + + CCNxCodecEncodingBuffer *emailBuffer = ccnxCodecEncodingBuffer_Create(); + ccnxCodecEncodingBuffer_AppendBuffer(emailBuffer, email); + parcBuffer_Release(&email); + + ccnxCodecEncodingBuffer_Release(&emailBuffer); + + CCNxCodecEncodingBufferIOVec *iov = ccnxCodecEncodingBuffer_CreateIOVec(encodingBuffer); + ssize_t nwritten = writev(STDOUT_FILENO, iov->iov, iov->iovcnt); + assertTrue(nwritten > -1, "Error writev"); + + ccnxCodecEncodingBufferIOVec_Release(&iov); + + ccnxCodecEncodingBuffer_Release(&encodingBuffer); +} + +LONGBOW_TEST_FIXTURE(Local) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(ccnxCodec_EncodingBuffer); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-common/ccnx/common/codec/test/test_ccnxCodec_Error.c b/libccnx-common/ccnx/common/codec/test/test_ccnxCodec_Error.c new file mode 100755 index 00000000..1fee88c3 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/test/test_ccnxCodec_Error.c @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../ccnxCodec_Error.c" +#include <LongBow/unit-test.h> +#include <parc/algol/parc_SafeMemory.h> + +LONGBOW_TEST_RUNNER(tlv_Errors) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(tlv_Errors) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(tlv_Errors) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecError_Create_Destroy); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecError_GetByteOffset); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecError_GetErrorCode); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecError_GetLine); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecError_GetFunction); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecError_GetErrorMessage); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, ccnxCodecError_Create_Destroy) +{ + CCNxCodecError *error = ccnxCodecError_Create(TLV_ERR_NO_ERROR, "apple", 10, 100); + ccnxCodecError_Release(&error); + assertTrue(parcMemory_Outstanding() == 0, "memory imbalance after create/destroy"); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecError_GetByteOffset) +{ + CCNxCodecError *error = ccnxCodecError_Create(TLV_ERR_NO_ERROR, "apple", 10, 100); + assertTrue(ccnxCodecError_GetByteOffset(error) == 100, + "Wrong offset, expected %u got %zu", + 100, + ccnxCodecError_GetByteOffset(error)); + ccnxCodecError_Release(&error); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecError_GetErrorCode) +{ + CCNxCodecError *error = ccnxCodecError_Create(TLV_ERR_NO_ERROR, "apple", 10, 100); + assertTrue(ccnxCodecError_GetErrorCode(error) == TLV_ERR_NO_ERROR, + "Wrong error code, expected %d got %d", + TLV_ERR_NO_ERROR, + ccnxCodecError_GetErrorCode(error)); + ccnxCodecError_Release(&error); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecError_GetLine) +{ + CCNxCodecError *error = ccnxCodecError_Create(TLV_ERR_NO_ERROR, "apple", 10, 100); + assertTrue(ccnxCodecError_GetLine(error) == 10, + "Wrong line number, expected %d got %d", + 10, + ccnxCodecError_GetLine(error)); + ccnxCodecError_Release(&error); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecError_GetFunction) +{ + char *apple = "apple"; + + CCNxCodecError *error = ccnxCodecError_Create(TLV_ERR_NO_ERROR, apple, 10, 100); + assertTrue(ccnxCodecError_GetFunction(error) == apple, + "Wrong function string, expected %p got %p", + apple, + ccnxCodecError_GetFunction(error)); + ccnxCodecError_Release(&error); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecError_GetErrorMessage) +{ + char *apple = "apple"; + const char *truth = ccnxCodecErrors_ErrorMessage(TLV_ERR_NO_ERROR); + + CCNxCodecError *error = ccnxCodecError_Create(TLV_ERR_NO_ERROR, apple, 10, 100); + assertTrue(ccnxCodecError_GetErrorMessage(error) == truth, + "Wrong function string, expected %p got %p", + truth, + ccnxCodecError_GetErrorMessage(error)); + ccnxCodecError_Release(&error); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(tlv_Errors); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-common/ccnx/common/codec/test/test_ccnxCodec_NetworkBuffer.c b/libccnx-common/ccnx/common/codec/test/test_ccnxCodec_NetworkBuffer.c new file mode 100755 index 00000000..aec3018e --- /dev/null +++ b/libccnx-common/ccnx/common/codec/test/test_ccnxCodec_NetworkBuffer.c @@ -0,0 +1,952 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../ccnxCodec_NetworkBuffer.c" +#include <parc/algol/parc_SafeMemory.h> +#include <LongBow/unit-test.h> + +#include <stdlib.h> +#include <stdio.h> +#include <fcntl.h> +#include <arpa/inet.h> + +#include <parc/security/parc_Security.h> +#include <parc/security/parc_Pkcs12KeyStore.h> +#include <parc/security/parc_PublicKeySigner.h> + +typedef struct test_data { + CCNxCodecNetworkBuffer *buffer; +} TestData; + +static TestData * +_commonSetup(void) +{ + TestData *data = parcMemory_AllocateAndClear(sizeof(TestData)); + assertNotNull(data, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(TestData)); + data->buffer = ccnxCodecNetworkBuffer_Create(&ParcMemoryMemoryBlock, NULL); + return data; +} + +static void +_commonTeardown(TestData *data) +{ + ccnxCodecNetworkBuffer_Release(&data->buffer); + parcMemory_Deallocate((void **) &data); +} + +LONGBOW_TEST_RUNNER(ccnx_NetworkBuffer) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); + LONGBOW_RUN_TEST_FIXTURE(SetLimit); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(ccnx_NetworkBuffer) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(ccnx_NetworkBuffer) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecNetworkBufferIoVec_Acquire); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecNetworkBuffer_Acquire); + + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecNetworkBuffer_ComputeSignature); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecNetworkBuffer_Create); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecNetworkBuffer_CreateFromArray); + + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecNetworkBuffer_CreateIoVec); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecNetworkBuffer_Display); + + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecNetworkBuffer_GetUint8); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecNetworkBuffer_GetUint8_NotCurrentBlock); + + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecNetworkBuffer_Position); + + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecNetworkBuffer_PutArray_SpaceOk); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecNetworkBuffer_PutArray_SpaceToZero); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecNetworkBuffer_PutArray_NoSpace); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecNetworkBuffer_PutArray_SpanThree); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecNetworkBuffer_PutBuffer); + + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecNetworkBuffer_PutUint16); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecNetworkBuffer_PutUint64); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecNetworkBuffer_PutUint8_SpaceOk); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecNetworkBuffer_PutUint8_SpaceToZero); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecNetworkBuffer_PutUint8_NoSpace); + + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecNetworkBuffer_PutUint32_OK); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecNetworkBuffer_PutUint32_2bytes); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecNetworkBuffer_PutUint32_2bytes_withnext); + + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecNetworkBuffer_SetPosition_BeyondLimit); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecNetworkBuffer_SetPosition_InCurrent); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecNetworkBuffer_SetPosition_InDifferent); + + LONGBOW_RUN_TEST_CASE(Global, ccnxNetworkbufferIoVec_GetArray); + LONGBOW_RUN_TEST_CASE(Global, ccnxNetworkbufferIoVec_GetCount); + LONGBOW_RUN_TEST_CASE(Global, ccnxNetworkbufferIoVec_Length); + LONGBOW_RUN_TEST_CASE(Global, ccnxNetworkbufferIoVec_Display); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + longBowTestCase_SetClipBoardData(testCase, _commonSetup()); + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + _commonTeardown(longBowTestCase_GetClipBoardData(testCase)); + + if (parcSafeMemory_ReportAllocation(STDOUT_FILENO) != 0) { + printf("('%s' leaks memory by %d (allocs - frees)) ", longBowTestCase_GetName(testCase), parcMemory_Outstanding()); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, ccnxCodecNetworkBufferIoVec_Acquire) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + CCNxCodecNetworkBufferIoVec *first = ccnxCodecNetworkBuffer_CreateIoVec(data->buffer); + assertTrue(first->refcount == 1, "Wrong refcount, got %u expected %u", first->refcount, 1); + + CCNxCodecNetworkBufferIoVec *second = ccnxCodecNetworkBufferIoVec_Acquire(first); + assertTrue(first->refcount == 2, "Wrong refcount, got %u expected %u", first->refcount, 2); + + ccnxCodecNetworkBufferIoVec_Release(&second); + ccnxCodecNetworkBufferIoVec_Release(&first); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecNetworkBuffer_Acquire) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + CCNxCodecNetworkBuffer *second = ccnxCodecNetworkBuffer_Acquire(data->buffer); + assertTrue(data->buffer->refcount == 2, "wrong refcount, got %u expected %u", data->buffer->refcount, 2); + ccnxCodecNetworkBuffer_Release(&second); + assertTrue(data->buffer->refcount == 1, "wrong refcount, got %u expected %u", data->buffer->refcount, 1); +} + +/* + * Uses a test set generated by openssl: + * openssl genrsa -out test_rsa_key.pem + * openssl rsa -pubout -in test_rsa_key.pem -out test_rsa_pub.pem + * openssl req -new -key test_rsa_key.pem -out test_rsa.csr + * openssl x509 -req -days 365 -in test_rsa.csr -signkey test_rsa_key.pem -out test_rsa.crt + * openssl pkcs12 -export -in test_rsa.crt -inkey test_rsa_key.pem -out test_rsa.p12 -name ccnxuser -CAfile test_rsa.crt -caname root -chain -passout pass:blueberry + * openssl sha -sha256 -sign test_rsa_key.pem -out test_random_bytes.sig < test_random_bytes + * + * In English: generate a public private key, put it in a PKCS12 file (test_rsa.p12), then use that to sign + * a buffer (test_random_bytes) and put the signature in a file (test_random_bytes.sig). + */ +LONGBOW_TEST_CASE(Global, ccnxCodecNetworkBuffer_ComputeSignature) +{ + parcSecurity_Init(); + + PARCPkcs12KeyStore *publicKeyStore = parcPkcs12KeyStore_Open("test_rsa.p12", "blueberry", PARCCryptoHashType_SHA256); + PARCKeyStore *keyStore = parcKeyStore_Create(publicKeyStore, PARCPkcs12KeyStoreAsKeyStore); + parcPkcs12KeyStore_Release(&publicKeyStore); + PARCPublicKeySigner *publicKeySigner = parcPublicKeySigner_Create(keyStore, PARCSigningAlgorithm_RSA, PARCCryptoHashType_SHA256); + PARCSigner *signer = parcSigner_Create(publicKeySigner, PARCPublicKeySignerAsSigner); + parcPublicKeySigner_Release(&publicKeySigner); + + parcKeyStore_Release(&keyStore); + assertNotNull(signer, "Got null result from opening openssl pkcs12 file"); + + // read the buffer to sign + int fd = open("test_random_bytes", O_RDONLY); + assertTrue(fd != -1, "Cannot open test_random_bytes file."); + uint8_t buffer_to_sign[2048]; + ssize_t read_bytes = read(fd, buffer_to_sign, 2048); + close(fd); + + // Put it in a NetworkBuffer + TestData *data = longBowTestCase_GetClipBoardData(testCase); + ccnxCodecNetworkBuffer_PutArray(data->buffer, read_bytes, buffer_to_sign); + + // Sign it + PARCSignature *testSignature = ccnxCodecNetworkBuffer_ComputeSignature(data->buffer, 0, ccnxCodecNetworkBuffer_Limit(data->buffer), signer); + PARCBuffer *testBytes = parcSignature_GetSignature(testSignature); + + // now read the "true" signature + uint8_t scratch_buffer[1024]; + fd = open("test_random_bytes.sig", O_RDONLY); + assertTrue(fd != -1, "Cannot open test_random_bytes.sig file."); + read_bytes = read(fd, scratch_buffer, 1024); + assertTrue(read_bytes == 128, "read incorrect size signature from disk: %zu", read_bytes); + close(fd); + + PARCBuffer *truth = parcBuffer_Wrap(scratch_buffer, read_bytes, 0, read_bytes); + + assertTrue(parcBuffer_Equals(testBytes, truth), "Signatures do not match") + { + parcBuffer_Display(testBytes, 0); + parcBuffer_Display(truth, 0); + } + + parcBuffer_Release(&truth); + parcSignature_Release(&testSignature); + parcSigner_Release(&signer); + + parcSecurity_Fini(); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecNetworkBuffer_Create) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + assertNotNull(data->buffer, "null buffer"); + assertTrue(data->buffer->head == data->buffer->current && data->buffer->current == data->buffer->tail, + "wrong pointers, head should equal current should equal tail"); + assertTrue(data->buffer->refcount == 1, "wrong refcount, got %u expected %u", data->buffer->refcount, 1); + assertTrue(data->buffer->position == 0, "wrong position, got %zu expected %u", data->buffer->position, 1); +} + + +LONGBOW_TEST_CASE(Global, ccnxCodecNetworkBuffer_CreateFromArray) +{ + size_t length = 64; + uint8_t *memory = parcMemory_Allocate(length); + assertNotNull(memory, "parcMemory_Allocate(%zu) returned NULL", length); + for (int i = 0; i < length; i++) { + memory[i] = i * 3; + } + + CCNxCodecNetworkBuffer *netbuff = ccnxCodecNetworkBuffer_CreateFromArray(&ParcMemoryMemoryBlock, NULL, length, memory); + + assertNotNull(netbuff, "Got null from createFromArray"); + + PARCBuffer *test = ccnxCodecNetworkBuffer_CreateParcBuffer(netbuff); + PARCBuffer *truth = parcBuffer_Wrap(memory, length, 0, length); + + assertTrue(parcBuffer_Equals(test, truth), "Buffers do not match") + { + ccnxCodecNetworkBuffer_Display(netbuff, 3); + parcBuffer_Display(test, 3); + parcBuffer_Display(truth, 3); + } + + parcBuffer_Release(&truth); + parcBuffer_Release(&test); + ccnxCodecNetworkBuffer_Release(&netbuff); +} + + +LONGBOW_TEST_CASE(Global, ccnxCodecNetworkBuffer_CreateIoVec) +{ + // Write an array that will span 3 blocks + TestData *data = longBowTestCase_GetClipBoardData(testCase); + size_t arrayLength = 8192; + uint8_t array[arrayLength]; + + // fill the array with stuff so we have a pattern that must match + for (size_t i = 0; i < arrayLength; i++) { + array[i] = i; + } + + ccnxCodecNetworkBuffer_PutArray(data->buffer, arrayLength, array); + CCNxCodecNetworkBufferIoVec *vec = ccnxCodecNetworkBuffer_CreateIoVec(data->buffer); + + assertTrue(vec->iovcnt == 5, "iovcnt wrong got %d expected %d", vec->iovcnt, 5); + assertTrue(vec->totalBytes == 8192, "Wrong total bytes, got %zu expected %u", vec->totalBytes, 8192) + { + ccnxCodecNetworkBufferIoVec_Display(vec, 3); + } + + ccnxCodecNetworkBufferIoVec_Release(&vec); +} + +/* + * not much to do excpet make sure there's no leaks or assertions + */ +LONGBOW_TEST_CASE(Global, ccnxCodecNetworkBuffer_Display) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + ccnxCodecNetworkBuffer_Display(data->buffer, 0); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecNetworkBuffer_Position) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + data->buffer->position = 22; + + size_t test = ccnxCodecNetworkBuffer_Position(data->buffer); + assertTrue(test == 22, "wrong position, got %zu expected %u", test, 22); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecNetworkBuffer_PutArray_SpaceOk) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + uint8_t array[] = { 1, 2, 3, 4, 5, 6 }; + size_t nextPosition = data->buffer->position + sizeof(array); + + ccnxCodecNetworkBuffer_PutArray(data->buffer, sizeof(array), array); + assertTrue(data->buffer->position == nextPosition, "Wrong position, got %zu expected %zu", data->buffer->position, nextPosition); + assertTrue(memcmp(&data->buffer->current->memory[0], array, sizeof(array)) == 0, "wrong memory"); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecNetworkBuffer_PutArray_SpaceToZero) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + uint8_t array[] = { 1, 2, 3, 4, 5, 6 }; + + size_t startPosition = data->buffer->capacity - sizeof(array); + size_t nextPosition = startPosition + sizeof(array); + + data->buffer->position = startPosition; + + ccnxCodecNetworkBuffer_PutArray(data->buffer, sizeof(array), array); + assertTrue(data->buffer->position == nextPosition, "Wrong position, got %zu expected %zu", data->buffer->position, nextPosition); + assertTrue(memcmp(&data->buffer->current->memory[startPosition], array, sizeof(array)) == 0, "wrong memory"); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecNetworkBuffer_PutArray_NoSpace) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + uint8_t array[] = { 1, 2, 3, 4, 5, 6 }; + + // 3 elements in the current block, 3 in the next block + size_t startPosition = data->buffer->capacity - 3; + size_t nextPosition = startPosition + sizeof(array); + + data->buffer->position = startPosition; + + ccnxCodecNetworkBuffer_PutArray(data->buffer, sizeof(array), array); + assertTrue(data->buffer->position == nextPosition, "Wrong position, got %zu expected %zu", data->buffer->position, nextPosition); + assertTrue(memcmp(&data->buffer->head->memory[startPosition], array, 3) == 0, "wrong memory"); + assertTrue(memcmp(&data->buffer->tail->memory[0], array + 3, 3) == 0, "wrong memory"); + // and we should have a new buffer + assertTrue(data->buffer->head != data->buffer->tail, "head should not be equal to tail"); +} + + +LONGBOW_TEST_CASE(Global, ccnxCodecNetworkBuffer_PutArray_SpanThree) +{ + // Write an array that will span 3 blocks + TestData *data = longBowTestCase_GetClipBoardData(testCase); + size_t arrayLength = 8192; + uint8_t array[arrayLength]; + + // fill the array with stuff so we have a pattern that must match + for (size_t i = 0; i < arrayLength; i++) { + array[i] = i; + } + + ccnxCodecNetworkBuffer_PutArray(data->buffer, arrayLength, array); + + CCNxCodecNetworkBufferMemory *block = data->buffer->head; + size_t offset = 0; + while (offset < arrayLength) { + size_t remaining = (arrayLength - offset > block->capacity) ? block->capacity : arrayLength - offset; + assertTrue(memcmp(&block->memory[0], array + offset, remaining) == 0, "wrong memory"); + offset += remaining; + block = block->next; + } +} + +LONGBOW_TEST_CASE(Global, ccnxCodecNetworkBuffer_PutBuffer) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + uint8_t array[] = { 1, 2, 3, 4, 5, 6 }; + PARCBuffer *buffer = parcBuffer_Wrap(array, sizeof(array), 0, sizeof(array)); + + size_t nextPosition = data->buffer->position + sizeof(array); + + ccnxCodecNetworkBuffer_PutBuffer(data->buffer, buffer); + assertTrue(data->buffer->position == nextPosition, "Wrong position, got %zu expected %zu", data->buffer->position, nextPosition); + assertTrue(memcmp(&data->buffer->current->memory[0], array, sizeof(array)) == 0, "wrong memory"); + + parcBuffer_Release(&buffer); +} + + +LONGBOW_TEST_CASE(Global, ccnxCodecNetworkBuffer_PutUint16) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + uint16_t value = 0x2587; + size_t nextPosition = data->buffer->position + sizeof(value); + + ccnxCodecNetworkBuffer_PutUint16(data->buffer, value); + assertTrue(data->buffer->position == nextPosition, "Wrong position, got %zu expected %zu", data->buffer->position, nextPosition); + + uint16_t testValue = htons(value); + assertTrue(memcmp(&data->buffer->current->memory[0], &testValue, sizeof(testValue)) == 0, "wrong memory") + { + ccnxCodecNetworkBuffer_Display(data->buffer, 0); + } +} + +LONGBOW_TEST_CASE(Global, ccnxCodecNetworkBuffer_PutUint64) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + uint64_t value = 0xABCDEF0122334455; + size_t nextPosition = data->buffer->position + sizeof(value); + + ccnxCodecNetworkBuffer_PutUint64(data->buffer, value); + assertTrue(data->buffer->position == nextPosition, "Wrong position, got %zu expected %zu", data->buffer->position, nextPosition); + + uint8_t truthValue[] = { 0xAB, 0xCD, 0xEF, 0x01, 0x22, 0x33, 0x44, 0x55 }; + assertTrue(memcmp(&data->buffer->current->memory[0], truthValue, sizeof(truthValue)) == 0, "wrong memory") + { + ccnxCodecNetworkBuffer_Display(data->buffer, 0); + } +} + +/* + * Put a uint32 in to a block with plenty of space + */ +LONGBOW_TEST_CASE(Global, ccnxCodecNetworkBuffer_PutUint32_OK) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + uint32_t value = 0xABCDEF01; + size_t nextPosition = data->buffer->position + 4; + + ccnxCodecNetworkBuffer_PutUint32(data->buffer, value); + assertTrue(data->buffer->position == nextPosition, "Wrong position, got %zu expected %zu", data->buffer->position, nextPosition); + + uint32_t testValue = htonl(value); + assertTrue(memcmp(&data->buffer->current->memory[0], &testValue, sizeof(testValue)) == 0, "wrong memory") + { + ccnxCodecNetworkBuffer_Display(data->buffer, 0); + } +} + +/* + * The current block only has 2 bytes left and there is no next pointer. Should throw away + * those 2 bytes, allocate a new block, then write the whole thing there. + */ +LONGBOW_TEST_CASE(Global, ccnxCodecNetworkBuffer_PutUint32_2bytes) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + // set limit and position out to capacity -2 + data->buffer->current->limit = data->buffer->current->capacity - 2; + data->buffer->position = data->buffer->current->limit; + + uint32_t value = 0xABCDEF01; + size_t nextPosition = data->buffer->position + 4; + + ccnxCodecNetworkBuffer_PutUint32(data->buffer, value); + assertTrue(data->buffer->position == nextPosition, "Wrong position, got %zu expected %zu", data->buffer->position, nextPosition); + + uint32_t testValue = htonl(value); + assertTrue(memcmp(&data->buffer->current->memory[0], &testValue, sizeof(testValue)) == 0, "wrong memory") + { + ccnxCodecNetworkBuffer_Display(data->buffer, 0); + } +} + +/* + * The current block only has 2 bytes left and there is a next block. Because the current + * block is frozen, it will need to split the write over the two blocks. + */ +LONGBOW_TEST_CASE(Global, ccnxCodecNetworkBuffer_PutUint32_2bytes_withnext) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + // this is where we'll want to start our write + size_t start = data->buffer->current->capacity - 2; + size_t nextPosition = start + 4; + + // set limit and position out to capacity then allocate another block + data->buffer->current->limit = data->buffer->current->capacity; + data->buffer->position = data->buffer->current->limit; + _ccnxCodecNetworkBuffer_AllocateIfNeeded(data->buffer); + + ccnxCodecNetworkBuffer_SetPosition(data->buffer, start); + uint32_t value = 0x33445566; + ccnxCodecNetworkBuffer_PutUint32(data->buffer, value); + assertTrue(data->buffer->position == nextPosition, "Wrong position, got %zu expected %zu", data->buffer->position, nextPosition); + + uint32_t testValue = htonl(value); + // check the value is split between the two buffers + assertTrue(memcmp(&data->buffer->head->memory[start], &testValue, 2) == 0, "wrong memory in first buffer") + { + ccnxCodecNetworkBuffer_Display(data->buffer, 0); + } + + assertTrue(memcmp(&data->buffer->tail->memory[0], (uint8_t *) &testValue + 2, 2) == 0, "wrong memory in second buffer") + { + ccnxCodecNetworkBuffer_Display(data->buffer, 0); + } +} + +LONGBOW_TEST_CASE(Global, ccnxCodecNetworkBuffer_GetUint8) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + uint8_t value = 1; + + ccnxCodecNetworkBuffer_PutUint8(data->buffer, value); + + uint8_t test = ccnxCodecNetworkBuffer_GetUint8(data->buffer, 0); + assertTrue(data->buffer->current->memory[0] == test, "wrong memory, got %u expected %u", test, data->buffer->current->memory[0]); +} + +/* + * Write stuff that spans two blocks, then get the uint8 from the second block + */ +LONGBOW_TEST_CASE(Global, ccnxCodecNetworkBuffer_GetUint8_NotCurrentBlock) +{ + // Write an array that will span 5 blocks + TestData *data = longBowTestCase_GetClipBoardData(testCase); + size_t arrayLength = 8192; + uint8_t array[arrayLength]; + + // fill the array with stuff so we have a pattern that must match + for (size_t i = 0; i < arrayLength; i++) { + array[i] = i; + } + + ccnxCodecNetworkBuffer_PutArray(data->buffer, arrayLength, array); + + uint8_t test = ccnxCodecNetworkBuffer_GetUint8(data->buffer, 4777); + assertTrue(test == array[4777], "Data at index 4777 wrong, got %02X expected %02X", + test, array[4777]); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecNetworkBuffer_PutUint8_SpaceOk) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + uint8_t value = 1; + size_t relativePosition = data->buffer->position - data->buffer->current->begin; + size_t nextPosition = data->buffer->position + 1; + + ccnxCodecNetworkBuffer_PutUint8(data->buffer, value); + assertTrue(data->buffer->position == nextPosition, "Wrong position, got %zu expected %zu", data->buffer->position, nextPosition); + assertTrue(data->buffer->current->memory[relativePosition] == value, "wrong memory"); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecNetworkBuffer_PutUint8_SpaceToZero) +{ + // put the position to just before the end of the buffer + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + uint8_t value = 1; + + data->buffer->position = data->buffer->current->capacity - 1; + size_t relativePosition = data->buffer->position - data->buffer->current->begin; + size_t nextPosition = data->buffer->position + 1; + + ccnxCodecNetworkBuffer_PutUint8(data->buffer, value); + assertTrue(data->buffer->position == nextPosition, "Wrong position, got %zu expected %zu", data->buffer->position, nextPosition); + assertTrue(data->buffer->current->memory[relativePosition] == value, "wrong memory"); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecNetworkBuffer_PutUint8_NoSpace) +{ + // put the position at the end of the current buffer, force an allocation + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + uint8_t value = 1; + + // pretend we've written all the way out to the capacity + data->buffer->position = data->buffer->current->capacity; + data->buffer->current->limit = data->buffer->current->capacity; + + size_t nextPosition = data->buffer->position + 1; + + ccnxCodecNetworkBuffer_PutUint8(data->buffer, value); + + size_t relativePosition = 0; + + assertTrue(data->buffer->position == nextPosition, "Wrong position, got %zu expected %zu", data->buffer->position, nextPosition); + assertTrue(data->buffer->current->memory[relativePosition] == value, "wrong memory"); + // and we should have a new buffer + assertTrue(data->buffer->head != data->buffer->tail, "head should not be equal to tail"); +} + +/* + * Set position beyond the limit of what's been written + */ +LONGBOW_TEST_CASE_EXPECTS(Global, ccnxCodecNetworkBuffer_SetPosition_BeyondLimit, .event = &LongBowAssertEvent) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + size_t limit = ccnxCodecNetworkBuffer_Limit(data->buffer); + ccnxCodecNetworkBuffer_SetPosition(data->buffer, limit + 1); +} + +/* + * Set position to good location that is in the current block + */ +LONGBOW_TEST_CASE(Global, ccnxCodecNetworkBuffer_SetPosition_InCurrent) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + ccnxCodecNetworkBuffer_PutUint32(data->buffer, 0x12345678); + + size_t limit = ccnxCodecNetworkBuffer_Limit(data->buffer); + ccnxCodecNetworkBuffer_SetPosition(data->buffer, limit - 1); + + assertTrue(data->buffer->current->memory[data->buffer->position] == 0x78, + "Wrong memory got %02X expected %02X", + data->buffer->current->memory[data->buffer->position], 0x78) + { + ccnxCodecNetworkBuffer_Display(data->buffer, 0); + } +} + +/* + * Set position to a good location that is not in the current block + */ +LONGBOW_TEST_CASE(Global, ccnxCodecNetworkBuffer_SetPosition_InDifferent) +{ + // Write an array that will span 5 blocks + TestData *data = longBowTestCase_GetClipBoardData(testCase); + size_t arrayLength = 8192; + uint8_t array[arrayLength]; + + // fill the array with stuff so we have a pattern that must match + for (size_t i = 0; i < arrayLength; i++) { + array[i] = i; + } + + ccnxCodecNetworkBuffer_PutArray(data->buffer, arrayLength, array); + + ccnxCodecNetworkBuffer_SetPosition(data->buffer, 4777); + + assertTrue(data->buffer->position == 4777, "Wrong position set, got %zu expected %u", data->buffer->position, 4777); + assertTrue(_ccnxCodecNetworkBufferMemory_ContainsPosition(data->buffer->current, 4777), "Did not seek to right position"); +} + +LONGBOW_TEST_CASE(Global, ccnxNetworkbufferIoVec_GetArray) +{ + // Write an array that will span 3 blocks + TestData *data = longBowTestCase_GetClipBoardData(testCase); + size_t arrayLength = 8192; + uint8_t array[arrayLength]; + + // fill the array with stuff so we have a pattern that must match + for (size_t i = 0; i < arrayLength; i++) { + array[i] = i; + } + + ccnxCodecNetworkBuffer_PutArray(data->buffer, arrayLength, array); + CCNxCodecNetworkBufferIoVec *vec = ccnxCodecNetworkBuffer_CreateIoVec(data->buffer); + + const struct iovec *iov = ccnxCodecNetworkBufferIoVec_GetArray(vec); + assertTrue(iov == vec->array, "Got wrong iovec array, got %p expected %p", (void *) iov, (void *) vec->array); + + ccnxCodecNetworkBufferIoVec_Release(&vec); +} + +LONGBOW_TEST_CASE(Global, ccnxNetworkbufferIoVec_GetCount) +{ + // Write an array that will span 3 blocks + TestData *data = longBowTestCase_GetClipBoardData(testCase); + size_t arrayLength = 8192; + uint8_t array[arrayLength]; + + // fill the array with stuff so we have a pattern that must match + for (size_t i = 0; i < arrayLength; i++) { + array[i] = i; + } + + ccnxCodecNetworkBuffer_PutArray(data->buffer, arrayLength, array); + CCNxCodecNetworkBufferIoVec *vec = ccnxCodecNetworkBuffer_CreateIoVec(data->buffer); + + assertTrue(ccnxCodecNetworkBufferIoVec_GetCount(vec) == 5, "iovcnt wrong got %d expected %d", ccnxCodecNetworkBufferIoVec_GetCount(vec), 5); + + ccnxCodecNetworkBufferIoVec_Release(&vec); +} + +LONGBOW_TEST_CASE(Global, ccnxNetworkbufferIoVec_Length) +{ + // Write an array that will span 3 blocks + TestData *data = longBowTestCase_GetClipBoardData(testCase); + size_t arrayLength = 8192; + uint8_t array[arrayLength]; + + // fill the array with stuff so we have a pattern that must match + for (size_t i = 0; i < arrayLength; i++) { + array[i] = i; + } + + ccnxCodecNetworkBuffer_PutArray(data->buffer, arrayLength, array); + CCNxCodecNetworkBufferIoVec *vec = ccnxCodecNetworkBuffer_CreateIoVec(data->buffer); + + assertTrue(ccnxCodecNetworkBufferIoVec_Length(vec) == arrayLength, "Wrong length got %zu expected %zu", ccnxCodecNetworkBufferIoVec_Length(vec), arrayLength); + + ccnxCodecNetworkBufferIoVec_Release(&vec); +} + +LONGBOW_TEST_CASE(Global, ccnxNetworkbufferIoVec_Display) +{ + // Write an array that will span 3 blocks + TestData *data = longBowTestCase_GetClipBoardData(testCase); + size_t arrayLength = 8192; + uint8_t array[arrayLength]; + + // fill the array with stuff so we have a pattern that must match + for (size_t i = 0; i < arrayLength; i++) { + array[i] = i; + } + + ccnxCodecNetworkBuffer_PutArray(data->buffer, arrayLength, array); + CCNxCodecNetworkBufferIoVec *vec = ccnxCodecNetworkBuffer_CreateIoVec(data->buffer); + + ccnxCodecNetworkBufferIoVec_Display(vec, 0); + + ccnxCodecNetworkBufferIoVec_Release(&vec); +} + +// ===================================================================== + +LONGBOW_TEST_FIXTURE(SetLimit) +{ + LONGBOW_RUN_TEST_CASE(SetLimit, ccnxCodecNetworkBuffer_Finalize_EndOfTail); + LONGBOW_RUN_TEST_CASE(SetLimit, ccnxCodecNetworkBuffer_Finalize_MidOfTail); + LONGBOW_RUN_TEST_CASE(SetLimit, ccnxCodecNetworkBuffer_Finalize_StartOfTail); + LONGBOW_RUN_TEST_CASE(SetLimit, ccnxCodecNetworkBuffer_Finalize_EndOfMid); + LONGBOW_RUN_TEST_CASE(SetLimit, ccnxCodecNetworkBuffer_Finalize_MidOfMid); + LONGBOW_RUN_TEST_CASE(SetLimit, ccnxCodecNetworkBuffer_Finalize_StartOfMid); + LONGBOW_RUN_TEST_CASE(SetLimit, ccnxCodecNetworkBuffer_Finalize_Zero); +} + +LONGBOW_TEST_FIXTURE_SETUP(SetLimit) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(SetLimit) +{ + if (parcSafeMemory_ReportAllocation(STDOUT_FILENO) != 0) { + printf("('%s' leaks memory by %d (allocs - frees)) ", longBowTestCase_GetName(testCase), parcMemory_Outstanding()); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +typedef struct setlimit_s { + CCNxCodecNetworkBuffer *netbuff; + PARCBuffer *truth; +} SetLimitData; + +/* + * In this test, SetLimit is called when we are at position 4036 + * + * (always in ABSOLUTE bytes) + * position = 4077 + * begin = 0 begin = 1536 begin = 3577 | + * | | | | + * +--------------------------+--------------------------+--------------------------+ + * | block 0 | block 1 | block 2 | + * +--------------------------+--------------------------+--------------------------+ + * | | | | + * capacity = 1536 capacity = 2048 | capacity = 2048 + * limit = 1536 limit = 2041 limit = 500 + * (always in RELATIVE bytes) + */ +static SetLimitData +_allocateData(void) +{ + SetLimitData data; + + data.netbuff = ccnxCodecNetworkBuffer_Create(&ParcMemoryMemoryBlock, NULL); + + size_t buffer1_length = 3577; + uint8_t buffer1[buffer1_length]; + memset(buffer1, 0x11, buffer1_length); + + ccnxCodecNetworkBuffer_PutArray(data.netbuff, buffer1_length, buffer1); + assertTrue(data.netbuff->position == buffer1_length, "Wrong position, expected %zu got %zu", buffer1_length, data.netbuff->position); + + // we should be in 'block1' in the diagram + assertTrue(data.netbuff->current->limit == 2041, "wrong limit, expected %u got %zu", 2041, data.netbuff->current->limit); + + // now allocate the second buffer to move to 'block 2'. this should freeze 'block 1' at 2000 bytes. + + // now we need to write it at 8 bytes to get block 1 to freeze + uint64_t x = 0x1234567812345678ULL; + + ccnxCodecNetworkBuffer_PutUint64(data.netbuff, x); + assertTrue(data.netbuff->position == 3585, "Wrong position, expected %u got %zu", 3585, data.netbuff->position); + assertTrue(data.netbuff->current->limit == 8, "wrong limit, expected %u got %zu", 8, data.netbuff->current->limit); + + size_t buffer2_length = 492; + uint8_t buffer2[buffer2_length]; + memset(buffer2, 0xaa, buffer2_length); + + ccnxCodecNetworkBuffer_PutArray(data.netbuff, buffer2_length, buffer2); + + assertTrue(data.netbuff->position == 4077, "Wrong position, expected %u got %zu", 4077, data.netbuff->current->limit); + assertTrue(data.netbuff->current->limit == 500, "wrong limit, expected %u got %zu", 500, data.netbuff->current->limit); + + data.truth = parcBuffer_Allocate(buffer1_length + buffer2_length + 8); + parcBuffer_PutArray(data.truth, buffer1_length, buffer1); + parcBuffer_PutUint64(data.truth, x); + parcBuffer_PutArray(data.truth, buffer2_length, buffer2); + parcBuffer_Flip(data.truth); + + return data; +} + +static void +_destroyData(SetLimitData data) +{ + ccnxCodecNetworkBuffer_Release(&data.netbuff); + parcBuffer_Release(&data.truth); +} + +static void +_runDataTest(size_t position) +{ + SetLimitData data = _allocateData(); + ccnxCodecNetworkBuffer_SetPosition(data.netbuff, position); + parcBuffer_SetLimit(data.truth, position); + + ccnxCodecNetworkBuffer_Finalize(data.netbuff); + PARCBuffer *test = ccnxCodecNetworkBuffer_CreateParcBuffer(data.netbuff); + assertTrue(parcBuffer_Equals(data.truth, test), "wrong value") + { + printf("Expected\n"); + parcBuffer_Display(data.truth, 3); + printf("Got\n"); + parcBuffer_Display(test, 3); + } + + parcBuffer_Release(&test); + _destroyData(data); +} + +/* + * In this test, SetLimit is called when we are at position 4077 + */ +LONGBOW_TEST_CASE(SetLimit, ccnxCodecNetworkBuffer_Finalize_EndOfTail) +{ + _runDataTest(4077); +} + +/* + * In this test, SetLimit is called when we are at position 4000, which + * is in the middle of 'block 2' + */ +LONGBOW_TEST_CASE(SetLimit, ccnxCodecNetworkBuffer_Finalize_MidOfTail) +{ + _runDataTest(4000); +} + +/* + * In this test, SetLimit is called when we are at position 3577, which + * is in the start of 'block 2' + */ +LONGBOW_TEST_CASE(SetLimit, ccnxCodecNetworkBuffer_Finalize_StartOfTail) +{ + _runDataTest(3577); +} + +/* + * In this test, SetLimit is called when we are at position 3576, which + * is the last byte of 'block 1' + */ +LONGBOW_TEST_CASE(SetLimit, ccnxCodecNetworkBuffer_Finalize_EndOfMid) +{ + _runDataTest(3576); +} + +/* + * In this test, SetLimit is called when we are at position 2000, which + * is the middle of 'block 1' + */ +LONGBOW_TEST_CASE(SetLimit, ccnxCodecNetworkBuffer_Finalize_MidOfMid) +{ + _runDataTest(2000); +} + +/* + * 1536 is 1st byte of 'block 1' + */ +LONGBOW_TEST_CASE(SetLimit, ccnxCodecNetworkBuffer_Finalize_StartOfMid) +{ + _runDataTest(1536); +} + +/* + * Wipe it all out + */ +LONGBOW_TEST_CASE(SetLimit, ccnxCodecNetworkBuffer_Finalize_Zero) +{ + _runDataTest(0); +} + + +// ========================================================================= + +LONGBOW_TEST_FIXTURE(Local) +{ + LONGBOW_RUN_TEST_CASE(Local, _ccnxCodecNetworkBufferMemory_Allocate); +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + longBowTestCase_SetClipBoardData(testCase, _commonSetup()); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + _commonTeardown(longBowTestCase_GetClipBoardData(testCase)); + + if (parcSafeMemory_ReportAllocation(STDOUT_FILENO) != 0) { + printf("('%s' leaks memory by %d (allocs - frees)) ", longBowTestCase_GetName(testCase), parcMemory_Outstanding()); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Local, _ccnxCodecNetworkBufferMemory_Allocate) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + size_t desired = 2048; + CCNxCodecNetworkBufferMemory *memory = _ccnxCodecNetworkBufferMemory_Allocate(data->buffer, desired); + assertNotNull(memory, "Got null memory"); + assertNull(memory->next, "memory->next is not null"); + assertTrue(memory->begin == 0, "memory has wrong offset, got %zu expecting %u", memory->begin, 0); + assertTrue(memory->capacity == desired, "Wrong capacity, got %zu expecting %zu", memory->capacity, desired); + + _ccnxCodecNetworkBufferMemory_Release(data->buffer, &memory); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(ccnx_NetworkBuffer); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-common/ccnx/common/codec/test/test_ccnxCodec_TlvDecoder.c b/libccnx-common/ccnx/common/codec/test/test_ccnxCodec_TlvDecoder.c new file mode 100755 index 00000000..db835bb5 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/test/test_ccnxCodec_TlvDecoder.c @@ -0,0 +1,808 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../ccnxCodec_TlvDecoder.c" +#include <parc/algol/parc_SafeMemory.h> +#include <LongBow/unit-test.h> + +#include <inttypes.h> + +LONGBOW_TEST_RUNNER(parc_Tlv) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Decoder); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(parc_Tlv) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(parc_Tlv) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// ============================================ + +LONGBOW_TEST_FIXTURE(Decoder) +{ + LONGBOW_RUN_TEST_CASE(Decoder, ccnxCodecTlvDecoder_Create); + LONGBOW_RUN_TEST_CASE(Decoder, ccnxCodecTlvDecoder_GetLength); + LONGBOW_RUN_TEST_CASE(Decoder, ccnxCodecTlvDecoder_GetType); + LONGBOW_RUN_TEST_CASE(Decoder, ccnxCodecTlvDecoder_PeekType); + LONGBOW_RUN_TEST_CASE(Decoder, ccnxCodecTlvDecoder_GetValue); + LONGBOW_RUN_TEST_CASE(Decoder, ccnxCodecTlvDecoder_GetValue_TooLong); + LONGBOW_RUN_TEST_CASE(Decoder, ccnxCodecTlvDecoder_GetContainer); + LONGBOW_RUN_TEST_CASE(Decoder, ccnxCodecTlvDecoder_GetContainer_TooLong); + + LONGBOW_RUN_TEST_CASE(Decoder, ccnxCodecTlvDecoder_IsEmpty_True); + LONGBOW_RUN_TEST_CASE(Decoder, ccnxCodecTlvDecoder_IsEmpty_False); + LONGBOW_RUN_TEST_CASE(Decoder, ccnxCodecTlvDecoder_Position); + LONGBOW_RUN_TEST_CASE(Decoder, ccnxCodecTlvDecoder_EnsureRemaining_True); + LONGBOW_RUN_TEST_CASE(Decoder, ccnxCodecTlvDecoder_EnsureRemaining_False); + + LONGBOW_RUN_TEST_CASE(Decoder, ccnxCodecTlvDecoder_GetUint8_Good); + LONGBOW_RUN_TEST_CASE(Decoder, ccnxCodecTlvDecoder_GetUint8_Short); + LONGBOW_RUN_TEST_CASE(Decoder, ccnxCodecTlvDecoder_GetUint8_WrongType); + LONGBOW_RUN_TEST_CASE(Decoder, ccnxCodecTlvDecoder_GetUint8_WrongLength); + + LONGBOW_RUN_TEST_CASE(Decoder, ccnxCodecTlvDecoder_GetUint16_Good); + LONGBOW_RUN_TEST_CASE(Decoder, ccnxCodecTlvDecoder_GetUint16_Short); + LONGBOW_RUN_TEST_CASE(Decoder, ccnxCodecTlvDecoder_GetUint16_WrongType); + LONGBOW_RUN_TEST_CASE(Decoder, ccnxCodecTlvDecoder_GetUint16_WrongLength); + + LONGBOW_RUN_TEST_CASE(Decoder, ccnxCodecTlvDecoder_GetUint32_Good); + LONGBOW_RUN_TEST_CASE(Decoder, ccnxCodecTlvDecoder_GetUint32_Short); + LONGBOW_RUN_TEST_CASE(Decoder, ccnxCodecTlvDecoder_GetUint32_WrongType); + LONGBOW_RUN_TEST_CASE(Decoder, ccnxCodecTlvDecoder_GetUint32_WrongLength); + + LONGBOW_RUN_TEST_CASE(Decoder, ccnxCodecTlvDecoder_GetUint64_Good); + LONGBOW_RUN_TEST_CASE(Decoder, ccnxCodecTlvDecoder_GetUint64_Short); + LONGBOW_RUN_TEST_CASE(Decoder, ccnxCodecTlvDecoder_GetUint64_WrongType); + LONGBOW_RUN_TEST_CASE(Decoder, ccnxCodecTlvDecoder_GetUint64_WrongLength); + + LONGBOW_RUN_TEST_CASE(Decoder, ccnxCodecTlvDecoder_GetBuffer_Good); + LONGBOW_RUN_TEST_CASE(Decoder, ccnxCodecTlvDecoder_GetBuffer_WrongType); + LONGBOW_RUN_TEST_CASE(Decoder, ccnxCodecTlvDecoder_GetBuffer_TooShort); + LONGBOW_RUN_TEST_CASE(Decoder, ccnxCodecTlvDecoder_GetBuffer_TooLong); + + LONGBOW_RUN_TEST_CASE(Decoder, ccnxCodecTlvDecoder_Advance_Good); + LONGBOW_RUN_TEST_CASE(Decoder, ccnxCodecTlvDecoder_Advance_TooLong); + + LONGBOW_RUN_TEST_CASE(Decoder, ccnxCodecTlvDecoder_GetVarInt); +} + +LONGBOW_TEST_FIXTURE_SETUP(Decoder) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Decoder) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +/** + * check for memory leaks on create/destroy + */ +LONGBOW_TEST_CASE(Decoder, ccnxCodecTlvDecoder_Create) +{ + PARCBuffer *buffer = parcBuffer_Allocate(1); + size_t before = parcMemory_Outstanding(); + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(buffer); + ccnxCodecTlvDecoder_Destroy(&decoder); + size_t after = parcMemory_Outstanding(); + parcBuffer_Release(&buffer); + assertTrue(before == after, "Memory leak, expected %zu got %zu bytes\n", before, after); +} + +LONGBOW_TEST_CASE(Decoder, ccnxCodecTlvDecoder_GetLength) +{ + /** + * We will create a TLV structure that looks like this: + * { T = 1, L = 19 }, + * { T = 2, L = 5, V = "hello" } + * { T = 3, L = 6, V = "mr tlv" } + */ + uint8_t truthBytes[] = { + 0x00, 0x01, 0x00, 0x13, + 0x00, 0x02, 0x00, 0x05,'h', 'e', 'l', 'l', 'o', + 0x00, 0x03, 0x00, 0x06,'m', 'r', ' ', 't', 'l', 'v' + }; + + PARCBuffer *buffer = parcBuffer_Wrap(truthBytes, sizeof(truthBytes), 0, sizeof(truthBytes)); + + CCNxCodecTlvDecoder *outerDecoder = ccnxCodecTlvDecoder_Create(buffer); + parcBuffer_Release(&buffer); + + // we're calling this on byte 0, so the "length" will be 0x0001 + uint16_t length = ccnxCodecTlvDecoder_GetLength(outerDecoder); + + assertTrue(parcBuffer_Position(outerDecoder->buffer) == 2, + "Did not advance buffer to right spot, expected %u got %zu", + 2, parcBuffer_Position(outerDecoder->buffer)); + + assertTrue(length == 1, "Wrong length expected %u got %u", 1, length); + + ccnxCodecTlvDecoder_Destroy(&outerDecoder); +} + +LONGBOW_TEST_CASE(Decoder, ccnxCodecTlvDecoder_GetType) +{ + /** + * We will create a TLV structure that looks like this: + * { T = 1, L = 19 }, + * { T = 2, L = 5, V = "hello" } + * { T = 3, L = 6, V = "mr tlv" } + */ + uint8_t truthBytes[] = { + 0x00, 0x01, 0x00, 0x13, + 0x00, 0x02, 0x00, 0x05,'h', 'e', 'l', 'l', 'o', + 0x00, 0x03, 0x00, 0x06,'m', 'r', ' ', 't', 'l', 'v' + }; + + PARCBuffer *buffer = parcBuffer_Wrap(truthBytes, sizeof(truthBytes), 0, sizeof(truthBytes)); + + CCNxCodecTlvDecoder *outerDecoder = ccnxCodecTlvDecoder_Create(buffer); + parcBuffer_Release(&buffer); + + uint16_t type = ccnxCodecTlvDecoder_GetType(outerDecoder); + + assertTrue(parcBuffer_Position(outerDecoder->buffer) == 2, + "Did not advance buffer to right spot, expected %u got %zu", + 2, parcBuffer_Position(outerDecoder->buffer)); + + assertTrue(type == 1, "Wrong type expected %u got %u", 1, type); + + ccnxCodecTlvDecoder_Destroy(&outerDecoder); +} + +LONGBOW_TEST_CASE(Decoder, ccnxCodecTlvDecoder_PeekType) +{ + /** + * We will create a TLV structure that looks like this: + * { T = 1, L = 19 }, + * { T = 2, L = 5, V = "hello" } + * { T = 3, L = 6, V = "mr tlv" } + */ + uint8_t truthBytes[] = { + 0x00, 0x01, 0x00, 0x13, + 0x00, 0x02, 0x00, 0x05,'h', 'e', 'l', 'l', 'o', + 0x00, 0x03, 0x00, 0x06,'m', 'r', ' ', 't', 'l', 'v' + }; + + PARCBuffer *buffer = parcBuffer_Wrap(truthBytes, sizeof(truthBytes), 0, sizeof(truthBytes)); + + CCNxCodecTlvDecoder *outerDecoder = ccnxCodecTlvDecoder_Create(buffer); + parcBuffer_Release(&buffer); + + uint16_t type = ccnxCodecTlvDecoder_PeekType(outerDecoder); + + assertTrue(parcBuffer_Position(outerDecoder->buffer) == 0, + "Did not advance buffer to right spot, expected %u got %zu", + 0, parcBuffer_Position(outerDecoder->buffer)); + + assertTrue(type == 1, "Wrong type expected %u got %u", 1, type); + + ccnxCodecTlvDecoder_Destroy(&outerDecoder); +} + +LONGBOW_TEST_CASE(Decoder, ccnxCodecTlvDecoder_GetValue) +{ + /** + * We will create a TLV structure that looks like this: + * { T = 1, L = 19 }, + * { T = 2, L = 5, V = "hello" } + * { T = 3, L = 6, V = "mr tlv" } + */ + uint8_t truthBytes[] = { + 0x00, 0x01, 0x00, 0x13, + 0x00, 0x02, 0x00, 0x05,'h', 'e', 'l', 'l', 'o', + 0x00, 0x03, 0x00, 0x06,'m', 'r', ' ', 't', 'l', 'v' + }; + + PARCBuffer *buffer = parcBuffer_Wrap(truthBytes, sizeof(truthBytes), 0, sizeof(truthBytes)); + + CCNxCodecTlvDecoder *outerDecoder = ccnxCodecTlvDecoder_Create(buffer); + parcBuffer_Release(&buffer); + + uint16_t type = ccnxCodecTlvDecoder_GetType(outerDecoder); + uint16_t length = ccnxCodecTlvDecoder_GetLength(outerDecoder); + + assertTrue(type == 1, "Wrong type expected %u got %u", 1, type); + assertTrue(length == 19, "Wrong length expected %u got %u", 19, length); + + PARCBuffer *inner = ccnxCodecTlvDecoder_GetValue(outerDecoder, length); + + // inner should now be empty + assertTrue(ccnxCodecTlvDecoder_IsEmpty(outerDecoder), "outer decoder should be emtpy"); + + CCNxCodecTlvDecoder *innerDecoder = ccnxCodecTlvDecoder_Create(inner); + parcBuffer_Release(&inner); + + type = ccnxCodecTlvDecoder_GetType(innerDecoder); + length = ccnxCodecTlvDecoder_GetLength(innerDecoder); + + assertTrue(type == 2, "Wrong type expected %u got %u", 2, type); + assertTrue(length == 5, "Wrong length expected %u got %u", 5, length); + + PARCBuffer *hello = ccnxCodecTlvDecoder_GetValue(innerDecoder, length); + + parcBuffer_Release(&hello); + ccnxCodecTlvDecoder_Destroy(&innerDecoder); + ccnxCodecTlvDecoder_Destroy(&outerDecoder); +} + +LONGBOW_TEST_CASE(Decoder, ccnxCodecTlvDecoder_GetValue_TooLong) +{ + // Length is beyond end of buffer + uint8_t truthBytes[] = { 0x00, 0x02, 0x00, 0x99, 'h', 'e', 'l', 'l', 'o' }; + + PARCBuffer *buffer = parcBuffer_Wrap(truthBytes, sizeof(truthBytes), 0, sizeof(truthBytes)); + + CCNxCodecTlvDecoder *outerDecoder = ccnxCodecTlvDecoder_Create(buffer); + parcBuffer_Release(&buffer); + + (void) ccnxCodecTlvDecoder_GetType(outerDecoder); + uint16_t length = ccnxCodecTlvDecoder_GetLength(outerDecoder); + PARCBuffer *value = ccnxCodecTlvDecoder_GetValue(outerDecoder, length); + + assertNull(value, "Value should be null because of buffer underrun"); + + ccnxCodecTlvDecoder_Destroy(&outerDecoder); +} + +LONGBOW_TEST_CASE(Decoder, ccnxCodecTlvDecoder_IsEmpty_True) +{ + /** + * We will create a TLV structure that looks like this: + * { T = 1, L = 19 }, + * { T = 2, L = 5, V = "hello" } + * { T = 3, L = 6, V = "mr tlv" } + */ + uint8_t truthBytes[] = { + 0x00, 0x01, 0x00, 0x13, + 0x00, 0x02, 0x00, 0x05,'h', 'e', 'l', 'l', 'o', + 0x00, 0x03, 0x00, 0x06,'m', 'r', ' ', 't', 'l', 'v' + }; + + PARCBuffer *buffer = parcBuffer_Wrap(truthBytes, sizeof(truthBytes), 0, sizeof(truthBytes)); + + CCNxCodecTlvDecoder *outerDecoder = ccnxCodecTlvDecoder_Create(buffer); + parcBuffer_Release(&buffer); + + PARCBuffer *value = ccnxCodecTlvDecoder_GetValue(outerDecoder, sizeof(truthBytes)); + parcBuffer_Release(&value); + + assertTrue(ccnxCodecTlvDecoder_IsEmpty(outerDecoder), "Decoder said it was not empty when its should be empty"); + ccnxCodecTlvDecoder_Destroy(&outerDecoder); +} + +LONGBOW_TEST_CASE(Decoder, ccnxCodecTlvDecoder_IsEmpty_False) +{ + /** + * We will create a TLV structure that looks like this: + * { T = 1, L = 19 }, + * { T = 2, L = 5, V = "hello" } + * { T = 3, L = 6, V = "mr tlv" } + */ + uint8_t truthBytes[] = { + 0x00, 0x01, 0x00, 0x13, + 0x00, 0x02, 0x00, 0x05,'h', 'e', 'l', 'l', 'o', + 0x00, 0x03, 0x00, 0x06,'m', 'r', ' ', 't', 'l', 'v' + }; + + PARCBuffer *buffer = parcBuffer_Wrap(truthBytes, sizeof(truthBytes), 0, sizeof(truthBytes)); + + CCNxCodecTlvDecoder *outerDecoder = ccnxCodecTlvDecoder_Create(buffer); + parcBuffer_Release(&buffer); + + assertFalse(ccnxCodecTlvDecoder_IsEmpty(outerDecoder), "Decoder said it was empty when its full"); + ccnxCodecTlvDecoder_Destroy(&outerDecoder); +} + +LONGBOW_TEST_CASE(Decoder, ccnxCodecTlvDecoder_Position) +{ + /** + * We will create a TLV structure that looks like this: + * { T = 1, L = 19 }, + * { T = 2, L = 5, V = "hello" } + * { T = 3, L = 6, V = "mr tlv" } + */ + uint8_t truthBytes[] = { + 0x00, 0x01, 0x00, 0x13, + 0x00, 0x02, 0x00, 0x05,'h', 'e', 'l', 'l', 'o', + 0x00, 0x03, 0x00, 0x06,'m', 'r', ' ', 't', 'l', 'v' + }; + + PARCBuffer *buffer = parcBuffer_Wrap(truthBytes, sizeof(truthBytes), 0, sizeof(truthBytes)); + + CCNxCodecTlvDecoder *outerDecoder = ccnxCodecTlvDecoder_Create(buffer); + parcBuffer_Release(&buffer); + + PARCBuffer *value = ccnxCodecTlvDecoder_GetValue(outerDecoder, 8); + parcBuffer_Release(&value); + + assertTrue(ccnxCodecTlvDecoder_Position(outerDecoder) == 8, + "Decoder reports wrong position, expected %u got %zu", + 8, + ccnxCodecTlvDecoder_Position(outerDecoder)); + + ccnxCodecTlvDecoder_Destroy(&outerDecoder); +} + +LONGBOW_TEST_CASE(Decoder, ccnxCodecTlvDecoder_EnsureRemaining_True) +{ + uint8_t truthBytes[] = { + 0x00, 0x01, 0x00, 0x13, + 0x00, 0x02, 0x00, 0x05,'h', 'e', 'l', 'l', 'o', + 0x00, 0x03, 0x00, 0x06,'m', 'r', ' ', 't', 'l', 'v' + }; + + PARCBuffer *buffer = parcBuffer_Wrap(truthBytes, sizeof(truthBytes), 0, sizeof(truthBytes)); + + CCNxCodecTlvDecoder *outerDecoder = ccnxCodecTlvDecoder_Create(buffer); + parcBuffer_Release(&buffer); + + bool success = ccnxCodecTlvDecoder_EnsureRemaining(outerDecoder, 5); + + ccnxCodecTlvDecoder_Destroy(&outerDecoder); + + assertTrue(success, + "Decoder failed ensureRemaining check for 5 bytes when its a 19 byte buffer"); +} + +LONGBOW_TEST_CASE(Decoder, ccnxCodecTlvDecoder_EnsureRemaining_False) +{ + uint8_t truthBytes[] = { + 0x00, 0x01, 0x00, 0x13, + 0x00, 0x02, 0x00, 0x05,'h', 'e', 'l', 'l', 'o', + 0x00, 0x03, 0x00, 0x06,'m', 'r', ' ', 't', 'l', 'v' + }; + + PARCBuffer *buffer = parcBuffer_Wrap(truthBytes, sizeof(truthBytes), 0, sizeof(truthBytes)); + + CCNxCodecTlvDecoder *outerDecoder = ccnxCodecTlvDecoder_Create(buffer); + parcBuffer_Release(&buffer); + + bool success = ccnxCodecTlvDecoder_EnsureRemaining(outerDecoder, 24); + + ccnxCodecTlvDecoder_Destroy(&outerDecoder); + + assertFalse(success, + "Decoder passed ensureRemaining check for 24 bytes when its a 19 byte buffer"); +} + +LONGBOW_TEST_CASE(Decoder, ccnxCodecTlvDecoder_GetUint8_Good) +{ + PARCBuffer *buffer = parcBuffer_Wrap((uint8_t[]) { 0x10, 0x20, 0x00, 0x01, 0xFF }, 5, 0, 5); + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(buffer); + uint8_t value; + bool success = ccnxCodecTlvDecoder_GetUint8(decoder, 0x1020, &value); + assertTrue(success, "Did not decode a correct buffer"); + assertTrue(value == 0xFF, "Incorrect value, expected 0x%X got 0x%X", 0xFF, value); + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Decoder, ccnxCodecTlvDecoder_GetUint8_Short) +{ + // LIMIT IS SHORT + PARCBuffer *buffer = parcBuffer_Wrap((uint8_t[]) { 0x10, 0x20, 0x00, 0x01, 0xFF }, 5, 0, 4); + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(buffer); + uint8_t value; + bool success = ccnxCodecTlvDecoder_GetUint8(decoder, 0x1020, &value); + assertFalse(success, "Should have failed a short buffer"); + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Decoder, ccnxCodecTlvDecoder_GetUint8_WrongType) +{ + // TYPE IS WRONG + PARCBuffer *buffer = parcBuffer_Wrap((uint8_t[]) { 0xFF, 0xFF, 0x00, 0x01, 0xFF }, 5, 0, 5); + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(buffer); + uint8_t value; + bool success = ccnxCodecTlvDecoder_GetUint8(decoder, 0x1020, &value); + assertFalse(success, "Should have failed because of wrong type"); + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Decoder, ccnxCodecTlvDecoder_GetUint8_WrongLength) +{ + // LENGTH TOO BIG + PARCBuffer *buffer = parcBuffer_Wrap((uint8_t[]) { 0x10, 0x20, 0x00, 0x99, 0xFF }, 5, 0, 5); + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(buffer); + uint8_t value; + bool success = ccnxCodecTlvDecoder_GetUint8(decoder, 0x1020, &value); + assertFalse(success, "Should have failed because of incorrect length"); + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Decoder, ccnxCodecTlvDecoder_GetUint16_Good) +{ + PARCBuffer *buffer = parcBuffer_Wrap((uint8_t[]) { 0x10, 0x21, 0x00, 0x02, 0xFF, 0x01 }, 6, 0, 6); + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(buffer); + uint16_t value; + bool success = ccnxCodecTlvDecoder_GetUint16(decoder, 0x1021, &value); + assertTrue(success, "Did not decode a correct buffer"); + assertTrue(value == 0xFF01, "Incorrect value, expected 0x%X got 0x%X", 0xFF01, value); + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Decoder, ccnxCodecTlvDecoder_GetUint16_Short) +{ + // LIMIT IS SHORT + PARCBuffer *buffer = parcBuffer_Wrap((uint8_t[]) { 0x10, 0x21, 0x00, 0x02, 0xFF, 0x01 }, 6, 0, 5); + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(buffer); + uint16_t value; + bool success = ccnxCodecTlvDecoder_GetUint16(decoder, 0x1021, &value); + assertFalse(success, "Should have failed because of wrong type"); + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Decoder, ccnxCodecTlvDecoder_GetUint16_WrongType) +{ + // TYPE IS WRONG + PARCBuffer *buffer = parcBuffer_Wrap((uint8_t[]) { 0xFF, 0xFF, 0x00, 0x02, 0xFF, 0x01 }, 6, 0, 6); + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(buffer); + uint16_t value; + bool success = ccnxCodecTlvDecoder_GetUint16(decoder, 0x1021, &value); + assertFalse(success, "Should have failed because of wrong type"); + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&buffer); +} +LONGBOW_TEST_CASE(Decoder, ccnxCodecTlvDecoder_GetUint16_WrongLength) +{ + // LENGTH TOO BIG + PARCBuffer *buffer = parcBuffer_Wrap((uint8_t[]) { 0x10, 0x21, 0x00, 0x99, 0xFF }, 5, 0, 5); + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(buffer); + uint16_t value; + bool success = ccnxCodecTlvDecoder_GetUint16(decoder, 0x1021, &value); + assertFalse(success, "Should have failed because of incorrect length"); + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Decoder, ccnxCodecTlvDecoder_GetUint32_Good) +{ + PARCBuffer *buffer = parcBuffer_Wrap((uint8_t[]) { 0x10, 0x22, 0x00, 0x04, 0xFF, 0x01, 0x02, 0x03 }, 8, 0, 8); + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(buffer); + uint32_t value; + bool success = ccnxCodecTlvDecoder_GetUint32(decoder, 0x1022, &value); + assertTrue(success, "Did not decode a correct buffer"); + assertTrue(value == 0xFF010203, "Incorrect value, expected 0x%X got 0x%X", 0xFF010203, value); + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Decoder, ccnxCodecTlvDecoder_GetUint32_Short) +{ + // LIMIT IS SHORT + PARCBuffer *buffer = parcBuffer_Wrap((uint8_t[]) { 0x10, 0x22, 0x00, 0x04, 0xFF, 0x01, 0x02, 0x03 }, 8, 0, 7); + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(buffer); + uint32_t value; + bool success = ccnxCodecTlvDecoder_GetUint32(decoder, 0x1022, &value); + assertFalse(success, "Should have failed because of wrong type"); + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Decoder, ccnxCodecTlvDecoder_GetUint32_WrongType) +{ + // TYPE IS WRONG + PARCBuffer *buffer = parcBuffer_Wrap((uint8_t[]) { 0xFF, 0xFF, 0x00, 0x04, 0xFF, 0x01, 0x02, 0x03 }, 8, 0, 8); + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(buffer); + uint32_t value; + bool success = ccnxCodecTlvDecoder_GetUint32(decoder, 0x1022, &value); + assertFalse(success, "Should have failed because of wrong type"); + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Decoder, ccnxCodecTlvDecoder_GetUint32_WrongLength) +{ + // LENGTH TOO BIG + PARCBuffer *buffer = parcBuffer_Wrap((uint8_t[]) { 0x10, 0x22, 0x00, 0x99, 0xFF, 0x01, 0x02, 0x03 }, 8, 0, 8); + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(buffer); + uint32_t value; + bool success = ccnxCodecTlvDecoder_GetUint32(decoder, 0x1022, &value); + assertFalse(success, "Should have failed because of incorrect length"); + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Decoder, ccnxCodecTlvDecoder_GetUint64_Good) +{ + PARCBuffer *buffer = parcBuffer_Wrap((uint8_t[]) { 0x10, 0x23, 0x00, 0x08, 0xFF, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 }, 12, 0, 12); + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(buffer); + uint64_t value; + uint64_t truth = 0xFF01020304050607ULL; + bool success = ccnxCodecTlvDecoder_GetUint64(decoder, 0x1023, &value); + assertTrue(success, "Did not decode a correct buffer"); + assertTrue(value == truth, "Incorrect value, expected %#" PRIx64 " got %#" PRIx64, truth, value); + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Decoder, ccnxCodecTlvDecoder_GetUint64_Short) +{ + // LIMIT IS SHORT + PARCBuffer *buffer = parcBuffer_Wrap((uint8_t[]) { 0x10, 0x23, 0x00, 0x08, 0xFF, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 }, 12, 0, 11); + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(buffer); + uint64_t value; + bool success = ccnxCodecTlvDecoder_GetUint64(decoder, 0x1023, &value); + assertFalse(success, "Should have failed because of wrong type"); + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Decoder, ccnxCodecTlvDecoder_GetUint64_WrongType) +{ + // TYPE IS WRONG + PARCBuffer *buffer = parcBuffer_Wrap((uint8_t[]) { 0xFF, 0xFF, 0x00, 0x08, 0xFF, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 }, 12, 0, 11); + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(buffer); + uint64_t value; + bool success = ccnxCodecTlvDecoder_GetUint64(decoder, 0x1023, &value); + assertFalse(success, "Should have failed because of wrong type"); + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Decoder, ccnxCodecTlvDecoder_GetUint64_WrongLength) +{ + // LENGTH TOO BIG + PARCBuffer *buffer = parcBuffer_Wrap((uint8_t[]) { 0x10, 0x23, 0x00, 0x99, 0xFF, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 }, 12, 0, 12); + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(buffer); + uint64_t value; + bool success = ccnxCodecTlvDecoder_GetUint64(decoder, 0x1023, &value); + assertFalse(success, "Should have failed because of incorrect length"); + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Decoder, ccnxCodecTlvDecoder_GetBuffer_Good) +{ + PARCBuffer *truth = parcBuffer_Wrap((uint8_t[]) { 0x01, 0x02, 0x03, 0x04 }, 4, 0, 4); + PARCBuffer *input = parcBuffer_Wrap((uint8_t[]) { 0x00, 0x01, 0x00, 0x08, 0xAA, 0xBB, 0x00, 0x04, 0x01, 0x02, 0x03, 0x04 }, 12, 0, 12); + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(input); + + (void) ccnxCodecTlvDecoder_GetType(decoder); + (void) ccnxCodecTlvDecoder_GetLength(decoder); + + PARCBuffer *test = ccnxCodecTlvDecoder_GetBuffer(decoder, 0xAABB); + if (!parcBuffer_Equals(truth, test)) { + printf("Buffers not equal\n"); + printf("Expected\n"); + parcBuffer_Display(truth, 3); + printf("Got\n"); + parcBuffer_Display(test, 3); + assertTrue(parcBuffer_Equals(truth, test), "Buffers not equal"); + } + + parcBuffer_Release(&test); + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&truth); + parcBuffer_Release(&input); +} + +LONGBOW_TEST_CASE(Decoder, ccnxCodecTlvDecoder_GetBuffer_WrongType) +{ + // INNER TYPE IS WRONG + PARCBuffer *input = parcBuffer_Wrap((uint8_t[]) { 0x00, 0x01, 0x00, 0x08, 0xFF, 0xFF, 0x00, 0x04, 0x01, 0x02, 0x03, 0x04 }, 12, 0, 12); + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(input); + + (void) ccnxCodecTlvDecoder_GetType(decoder); + (void) ccnxCodecTlvDecoder_GetLength(decoder); + + PARCBuffer *test = ccnxCodecTlvDecoder_GetBuffer(decoder, 0xAABB); + assertNull(test, "Should have returned NULL because of incorrect TLV type"); + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&input); +} + +LONGBOW_TEST_CASE(Decoder, ccnxCodecTlvDecoder_GetBuffer_TooShort) +{ + // OVERALL LENGTH TOO SHORT TO PARSE + // buffer only goes to here ---------------------------------! + PARCBuffer *input = parcBuffer_Wrap((uint8_t[]) { 0x00, 0x01, 0x00, 0x08, 0xAA, 0xBB, 0x00, 0x04, 0x01, 0x02, 0x03, 0x04 }, 12, 0, 6); + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(input); + + (void) ccnxCodecTlvDecoder_GetType(decoder); + (void) ccnxCodecTlvDecoder_GetLength(decoder); + + PARCBuffer *test = ccnxCodecTlvDecoder_GetBuffer(decoder, 0xAABB); + assertNull(test, "Should have returned NULL because of input underrun"); + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&input); +} + +LONGBOW_TEST_CASE(Decoder, ccnxCodecTlvDecoder_GetBuffer_TooLong) +{ + // VALUE (4 bytes) SHORTER THAN LENGTH (0x99) + PARCBuffer *input = parcBuffer_Wrap((uint8_t[]) { 0x00, 0x01, 0x00, 0x08, 0xAA, 0xBB, 0x00, 0x99, 0x01, 0x02, 0x03, 0x04 }, 12, 0, 12); + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(input); + + (void) ccnxCodecTlvDecoder_GetType(decoder); + (void) ccnxCodecTlvDecoder_GetLength(decoder); + + PARCBuffer *test = ccnxCodecTlvDecoder_GetBuffer(decoder, 0xAABB); + assertNull(test, "Should have returned NULL because of value underrun"); + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&input); +} + +LONGBOW_TEST_CASE(Decoder, ccnxCodecTlvDecoder_GetContainer) +{ + /** + * We will create a TLV structure that looks like this: + * { T = 1, L = 19 }, + * { T = 2, L = 5, V = "hello" } + * { T = 3, L = 6, V = "mr tlv" } + */ + uint8_t truthBytes[] = { + 0x00, 0x01, 0x00, 0x13, + 0x00, 0x02, 0x00, 0x05,'h', 'e', 'l', 'l', 'o', + 0x00, 0x03, 0x00, 0x06,'m', 'r', ' ', 't', 'l', 'v' + }; + + PARCBuffer *buffer = parcBuffer_Wrap(truthBytes, sizeof(truthBytes), 0, sizeof(truthBytes)); + + CCNxCodecTlvDecoder *outerDecoder = ccnxCodecTlvDecoder_Create(buffer); + parcBuffer_Release(&buffer); + + (void) ccnxCodecTlvDecoder_GetType(outerDecoder); + uint16_t length = ccnxCodecTlvDecoder_GetLength(outerDecoder); + CCNxCodecTlvDecoder *innerDecoder = ccnxCodecTlvDecoder_GetContainer(outerDecoder, length); + assertNotNull(innerDecoder, "Got a null decoder for a valid slice"); + assertTrue(ccnxCodecTlvDecoder_Position(innerDecoder) == 0, "Wrong position, expected 0 got %zu", ccnxCodecTlvDecoder_Position(innerDecoder)); + assertTrue(ccnxCodecTlvDecoder_EnsureRemaining(innerDecoder, 19), "Inner decoder does not have enough bytes in it"); + + ccnxCodecTlvDecoder_Destroy(&innerDecoder); + ccnxCodecTlvDecoder_Destroy(&outerDecoder); +} + +LONGBOW_TEST_CASE(Decoder, ccnxCodecTlvDecoder_GetContainer_TooLong) +{ + /** + * We will create a TLV structure that looks like this: + * { T = 1, L = 19 }, + * { T = 2, L = 5, V = "hello" } + * { T = 3, L = 6, V = "mr tlv" } + */ + uint8_t truthBytes[] = { + 0x00, 0x01, 0x00, 0x13, + 0x00, 0x02, 0x00, 0x05,'h', 'e', 'l', 'l', 'o', + 0x00, 0x03, 0x00, 0x06,'m', 'r', ' ', 't', 'l', 'v' + }; + + PARCBuffer *buffer = parcBuffer_Wrap(truthBytes, sizeof(truthBytes), 0, sizeof(truthBytes)); + + CCNxCodecTlvDecoder *outerDecoder = ccnxCodecTlvDecoder_Create(buffer); + parcBuffer_Release(&buffer); + + (void) ccnxCodecTlvDecoder_GetType(outerDecoder); + (void) ccnxCodecTlvDecoder_GetLength(outerDecoder); + // ask for too many bytes + CCNxCodecTlvDecoder *innerDecoder = ccnxCodecTlvDecoder_GetContainer(outerDecoder, 100); + assertNull(innerDecoder, "Got a decoder for an invalid slice"); + ccnxCodecTlvDecoder_Destroy(&outerDecoder); +} + +LONGBOW_TEST_CASE(Decoder, ccnxCodecTlvDecoder_Advance_Good) +{ + PARCBuffer *buffer = parcBuffer_Wrap((uint8_t[]) { 0xFF, 0xFF, 0x00, 0x04, 0xFF, 0x01, 0x02, 0x03 }, 8, 0, 8); + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(buffer); + + size_t advance = 3; + size_t beforePosition = ccnxCodecTlvDecoder_Position(decoder); + bool success = ccnxCodecTlvDecoder_Advance(decoder, advance); + size_t afterPosition = ccnxCodecTlvDecoder_Position(decoder); + + assertTrue(success, "Failed to advance decoder"); + assertTrue(beforePosition + advance == afterPosition, "Wrong position, got %zu expected %zu", afterPosition, beforePosition + advance); + + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Decoder, ccnxCodecTlvDecoder_Advance_TooLong) +{ + PARCBuffer *buffer = parcBuffer_Wrap((uint8_t[]) { 0xFF, 0xFF, 0x00, 0x04, 0xFF, 0x01, 0x02, 0x03 }, 8, 0, 8); + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(buffer); + + size_t advance = 9; + size_t beforePosition = ccnxCodecTlvDecoder_Position(decoder); + bool success = ccnxCodecTlvDecoder_Advance(decoder, advance); + size_t afterPosition = ccnxCodecTlvDecoder_Position(decoder); + + assertFalse(success, "Should have returned false advancing beyond end of decoder"); + assertTrue(beforePosition == afterPosition, "Wrong position, got %zu expected %zu", afterPosition, beforePosition); + + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Decoder, ccnxCodecTlvDecoder_GetVarInt) +{ + struct test_vector { + uint64_t value; + bool valid; + int length; + uint8_t *array; + } vectors[] = { + // length 0 invalid + { .value = 0, .valid = false, .length = 0, .array = (uint8_t[]) { 0x00 } }, + { .value = 0, .valid = true, .length = 1, .array = (uint8_t[]) { 0x00 } }, + { .value = 0xFF, .valid = true, .length = 1, .array = (uint8_t[]) { 0xFF } }, + { .value = 0x0001, .valid = true, .length = 2, .array = (uint8_t[]) { 0x00, 0x01} }, + { .value = 0xFF01, .valid = true, .length = 2, .array = (uint8_t[]) { 0xFF, 0x01} }, + { .value = 0x000001, .valid = true, .length = 3, .array = (uint8_t[]) { 0x00, 0x00, 0x01} }, + { .value = 0xFF0001, .valid = true, .length = 3, .array = (uint8_t[]) { 0xFF, 0x00, 0x01} }, + { .value = 0x00000001, .valid = true, .length = 4, .array = (uint8_t[]) { 0x00, 0x00, 0x00, 0x01} }, + { .value = 0xFF002001, .valid = true, .length = 4, .array = (uint8_t[]) { 0xFF, 0x00, 0x20, 0x01} }, + { .value = 0xFF00200103040506ULL, .valid = true, .length = 8, .array = (uint8_t[]) { 0xFF, 0x00, 0x20, 0x01, 0x03, 0x04, 0x05, 0x06} }, + // length 9 invalid + { .value = 0, .valid = false, .length = 9, .array = (uint8_t[]) { 0xFF, 0x00, 0x20, 0x01, 0x03, 0x04, 0x05, 0x06, 0x07} }, + // sentinal is NULL array + { .value = 0, .valid = false, .length = 0, .array = NULL }, + }; + + for (int i = 0; vectors[i].array != NULL; i++) { + PARCBuffer *buffer = parcBuffer_Wrap(vectors[i].array, vectors[i].length, 0, vectors[i].length); + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(buffer); + + uint64_t value; + bool success = ccnxCodecTlvDecoder_GetVarInt(decoder, vectors[i].length, &value); + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&buffer); + + assertTrue(success == vectors[i].valid, "index %d: Wrong return, got %d expected %d", i, success, vectors[i].valid); + if (vectors[i].valid) { + assertTrue(value == vectors[i].value, "index %d: wrong value: got %" PRIu64 " expected %" PRIu64, i, value, vectors[i].value); + } + } +} + +// ============================================ + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parc_Tlv); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-common/ccnx/common/codec/test/test_ccnxCodec_TlvEncoder.c b/libccnx-common/ccnx/common/codec/test/test_ccnxCodec_TlvEncoder.c new file mode 100755 index 00000000..0898077d --- /dev/null +++ b/libccnx-common/ccnx/common/codec/test/test_ccnxCodec_TlvEncoder.c @@ -0,0 +1,874 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../ccnxCodec_TlvEncoder.c" +#include <parc/algol/parc_SafeMemory.h> +#include <LongBow/unit-test.h> + +#include <ccnx/common/validation/ccnxValidation_CRC32C.h> + +LONGBOW_TEST_RUNNER(parc_Tlv) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Encoder); + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(parc_Tlv) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(parc_Tlv) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// ========================================== + +LONGBOW_TEST_FIXTURE(Encoder) +{ + LONGBOW_RUN_TEST_CASE(Encoder, ccnxCodecTlvEncoder_AppendArray); + LONGBOW_RUN_TEST_CASE(Encoder, ccnxCodecTlvEncoder_AppendRawArray); + LONGBOW_RUN_TEST_CASE(Encoder, ccnxCodecTlvEncoder_AppendBuffer); + LONGBOW_RUN_TEST_CASE(Encoder, ccnxCodecTlvEncoder_AppendBuffer_TestReturn); + LONGBOW_RUN_TEST_CASE(Encoder, ccnxCodecTlvEncoder_AppendContainer); + LONGBOW_RUN_TEST_CASE(Encoder, ccnxCodecTlvEncoder_AppendUint8); + LONGBOW_RUN_TEST_CASE(Encoder, ccnxCodecTlvEncoder_AppendUint16); + LONGBOW_RUN_TEST_CASE(Encoder, ccnxCodecTlvEncoder_AppendUint32); + LONGBOW_RUN_TEST_CASE(Encoder, ccnxCodecTlvEncoder_AppendUint64); + LONGBOW_RUN_TEST_CASE(Encoder, ccnxCodecTlvEncoder_AppendVarInt); + LONGBOW_RUN_TEST_CASE(Encoder, ccnxCodecTlvEncoder_PutUint8); + LONGBOW_RUN_TEST_CASE(Encoder, ccnxCodecTlvEncoder_PutUint16); + + LONGBOW_RUN_TEST_CASE(Encoder, ccnxCodecTlvEncoder_Create); + LONGBOW_RUN_TEST_CASE(Encoder, ccnxCodecTlvEncoder_Finalize); + LONGBOW_RUN_TEST_CASE(Encoder, ccnxCodecTlvEncoder_Finalize_TrimLimit_Buffer); + LONGBOW_RUN_TEST_CASE(Encoder, ccnxCodecTlvEncoder_Finalize_TrimLimit_IoVec); + LONGBOW_RUN_TEST_CASE(Encoder, ccnxCodecTlvEncoder_Initialize); + LONGBOW_RUN_TEST_CASE(Encoder, ccnxCodecTlvEncoder_Initialize_Twice); + LONGBOW_RUN_TEST_CASE(Encoder, ccnxCodecTlvEncoder_Position); + LONGBOW_RUN_TEST_CASE(Encoder, ccnxCodecTlvEncoder_SetContainerLength); + LONGBOW_RUN_TEST_CASE(Encoder, ccnxCodecTlvEncoder_SetPosition); + + LONGBOW_RUN_TEST_CASE(Encoder, ccnxCodecTlvEncoder_SetError_Present); + LONGBOW_RUN_TEST_CASE(Encoder, ccnxCodecTlvEncoder_SetError_Missing); + LONGBOW_RUN_TEST_CASE(Encoder, ccnxCodecTlvEncoder_GetError); + LONGBOW_RUN_TEST_CASE(Encoder, ccnxCodecTlvEncoder_ClearError_Present); + LONGBOW_RUN_TEST_CASE(Encoder, ccnxCodecTlvEncoder_ClearError_Missing); + + LONGBOW_RUN_TEST_CASE(Encoder, ccnxCodecTlvEncoder_MarkSignatureEnd); + LONGBOW_RUN_TEST_CASE(Encoder, ccnxCodecTlvEncoder_MarkSignatureStart); + LONGBOW_RUN_TEST_CASE(Encoder, ccnxCodecTlvEncoder_ComputeSignature); + LONGBOW_RUN_TEST_CASE(Encoder, ccnxCodecTlvEncoder_GetSigner); +} + +LONGBOW_TEST_FIXTURE_SETUP(Encoder) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Encoder) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +/** + * We will create a TLV structure that looks like this: + * { T = 1, L = 19 }, + * { T = 2, L = 5, V = "hello" } + * { T = 3, L = 6, V = "mr tlv" } + */ +LONGBOW_TEST_CASE(Encoder, ccnxCodecTlvEncoder_AppendArray) +{ + uint8_t truthBytes[] = { 0x00, 0x01, 0x00, 0x13, + 0x00, 0x02, 0x00, 0x05,'h', 'e', 'l', 'l', 'o', + 0x00, 0x03, 0x00, 0x06,'m', 'r', ' ', 't', 'l', 'v' }; + + PARCBuffer *truth = parcBuffer_Wrap(truthBytes, sizeof(truthBytes), 0, sizeof(truthBytes)); + + uint8_t helloString[] = "hello"; + uint8_t mrTlvString[] = "mr tlv"; + + CCNxCodecTlvEncoder *innerEncoder = ccnxCodecTlvEncoder_Create(); + + // sizeof -1 to account for null byte at end of string + ccnxCodecTlvEncoder_AppendArray(innerEncoder, 2, sizeof(helloString) - 1, helloString); + ccnxCodecTlvEncoder_AppendArray(innerEncoder, 3, sizeof(mrTlvString) - 1, mrTlvString); + + ccnxCodecTlvEncoder_Finalize(innerEncoder); + PARCBuffer *inner = ccnxCodecTlvEncoder_CreateBuffer(innerEncoder); + + CCNxCodecTlvEncoder *outerEncoder = ccnxCodecTlvEncoder_Create(); + + ccnxCodecTlvEncoder_AppendBuffer(outerEncoder, 1, inner); + ccnxCodecTlvEncoder_Finalize(outerEncoder); + PARCBuffer *container = ccnxCodecTlvEncoder_CreateBuffer(outerEncoder); + + parcBuffer_Release(&inner); + + ccnxCodecTlvEncoder_Destroy(&innerEncoder); + ccnxCodecTlvEncoder_Destroy(&outerEncoder); + + // parcBuffer_ToString() will not work well as we have 0 bytes, use parcBuffer_ToHexString() case 1017 + assertTrue(parcBuffer_Equals(truth, container), "Buffer mismatch") + { + printf("Expected\n"); + parcBuffer_Display(truth, 3); + printf("Got\n"); + parcBuffer_Display(container, 3); + } + + parcBuffer_Release(&truth); + parcBuffer_Release(&container); +} + +LONGBOW_TEST_CASE(Encoder, ccnxCodecTlvEncoder_AppendRawArray) +{ + uint8_t truthBytes[] = { 0x00, 0x01, 0x00, 0x13, + 0x00, 0x02, 0x00, 0x05,'h', 'e', 'l', 'l', 'o', + 0x00, 0x03, 0x00, 0x06,'m', 'r', ' ', 't', 'l', 'v' }; + + PARCBuffer *truth = parcBuffer_Wrap(truthBytes, sizeof(truthBytes), 0, sizeof(truthBytes)); + + uint8_t helloString[] = "hello"; + uint8_t mrTlvString[] = "mr tlv"; + + CCNxCodecTlvEncoder *innerEncoder = ccnxCodecTlvEncoder_Create(); + + // sizeof -1 to account for null byte at end of string + ccnxCodecTlvEncoder_AppendContainer(innerEncoder, 2, sizeof(helloString) - 1); + ccnxCodecTlvEncoder_AppendRawArray(innerEncoder, sizeof(helloString) - 1, helloString); + ccnxCodecTlvEncoder_AppendContainer(innerEncoder, 3, sizeof(mrTlvString) - 1); + ccnxCodecTlvEncoder_AppendRawArray(innerEncoder, sizeof(mrTlvString) - 1, mrTlvString); + + ccnxCodecTlvEncoder_Finalize(innerEncoder); + PARCBuffer *inner = ccnxCodecTlvEncoder_CreateBuffer(innerEncoder); + + CCNxCodecTlvEncoder *outerEncoder = ccnxCodecTlvEncoder_Create(); + + ccnxCodecTlvEncoder_AppendBuffer(outerEncoder, 1, inner); + ccnxCodecTlvEncoder_Finalize(outerEncoder); + PARCBuffer *container = ccnxCodecTlvEncoder_CreateBuffer(outerEncoder); + + parcBuffer_Release(&inner); + + ccnxCodecTlvEncoder_Destroy(&innerEncoder); + ccnxCodecTlvEncoder_Destroy(&outerEncoder); + + // parcBuffer_ToString() will not work well as we have 0 bytes, use parcBuffer_ToHexString() case 1017 + assertTrue(parcBuffer_Equals(truth, container), "Buffer mismatch") + { + printf("Expected\n"); + parcBuffer_Display(truth, 3); + printf("Got\n"); + parcBuffer_Display(container, 3); + } + + parcBuffer_Release(&truth); + parcBuffer_Release(&container); +} + + +/** + * We will create a TLV structure that looks like this: + * { T = 1, L = 19 }, + * { T = 2, L = 5, V = "hello" } + * { T = 3, L = 6, V = "mr tlv" } + */ +LONGBOW_TEST_CASE(Encoder, ccnxCodecTlvEncoder_AppendBuffer) +{ + uint8_t truthBytes[] = { 0x00, 0x01, 0x00, 0x13, + 0x00, 0x02, 0x00, 0x05,'h', 'e', 'l', 'l', 'o', + 0x00, 0x03, 0x00, 0x06,'m', 'r', ' ', 't', 'l', 'v' }; + + PARCBuffer *truth = parcBuffer_Wrap(truthBytes, sizeof(truthBytes), 0, sizeof(truthBytes)); + + uint8_t helloString[] = "hello"; + PARCBuffer *hello = parcBuffer_Wrap(helloString, 5, 0, 5); + + uint8_t mrTlvString[] = "mr tlv"; + PARCBuffer *mrTlv = parcBuffer_Wrap(mrTlvString, 6, 0, 6); + + CCNxCodecTlvEncoder *innerEncoder = ccnxCodecTlvEncoder_Create(); + + ccnxCodecTlvEncoder_AppendBuffer(innerEncoder, 2, hello); + ccnxCodecTlvEncoder_AppendBuffer(innerEncoder, 3, mrTlv); + + ccnxCodecTlvEncoder_Finalize(innerEncoder); + PARCBuffer *inner = ccnxCodecTlvEncoder_CreateBuffer(innerEncoder); + + CCNxCodecTlvEncoder *outerEncoder = ccnxCodecTlvEncoder_Create(); + + ccnxCodecTlvEncoder_AppendBuffer(outerEncoder, 1, inner); + ccnxCodecTlvEncoder_Finalize(outerEncoder); + PARCBuffer *container = ccnxCodecTlvEncoder_CreateBuffer(outerEncoder); + + parcBuffer_Release(&inner); + parcBuffer_Release(&hello); + parcBuffer_Release(&mrTlv); + + ccnxCodecTlvEncoder_Destroy(&innerEncoder); + ccnxCodecTlvEncoder_Destroy(&outerEncoder); + + // parcBuffer_ToString() will not work well as we have 0 bytes, use parcBuffer_ToHexString() case 1017 + assertTrue(parcBuffer_Equals(truth, container), + "buffers not equal\nexpected '%s'\ngot '%s'\n", + parcBuffer_ToString(truth), + parcBuffer_ToString(container) + ); + + parcBuffer_Release(&truth); + parcBuffer_Release(&container); +} + +LONGBOW_TEST_CASE(Encoder, ccnxCodecTlvEncoder_AppendBuffer_TestReturn) +{ + uint8_t helloString[] = "hello"; + PARCBuffer *hello = parcBuffer_Wrap(helloString, 5, 0, 5); + + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + ccnxCodecTlvEncoder_Initialize(encoder); + + size_t trueLength = 2 + 2 + 5; + size_t length = ccnxCodecTlvEncoder_AppendBuffer(encoder, 2, hello); + assertTrue(length == trueLength, "AppendBuffer returned wrong length, expected %zu got %zu", trueLength, length); + + ccnxCodecTlvEncoder_Destroy(&encoder); + parcBuffer_Release(&hello); +} + +LONGBOW_TEST_CASE(Encoder, ccnxCodecTlvEncoder_AppendContainer) +{ + uint8_t truthString[] = { 0x00, 0x02, 0xF1, 0x07 }; + PARCBuffer *truth = parcBuffer_Wrap(truthString, 4, 0, 4); + + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + ccnxCodecTlvEncoder_Initialize(encoder); + + size_t trueLength = 2 + 2; + size_t length = ccnxCodecTlvEncoder_AppendContainer(encoder, 2, 0xF107); + assertTrue(length == trueLength, "AppendBuffer returned wrong length, expected %zu got %zu", trueLength, length); + + ccnxCodecTlvEncoder_Finalize(encoder); + PARCBuffer *test = ccnxCodecTlvEncoder_CreateBuffer(encoder); + + assertTrue(parcBuffer_Equals(test, truth), "Buffer is incorrect."); + + ccnxCodecTlvEncoder_Destroy(&encoder); + parcBuffer_Release(&truth); + parcBuffer_Release(&test); +} + +/** + * Check for memory leaks and correct isInitialized state + */ +LONGBOW_TEST_CASE(Encoder, ccnxCodecTlvEncoder_Create) +{ + size_t before = parcMemory_Outstanding(); + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + + // add a signer to make sure it is destroyed + PARCSigner *signer = ccnxValidationCRC32C_CreateSigner(); + ccnxCodecTlvEncoder_SetSigner(encoder, signer); + parcSigner_Release(&signer); + + // add an error to make sure its destroyed too + CCNxCodecError *error = ccnxCodecError_Create(TLV_ERR_DECODE, "foo", 1, 1); + ccnxCodecTlvEncoder_SetError(encoder, error); + ccnxCodecError_Release(&error); + + ccnxCodecTlvEncoder_Destroy(&encoder); + size_t after = parcMemory_Outstanding(); + assertTrue(before == after, "Memory leak, expected %zu got %zu bytes\n", before, after); +} + +/** + * Check for memory leaks + */ +LONGBOW_TEST_CASE(Encoder, ccnxCodecTlvEncoder_Finalize) +{ + size_t before = parcMemory_Outstanding(); + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + + ccnxCodecTlvEncoder_Initialize(encoder); + ccnxCodecTlvEncoder_Finalize(encoder); + PARCBuffer *test = ccnxCodecTlvEncoder_CreateBuffer(encoder); + parcBuffer_Release(&test); + + ccnxCodecTlvEncoder_Destroy(&encoder); + size_t after = parcMemory_Outstanding(); + assertTrue(before == after, "Memory leak, expected %zu got %zu bytes\n", before, after); +} + +/* + * Do a long write, then backup the position. + * After Finalize, the Limit should have trimmed off the erased part. + */ +LONGBOW_TEST_CASE(Encoder, ccnxCodecTlvEncoder_Finalize_TrimLimit_Buffer) +{ + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + + uint8_t array[] = { 1, 2, 3, 4, 5, 6, 7, 8 }; + ccnxCodecTlvEncoder_AppendRawArray(encoder, sizeof(array), array); + ccnxCodecTlvEncoder_SetPosition(encoder, 3); + ccnxCodecTlvEncoder_Finalize(encoder); + + PARCBuffer *test = ccnxCodecTlvEncoder_CreateBuffer(encoder); + assertTrue(parcBuffer_Remaining(test) == 3, "Wrong length, expected 3 got %zu", parcBuffer_Remaining(test)); + + parcBuffer_Release(&test); + ccnxCodecTlvEncoder_Destroy(&encoder); +} + +/* + * Do a long write, then backup the position. + * After Finalize, the Limit should have trimmed off the erased part. + */ +LONGBOW_TEST_CASE(Encoder, ccnxCodecTlvEncoder_Finalize_TrimLimit_IoVec) +{ + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + + uint8_t array[] = { 1, 2, 3, 4, 5, 6, 7, 8 }; + ccnxCodecTlvEncoder_AppendRawArray(encoder, sizeof(array), array); + ccnxCodecTlvEncoder_SetPosition(encoder, 3); + ccnxCodecTlvEncoder_Finalize(encoder); + + CCNxCodecNetworkBufferIoVec *iov = ccnxCodecTlvEncoder_CreateIoVec(encoder); + assertTrue(ccnxCodecNetworkBufferIoVec_Length(iov) == 3, "Wrong length, expected 3 got %zu", ccnxCodecNetworkBufferIoVec_Length(iov)); + + ccnxCodecNetworkBufferIoVec_Release(&iov); + ccnxCodecTlvEncoder_Destroy(&encoder); +} + +/** + * Check for memory leaks and correct isInitialized state + */ +LONGBOW_TEST_CASE(Encoder, ccnxCodecTlvEncoder_Initialize) +{ + size_t before = parcMemory_Outstanding(); + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + + ccnxCodecTlvEncoder_Initialize(encoder); + + ccnxCodecTlvEncoder_Destroy(&encoder); + + size_t after = parcMemory_Outstanding(); + assertTrue(before == after, "Memory leak, expected %zu got %zu bytes\n", before, after); +} + +/** + * Make sure calling Initialized on an Initialized buffer does not leak + */ +LONGBOW_TEST_CASE(Encoder, ccnxCodecTlvEncoder_Initialize_Twice) +{ + size_t before = parcMemory_Outstanding(); + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + ccnxCodecTlvEncoder_Initialize(encoder); + ccnxCodecTlvEncoder_Initialize(encoder); + ccnxCodecTlvEncoder_Destroy(&encoder); + + size_t after = parcMemory_Outstanding(); + assertTrue(before == after, "Memory leak, expected %zu got %zu bytes\n", before, after); +} + +LONGBOW_TEST_CASE(Encoder, ccnxCodecTlvEncoder_Position) +{ + uint8_t helloString[] = "hello"; + PARCBuffer *hello = parcBuffer_Wrap(helloString, 5, 0, 5); + + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + ccnxCodecTlvEncoder_Initialize(encoder); + + size_t length = ccnxCodecTlvEncoder_AppendBuffer(encoder, 2, hello); + size_t position = ccnxCodecTlvEncoder_Position(encoder); + + assertTrue(length == position, "Position not right expected %zu got %zu", length, position); + + ccnxCodecTlvEncoder_Destroy(&encoder); + parcBuffer_Release(&hello); +} + +LONGBOW_TEST_CASE(Encoder, ccnxCodecTlvEncoder_SetContainerLength) +{ + uint8_t helloString[] = "hello"; + PARCBuffer *hello = parcBuffer_Wrap(helloString, 5, 0, 5); + + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + ccnxCodecTlvEncoder_Initialize(encoder); + + size_t containerPosition = ccnxCodecTlvEncoder_Position(encoder); + ccnxCodecTlvEncoder_AppendBuffer(encoder, 2, hello); + ccnxCodecTlvEncoder_AppendBuffer(encoder, 2, hello); + + size_t currentPosition = ccnxCodecTlvEncoder_Position(encoder); + + // when I set the length of the first container, we should be positioned back to + // the current location. + + ccnxCodecTlvEncoder_SetContainerLength(encoder, containerPosition, 99); + size_t testPosition = ccnxCodecTlvEncoder_Position(encoder); + + assertTrue(testPosition == currentPosition, "Position not right expected %zu got %zu", currentPosition, testPosition); + + // and make sure the length was updated + ccnxCodecTlvEncoder_Finalize(encoder); + PARCBuffer *output = ccnxCodecTlvEncoder_CreateBuffer(encoder); + parcBuffer_SetPosition(output, 2); + uint16_t testlength = parcBuffer_GetUint16(output); + + assertTrue(testlength == 99, "Updated length wrong, expected %u got %u", 99, testlength); + + ccnxCodecTlvEncoder_Destroy(&encoder); + parcBuffer_Release(&hello); + parcBuffer_Release(&output); +} + +LONGBOW_TEST_CASE(Encoder, ccnxCodecTlvEncoder_AppendUint8) +{ + PARCBuffer *truth = parcBuffer_Wrap((uint8_t[]) { 0x10, 0x20, 0x00, 0x01, 0xFF }, 5, 0, 5); + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + ccnxCodecTlvEncoder_Initialize(encoder); + ccnxCodecTlvEncoder_AppendUint8(encoder, 0x1020, 0xFF); + ccnxCodecTlvEncoder_Finalize(encoder); + PARCBuffer *test = ccnxCodecTlvEncoder_CreateBuffer(encoder); + + if (!parcBuffer_Equals(truth, test)) { + printf("Buffers not equal\n"); + printf("Expected\n"); + parcBuffer_Display(truth, 3); + printf("Got\n"); + parcBuffer_Display(test, 3); + assertTrue(parcBuffer_Equals(truth, test), "Buffers not equal"); + } + parcBuffer_Release(&test); + ccnxCodecTlvEncoder_Destroy(&encoder); + parcBuffer_Release(&truth); +} + +LONGBOW_TEST_CASE(Encoder, ccnxCodecTlvEncoder_AppendUint16) +{ + PARCBuffer *truth = parcBuffer_Wrap((uint8_t[]) { 0x10, 0x21, 0x00, 0x02, 0xFF, 0x01 }, 6, 0, 6); + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + ccnxCodecTlvEncoder_Initialize(encoder); + ccnxCodecTlvEncoder_AppendUint16(encoder, 0x1021, 0xFF01); + ccnxCodecTlvEncoder_Finalize(encoder); + PARCBuffer *test = ccnxCodecTlvEncoder_CreateBuffer(encoder); + + if (!parcBuffer_Equals(truth, test)) { + printf("Buffers not equal\n"); + printf("Expected\n"); + parcBuffer_Display(truth, 3); + printf("Got\n"); + parcBuffer_Display(test, 3); + assertTrue(parcBuffer_Equals(truth, test), "Buffers not equal"); + } + parcBuffer_Release(&test); + ccnxCodecTlvEncoder_Destroy(&encoder); + parcBuffer_Release(&truth); +} + +LONGBOW_TEST_CASE(Encoder, ccnxCodecTlvEncoder_AppendUint32) +{ + PARCBuffer *truth = parcBuffer_Wrap((uint8_t[]) { 0x10, 0x22, 0x00, 0x04, 0xFF, 0x01, 0x02, 0x03 }, 8, 0, 8); + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + ccnxCodecTlvEncoder_Initialize(encoder); + ccnxCodecTlvEncoder_AppendUint32(encoder, 0x1022, 0xFF010203); + ccnxCodecTlvEncoder_Finalize(encoder); + PARCBuffer *test = ccnxCodecTlvEncoder_CreateBuffer(encoder); + + if (!parcBuffer_Equals(truth, test)) { + printf("Buffers not equal\n"); + printf("Expected\n"); + parcBuffer_Display(truth, 3); + printf("Got\n"); + parcBuffer_Display(test, 3); + assertTrue(parcBuffer_Equals(truth, test), "Buffers not equal"); + } + parcBuffer_Release(&test); + ccnxCodecTlvEncoder_Destroy(&encoder); + parcBuffer_Release(&truth); +} + +LONGBOW_TEST_CASE(Encoder, ccnxCodecTlvEncoder_AppendUint64) +{ + PARCBuffer *truth = parcBuffer_Wrap((uint8_t[]) { 0x10, 0x23, 0x00, 0x08, 0xFF, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 }, 12, 0, 12); + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + ccnxCodecTlvEncoder_Initialize(encoder); + ccnxCodecTlvEncoder_AppendUint64(encoder, 0x1023, 0xFF01020304050607ULL); + ccnxCodecTlvEncoder_Finalize(encoder); + PARCBuffer *test = ccnxCodecTlvEncoder_CreateBuffer(encoder); + + if (!parcBuffer_Equals(truth, test)) { + printf("Buffers not equal\n"); + printf("Expected\n"); + parcBuffer_Display(truth, 3); + printf("Got\n"); + parcBuffer_Display(test, 3); + assertTrue(parcBuffer_Equals(truth, test), "Buffers not equal"); + } + parcBuffer_Release(&test); + ccnxCodecTlvEncoder_Destroy(&encoder); + parcBuffer_Release(&truth); +} + +LONGBOW_TEST_CASE(Encoder, ccnxCodecTlvEncoder_AppendVarInt) +{ + struct test_vector { + uint64_t value; + bool valid; + int length; + uint8_t *array; + } vectors[] = { + { .value = 0, .valid = true, .length = 5, .array = (uint8_t[]) { 0x10, 0x23, 0x00, 0x01, 0x00 } }, + { .value = 0xFF, .valid = true, .length = 5, .array = (uint8_t[]) { 0x10, 0x23, 0x00, 0x01, 0xFF } }, + { .value = 0x0101, .valid = true, .length = 6, .array = (uint8_t[]) { 0x10, 0x23, 0x00, 0x02, 0x01, 0x01} }, + { .value = 0xFF01, .valid = true, .length = 6, .array = (uint8_t[]) { 0x10, 0x23, 0x00, 0x02, 0xFF, 0x01} }, + { .value = 0x010001, .valid = true, .length = 7, .array = (uint8_t[]) { 0x10, 0x23, 0x00, 0x03, 0x01, 0x00, 0x01} }, + { .value = 0xFF0001, .valid = true, .length = 7, .array = (uint8_t[]) { 0x10, 0x23, 0x00, 0x03, 0xFF, 0x00, 0x01} }, + { .value = 0x01000000, .valid = true, .length = 8, .array = (uint8_t[]) { 0x10, 0x23, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00} }, + { .value = 0xFF002001, .valid = true, .length = 8, .array = (uint8_t[]) { 0x10, 0x23, 0x00, 0x04, 0xFF, 0x00, 0x20, 0x01} }, + { .value = 0xFF00200103040506ULL, .valid = true, .length = 12, .array = (uint8_t[]) { 0x10, 0x23, 0x00, 0x08, 0xFF, 0x00, 0x20, 0x01, 0x03, 0x04, 0x05, 0x06} }, + // sentinal is NULL array + { .value = 0, .valid = false, .length = 0, .array = NULL }, + }; + + for (int i = 0; vectors[i].array != NULL; i++) { + // only do the valid ones for the encode test + if (vectors[i].valid) { + PARCBuffer *truth = parcBuffer_Wrap(vectors[i].array, vectors[i].length, 0, vectors[i].length); + + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + ccnxCodecTlvEncoder_Initialize(encoder); + size_t length = ccnxCodecTlvEncoder_AppendVarInt(encoder, 0x1023, vectors[i].value); + + assertTrue(length == vectors[i].length, "Wrong length index %d, got %zu expected %d", i, length, vectors[i].length); + + ccnxCodecTlvEncoder_Finalize(encoder); + PARCBuffer *test = ccnxCodecTlvEncoder_CreateBuffer(encoder); + + assertTrue(parcBuffer_Equals(truth, test), "Buffers not equal index %d", i) + { + printf("Expected\n"); + parcBuffer_Display(truth, 3); + printf("Got\n"); + parcBuffer_Display(test, 3); + } + + parcBuffer_Release(&test); + ccnxCodecTlvEncoder_Destroy(&encoder); + parcBuffer_Release(&truth); + } + } +} + +LONGBOW_TEST_CASE(Encoder, ccnxCodecTlvEncoder_MarkSignatureEnd) +{ + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + ccnxCodecTlvEncoder_Initialize(encoder); + ccnxCodecTlvEncoder_AppendUint8(encoder, 0x1020, 0xFF); + + ccnxCodecTlvEncoder_MarkSignatureEnd(encoder); + assertTrue(encoder->signatureEnd == 5, "Wrong end position, expected %u got %zu", 5, encoder->signatureEnd) + { + ccnxCodecNetworkBuffer_Display(encoder->buffer, 3); + } + + assertTrue(encoder->signatureStartEndSet == END_SET, "Wrong flag, expected %d got %d", END_SET, encoder->signatureStartEndSet); + ccnxCodecTlvEncoder_Destroy(&encoder); +} + +LONGBOW_TEST_CASE(Encoder, ccnxCodecTlvEncoder_MarkSignatureStart) +{ + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + ccnxCodecTlvEncoder_Initialize(encoder); + ccnxCodecTlvEncoder_AppendUint8(encoder, 0x1020, 0xFF); + + ccnxCodecTlvEncoder_MarkSignatureStart(encoder); + assertTrue(encoder->signatureStart == 5, "Wrong start position, expected %u got %zu", 5, encoder->signatureStart) + { + ccnxCodecNetworkBuffer_Display(encoder->buffer, 3); + } + + assertTrue(encoder->signatureStartEndSet == START_SET, "Wrong flag, expected %d got %d", START_SET, encoder->signatureStartEndSet); + ccnxCodecTlvEncoder_Destroy(&encoder); +} + +LONGBOW_TEST_CASE(Encoder, ccnxCodecTlvEncoder_ComputeSignature) +{ + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + ccnxCodecTlvEncoder_Initialize(encoder); + ccnxCodecTlvEncoder_MarkSignatureStart(encoder); + ccnxCodecTlvEncoder_AppendUint8(encoder, 0x1020, 0xFF); + ccnxCodecTlvEncoder_MarkSignatureEnd(encoder); + + PARCSigner *signer = ccnxValidationCRC32C_CreateSigner(); + ccnxCodecTlvEncoder_SetSigner(encoder, signer); + parcSigner_Release(&signer); + + assertTrue(encoder->signatureStartEndSet == BOTH_SET, "Wrong flag, expected %d got %d", BOTH_SET, encoder->signatureStartEndSet); + + PARCSignature *sig = ccnxCodecTlvEncoder_ComputeSignature(encoder); + assertNotNull(sig, "Got null signature"); + + uint8_t truesig[] = { 0xA3, 0xAA, 0xC8, 0x4B }; + PARCBuffer *truesigBuffer = parcBuffer_Rewind(parcBuffer_CreateFromArray(truesig, sizeof(truesig))); + PARCBuffer *test = parcSignature_GetSignature(sig); + assertTrue(parcBuffer_Equals(truesigBuffer, test), "wrong crc value") + { + printf("Expected\n"); + parcBuffer_Display(truesigBuffer, 3); + printf("Got\n"); + parcBuffer_Display(test, 3); + } + + parcBuffer_Release(&truesigBuffer); + parcSignature_Release(&sig); + ccnxCodecTlvEncoder_Destroy(&encoder); +} + +LONGBOW_TEST_CASE(Encoder, ccnxCodecTlvEncoder_GetSigner) +{ + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + PARCSigner *signer = ccnxValidationCRC32C_CreateSigner(); + ccnxCodecTlvEncoder_SetSigner(encoder, signer); + + PARCSigner *test = ccnxCodecTlvEncoder_GetSigner(encoder); + assertTrue(test == signer, "Did not return the right signer, expected %p got %p", (void *) signer, (void *) test); + parcSigner_Release(&signer); + ccnxCodecTlvEncoder_Destroy(&encoder); +} + +LONGBOW_TEST_CASE(Encoder, ccnxCodecTlvEncoder_PutUint8) +{ + PARCBuffer *truth = parcBuffer_Wrap((uint8_t[]) { 0x10, 0xEE, 0x00, 0x01, 0xFF }, 5, 0, 5); + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + ccnxCodecTlvEncoder_Initialize(encoder); + ccnxCodecTlvEncoder_AppendUint8(encoder, 0x1020, 0xFF); + + ccnxCodecTlvEncoder_PutUint8(encoder, 1, 0xEE); + + ccnxCodecTlvEncoder_Finalize(encoder); + PARCBuffer *test = ccnxCodecTlvEncoder_CreateBuffer(encoder); + + if (!parcBuffer_Equals(truth, test)) { + printf("Buffers not equal\n"); + printf("Expected\n"); + parcBuffer_Display(truth, 3); + printf("Got\n"); + parcBuffer_Display(test, 3); + assertTrue(parcBuffer_Equals(truth, test), "Buffers not equal"); + } + parcBuffer_Release(&test); + ccnxCodecTlvEncoder_Destroy(&encoder); + parcBuffer_Release(&truth); +} + +LONGBOW_TEST_CASE(Encoder, ccnxCodecTlvEncoder_PutUint16) +{ + PARCBuffer *truth = parcBuffer_Wrap((uint8_t[]) { 0x10, 0xEE, 0xDD, 0x01, 0xFF }, 5, 0, 5); + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + ccnxCodecTlvEncoder_Initialize(encoder); + ccnxCodecTlvEncoder_AppendUint8(encoder, 0x1020, 0xFF); + + ccnxCodecTlvEncoder_PutUint16(encoder, 1, 0xEEDD); + + ccnxCodecTlvEncoder_Finalize(encoder); + PARCBuffer *test = ccnxCodecTlvEncoder_CreateBuffer(encoder); + + if (!parcBuffer_Equals(truth, test)) { + printf("Buffers not equal\n"); + printf("Expected\n"); + parcBuffer_Display(truth, 3); + printf("Got\n"); + parcBuffer_Display(test, 3); + assertTrue(parcBuffer_Equals(truth, test), "Buffers not equal"); + } + parcBuffer_Release(&test); + ccnxCodecTlvEncoder_Destroy(&encoder); + parcBuffer_Release(&truth); +} + + +LONGBOW_TEST_CASE(Encoder, ccnxCodecTlvEncoder_SetPosition) +{ + uint8_t helloString[] = "hello"; + PARCBuffer *hello = parcBuffer_Wrap(helloString, 5, 0, 5); + + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + ccnxCodecTlvEncoder_Initialize(encoder); + + ccnxCodecTlvEncoder_AppendBuffer(encoder, 2, hello); + // position is now at 9 (2+2+5) + + ccnxCodecTlvEncoder_SetPosition(encoder, 2); + + size_t position = ccnxCodecTlvEncoder_Position(encoder); + + assertTrue(2 == position, "Position not right expected %u got %zu", 2, position); + + ccnxCodecTlvEncoder_Destroy(&encoder); + parcBuffer_Release(&hello); +} + +LONGBOW_TEST_CASE(Encoder, ccnxCodecTlvEncoder_SetError_Present) +{ + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + + CCNxCodecError *error = ccnxCodecError_Create(TLV_ERR_DECODE, "foo", 1, 1); + ccnxCodecTlvEncoder_SetError(encoder, error); + + // now try to set a second time + bool success = ccnxCodecTlvEncoder_SetError(encoder, error); + ccnxCodecError_Release(&error); + + + assertFalse(success, "Returned success when should have failed"); + assertNotNull(encoder->error, "Encoder has null error member"); + + ccnxCodecTlvEncoder_Destroy(&encoder); +} + +LONGBOW_TEST_CASE(Encoder, ccnxCodecTlvEncoder_SetError_Missing) +{ + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + + CCNxCodecError *error = ccnxCodecError_Create(TLV_ERR_DECODE, "foo", 1, 1); + bool success = ccnxCodecTlvEncoder_SetError(encoder, error); + ccnxCodecError_Release(&error); + + assertTrue(success, "Returned failure when should have succeeded"); + assertNotNull(encoder->error, "Encoder has null error member"); + + ccnxCodecTlvEncoder_Destroy(&encoder); +} + +LONGBOW_TEST_CASE(Encoder, ccnxCodecTlvEncoder_GetError) +{ + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + + CCNxCodecError *error = ccnxCodecError_Create(TLV_ERR_DECODE, "foo", 1, 1); + ccnxCodecTlvEncoder_SetError(encoder, error); + ccnxCodecError_Release(&error); + + CCNxCodecError *test = ccnxCodecTlvEncoder_GetError(encoder); + assertNotNull(test, "Encoder has null error member"); + + ccnxCodecTlvEncoder_Destroy(&encoder); +} + +LONGBOW_TEST_CASE(Encoder, ccnxCodecTlvEncoder_ClearError_Present) +{ + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + + CCNxCodecError *error = ccnxCodecError_Create(TLV_ERR_DECODE, "foo", 1, 1); + ccnxCodecTlvEncoder_SetError(encoder, error); + ccnxCodecError_Release(&error); + + ccnxCodecTlvEncoder_ClearError(encoder); + assertNull(encoder->error, "Encoder does not have a null error"); + + ccnxCodecTlvEncoder_Destroy(&encoder); +} + +LONGBOW_TEST_CASE(Encoder, ccnxCodecTlvEncoder_ClearError_Missing) +{ + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + + ccnxCodecTlvEncoder_ClearError(encoder); + assertNull(encoder->error, "Encoder does not have a null error"); + + ccnxCodecTlvEncoder_Destroy(&encoder); +} + +// ============================================ + +LONGBOW_TEST_FIXTURE(Local) +{ + LONGBOW_RUN_TEST_CASE(Local, _ccnxCodecTlvEncoder_ComputeVarIntLength); +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Local, _ccnxCodecTlvEncoder_ComputeVarIntLength) +{ + struct test_vector { + uint64_t value; + int length; + } vectors[] = { + { .value = 0, .length = 1 }, + { .value = 0xFF, .length = 1 }, + { .value = 0x0101, .length = 2 }, + { .value = 0xFF01, .length = 2 }, + { .value = 0x010001, .length = 3 }, + { .value = 0xFF0001, .length = 3 }, + { .value = 0x01000000, .length = 4 }, + { .value = 0xFF002001, .length = 4 }, + { .value = 0x0100000000, .length = 5 }, + { .value = 0xFF00002001, .length = 5 }, + { .value = 0x010000000000, .length = 6 }, + { .value = 0xFF0000002001, .length = 6 }, + { .value = 0x01000000000000, .length = 7 }, + { .value = 0xFF000000002001, .length = 7 }, + { .value = 0xFF00200103040506ULL, .length = 8 }, + // sentinal is length 0 + { .value = 0, .length = 0 }, + }; + + for (int i = 0; vectors[i].length != 0; i++) { + unsigned test = _ccnxCodecTlvEncoder_ComputeVarIntLength(vectors[i].value); + assertTrue(test == vectors[i].length, "Incorrect length index %d, expected %u got %u", i, vectors[i].length, test); + } +} + + +// =================================================== + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(parc_Tlv); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-common/ccnx/common/codec/test/test_ccnxCodec_TlvPacket.c b/libccnx-common/ccnx/common/codec/test/test_ccnxCodec_TlvPacket.c new file mode 100644 index 00000000..2dfdfcb3 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/test/test_ccnxCodec_TlvPacket.c @@ -0,0 +1,447 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Does not do detailed tests of the decode -- those are tested in the individual schema_vX unit tests. + * These tests make sure that we (a) get a result when we expect to get a results, and (b) will spot-check + * the result, such as looking at the Name. + * + */ + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. + +#include <config.h> +#include <stdio.h> +#include <fcntl.h> + +#include "../ccnxCodec_TlvPacket.c" +#include <parc/algol/parc_SafeMemory.h> + +#include <LongBow/unit-test.h> + +#include <ccnx/common/codec/ccnxCodec_NetworkBuffer.h> +#include <ccnx/common/validation/ccnxValidation_HmacSha256.h> + +#include <ccnx/common/ccnx_Interest.h> +#include <ccnx/common/ccnx_ContentObject.h> + +#include <ccnx/common/internal/ccnx_InterestDefault.h> + +#include <ccnx/common/codec/schema_v1/testdata/v1_interest_all_fields.h> +#include <ccnx/common/codec/schema_v1/testdata/v1_content_nameA_keyid1_rsasha256.h> +#include <ccnx/common/codec/schema_v1/testdata/v1_interest_bad_message_length.h> + +LONGBOW_TEST_RUNNER(rta_TlvPacket) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(rta_TlvPacket) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(rta_TlvPacket) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, rtaTlvPacket_BufferDecode_V1); + LONGBOW_RUN_TEST_CASE(Global, rtaTlvPacket_BufferDecode_VFF); + + LONGBOW_RUN_TEST_CASE(Global, rtaTlvPacket_IoVecDecode_OneBuffer); + LONGBOW_RUN_TEST_CASE(Global, rtaTlvPacket_IoVecDecode_SeveralBuffer); + + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecTlvPacket_DictionaryEncode_V1); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecTlvPacket_DictionaryEncode_VFF); + + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecTlvPacket_Decode_V1); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecTlvPacket_Decode_VFF); + + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecTlvPacket_EncodeWithSignature); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecTlvPacket_GetPacketLength); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecTlvPacket_MinimalHeaderLength); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, rtaTlvPacket_BufferDecode_V1) +{ + PARCBufferComposer *composer = parcBufferComposer_Create(); + PARCBuffer *interestMessage = parcBuffer_Wrap(v1_interest_all_fields, sizeof(v1_interest_all_fields), 0, sizeof(v1_interest_all_fields)); + parcBufferComposer_PutBuffer(composer, interestMessage); + parcBuffer_Release(&interestMessage); + + // Append extraneous data to the end of the buffer to make sure the decoder terminates at the end of the CCNx message. + PARCBuffer *padding = parcBuffer_AllocateCString("ThisShouldNeverBeParsed"); + parcBufferComposer_PutBuffer(composer, padding); + parcBuffer_Release(&padding); + PARCBuffer *packetBuffer = parcBufferComposer_CreateBuffer(composer); + parcBufferComposer_Release(&composer); + parcBuffer_Rewind(packetBuffer); + + //PARCBuffer *packetBuffer = parcBuffer_Wrap(v1_interest_all_fields, sizeof(v1_interest_all_fields), 0, sizeof(v1_interest_all_fields)); + CCNxTlvDictionary *dict = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + bool success = ccnxCodecTlvPacket_BufferDecode(packetBuffer, dict); + assertTrue(success, "Failed to decode good v1 interest"); + + CCNxName *name = ccnxInterest_GetName(dict); + assertNotNull(name, "Did not find a name in the decoded interest"); + + ccnxTlvDictionary_Release(&dict); + parcBuffer_Release(&packetBuffer); +} + +LONGBOW_TEST_CASE(Global, rtaTlvPacket_BufferDecode_VFF) +{ + uint8_t encoded[] = { + 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + + PARCBuffer *packetBuffer = parcBuffer_Wrap(encoded, sizeof(encoded), 0, sizeof(encoded)); + + CCNxTlvDictionary *dict = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + bool success = ccnxCodecTlvPacket_BufferDecode(packetBuffer, dict); + assertFalse(success, "Did not fail on decode of version 255 packet"); + + ccnxTlvDictionary_Release(&dict); + parcBuffer_Release(&packetBuffer); +} + +typedef struct allocator_arg { + size_t maxallocation; +} AllocatorArg; + +static size_t +testAllocator(void *userarg, size_t bytes, void **output) +{ + AllocatorArg *arg = userarg; + if (bytes > arg->maxallocation) { + bytes = arg->maxallocation; + } + + *output = parcMemory_Allocate(bytes); + assertNotNull(*output, "parcMemory_Allocate(%zu) returned NULL", bytes); + if (*output) { + return bytes; + } + return 0; +} + +static void +testDeallocator(void *userarg, void **memory) +{ + parcMemory_Deallocate((void **) memory); +} + +const CCNxCodecNetworkBufferMemoryBlockFunctions TestMemoryBlock = { + .allocator = &testAllocator, + .deallocator = &testDeallocator +}; + + +static void +runIoVecTest(AllocatorArg maxalloc) +{ + CCNxCodecNetworkBuffer *netbuff = ccnxCodecNetworkBuffer_Create(&TestMemoryBlock, &maxalloc); + + ccnxCodecNetworkBuffer_PutArray(netbuff, + sizeof(v1_interest_all_fields), + v1_interest_all_fields); + + CCNxCodecNetworkBufferIoVec *vec = ccnxCodecNetworkBuffer_CreateIoVec(netbuff); + + CCNxTlvDictionary *output = ccnxTlvDictionary_Create(CCNxCodecSchemaV1TlvDictionary_MessageFastArray_END, CCNxCodecSchemaV1TlvDictionary_Lists_END); + + ccnxTlvDictionary_SetMessageType_Interest(output, CCNxTlvDictionary_SchemaVersion_V1); + + bool success = ccnxCodecTlvPacket_IoVecDecode(vec, output); + assertTrue(success, "Failed to decode buffer in iovec format"); + + ccnxTlvDictionary_Release(&output); + ccnxCodecNetworkBufferIoVec_Release(&vec); + ccnxCodecNetworkBuffer_Release(&netbuff); +} + +LONGBOW_TEST_CASE(Global, rtaTlvPacket_IoVecDecode_OneBuffer) +{ + AllocatorArg maxalloc = { .maxallocation = 2048 }; + runIoVecTest(maxalloc); +} + +LONGBOW_TEST_CASE(Global, rtaTlvPacket_IoVecDecode_SeveralBuffer) +{ + // 32 bytes is needed for bookkeeping, so this means we'll have a 32-byte memory block + AllocatorArg maxalloc = { .maxallocation = 64 }; + runIoVecTest(maxalloc); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecTlvPacket_DictionaryEncode_V1) +{ + CCNxName *name = ccnxName_CreateFromCString("ccnx:/Antidisestablishmentarianism"); + CCNxTlvDictionary *message = + ccnxInterest_CreateWithImpl(&CCNxInterestFacadeV1_Implementation, + name, CCNxInterestDefault_LifetimeMilliseconds, NULL, NULL, CCNxInterestDefault_HopLimit); + + CCNxCodecNetworkBufferIoVec *iovec = ccnxCodecTlvPacket_DictionaryEncode(message, NULL); + assertNotNull(iovec, "Got null iovec on a good dictionary"); + + ccnxCodecNetworkBufferIoVec_Release(&iovec); + ccnxTlvDictionary_Release(&message); + ccnxName_Release(&name); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecTlvPacket_DictionaryEncode_VFF) +{ + CCNxTlvDictionary *message = ccnxTlvDictionary_Create(20, 20); + ccnxTlvDictionary_SetMessageType_Interest(message, 0xFF); + CCNxCodecNetworkBufferIoVec *iovec = ccnxCodecTlvPacket_DictionaryEncode(message, NULL); + assertNull(iovec, "Should have gotten null result for schema version 255"); + ccnxTlvDictionary_Release(&message); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecTlvPacket_Decode_V1) +{ + PARCBufferComposer *composer = parcBufferComposer_Create(); + PARCBuffer *interestMessage = parcBuffer_Wrap(v1_interest_all_fields, sizeof(v1_interest_all_fields), 0, sizeof(v1_interest_all_fields)); + parcBufferComposer_PutBuffer(composer, interestMessage); + parcBuffer_Release(&interestMessage); + + // Append extraneous data to the end of the buffer to make sure the decoder terminates at the end of the CCNx message. + PARCBuffer *padding = parcBuffer_AllocateCString("ThisShouldNeverBeParsed"); + parcBufferComposer_PutBuffer(composer, padding); + parcBuffer_Release(&padding); + PARCBuffer *packetBuffer = parcBufferComposer_CreateBuffer(composer); + parcBufferComposer_Release(&composer); + parcBuffer_Rewind(packetBuffer); + + CCNxTlvDictionary *dict = ccnxCodecTlvPacket_Decode(packetBuffer); + assertNotNull(dict, "Got null dictionary decoding good packet"); + + ccnxTlvDictionary_Release(&dict); + parcBuffer_Release(&packetBuffer); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecTlvPacket_Decode_VFF) +{ + uint8_t encoded[] = { + 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + + PARCBuffer *packetBuffer = parcBuffer_Wrap(encoded, sizeof(encoded), 0, sizeof(encoded)); + + CCNxTlvDictionary *dict = ccnxCodecTlvPacket_Decode(packetBuffer); + assertNull(dict, "Got non-null dictionary decoding version 255 packet"); + parcBuffer_Release(&packetBuffer); +} + + +LONGBOW_TEST_CASE(Global, ccnxCodecTlvPacket_EncodeWithSignature) +{ + CCNxName *name = ccnxName_CreateFromCString("ccnx:/foo/bar"); + PARCBuffer *payload = parcBuffer_WrapCString("payload"); + CCNxContentObject *obj = ccnxContentObject_CreateWithNameAndPayload(name, payload); + ccnxName_Release(&name); + parcBuffer_Release(&payload); + + PARCBuffer *secretKey = parcBuffer_WrapCString("abcdefghijklmnopqrstuvwxyx"); + PARCSigner *signer = ccnxValidationHmacSha256_CreateSigner(secretKey); + + // should really scrub the memory + parcBuffer_Release(&secretKey); + + // this was breaking the signature + PARCKeyStore *keyStore = parcSigner_GetKeyStore(signer); + const PARCCryptoHash *secretHash = parcKeyStore_GetVerifierKeyDigest(keyStore); + const PARCBuffer *keyid = parcCryptoHash_GetDigest(secretHash); + ccnxValidationHmacSha256_Set(obj, keyid); + parcCryptoHash_Release((PARCCryptoHash **) &secretHash); + + CCNxCodecNetworkBufferIoVec *iovec = ccnxCodecTlvPacket_DictionaryEncode(obj, signer); + + ccnxCodecNetworkBufferIoVec_Display(iovec, 0); + + int fd = open("/dev/null", O_WRONLY); + assertTrue(fd != -1, "Error opening /dev/null"); + + ssize_t written = writev(fd, ccnxCodecNetworkBufferIoVec_GetArray(iovec), ccnxCodecNetworkBufferIoVec_GetCount(iovec)); + assertTrue(written != -1, "Error writting to /dev/null"); + close(fd); + + ccnxCodecNetworkBufferIoVec_Release(&iovec); + parcSigner_Release(&signer); + ccnxContentObject_Release(&obj); +} + +static uint8_t testDataV1_Interest_AllFields[] = { + 0x01, 0x00, 0x00, 100, // ver = 1, type = interest, length = 100 + 0x20, 0x00, 0x11, 14, // HopLimit = 32, reserved = 0, flags = 0x11, header length = 14 + // ------------------------ + 0x00, 0x01, 0x00, 2, // Interest Lifetime (2 bytes) + 0xEA, 0xEB, + // ------------------------ + 0x00, 0x01, 0x00, 82, // type = interest, length = 82 + // ------------------------ + 0x00, 0x00, 0x00, 8, // type = name, length = 8 + 0x00, 0x02, 0x00, 4, // type = binary, length = 4 + 'c', 'o', 'o', 'l', // "cool" + // ------------------------ + 0x00, 0x02, 0x00, 16, // type = keyid restriction, length = 16 + 0xa0, 0xa1, 0xa2, 0xa3, + 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, + 0xac, 0xad, 0xae, 0xaf, + // ------------------------ + 0x00, 0x03, 0x00, 32, // type = hash restriction, length = 32 + 0xb0, 0xb1, 0xb2, 0xb3, + 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, + 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, + 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, + 0xcc, 0xcd, 0xce, 0xcf, + // ------------------------ + 0x00, 0x04, 0x00, 1, // Internest payload method (1 byte) + 0x00, + // ------------------------ + 0x00, 0x01, 0x00, 5, // type = payload, length = 5 + 0xD0, 0xD1, 0xD2, 0xD3, + 0xD4, +}; + +LONGBOW_TEST_CASE(Global, ccnxCodecTlvPacket_GetPacketLength) +{ + PARCBuffer *packet = parcBuffer_Wrap(testDataV1_Interest_AllFields, sizeof(testDataV1_Interest_AllFields), 0, sizeof(testDataV1_Interest_AllFields)); + size_t packetLength = ccnxCodecTlvPacket_GetPacketLength(packet); + assertTrue(packetLength == sizeof(testDataV1_Interest_AllFields), "Wrong total message length, expected %zu got %zu", sizeof(testDataV1_Interest_AllFields), packetLength); + parcBuffer_Release(&packet); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecTlvPacket_MinimalHeaderLength) +{ + assertTrue(ccnxCodecTlvPacket_MinimalHeaderLength() > 0, "ccnxCodecTlvPacket_MinimalHeaderLength failed"); +} + +// ================================================================= + +LONGBOW_TEST_FIXTURE(Local) +{ + LONGBOW_RUN_TEST_CASE(Global, _decodeV1_Interest); + LONGBOW_RUN_TEST_CASE(Global, _decodeV1_ContentObject); + LONGBOW_RUN_TEST_CASE(Global, _decodeV1_Control); + LONGBOW_RUN_TEST_CASE(Global, _decodeV1_Unknown); + LONGBOW_RUN_TEST_CASE(Global, _decodeV1_Error); +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, _decodeV1_Interest) +{ + PARCBuffer *packetBuffer = parcBuffer_Wrap(v1_interest_all_fields, sizeof(v1_interest_all_fields), 0, sizeof(v1_interest_all_fields)); + CCNxTlvDictionary *dict = _decodeV1(packetBuffer); + assertNotNull(dict, "Error decoding good packet"); + + CCNxName *name = ccnxInterest_GetName(dict); + assertNotNull(name, "Null name in decoded Interest"); + + ccnxTlvDictionary_Release(&dict); + parcBuffer_Release(&packetBuffer); +} + +LONGBOW_TEST_CASE(Global, _decodeV1_ContentObject) +{ + PARCBuffer *packetBuffer = parcBuffer_Wrap(v1_content_nameA_keyid1_rsasha256, sizeof(v1_content_nameA_keyid1_rsasha256), 0, sizeof(v1_content_nameA_keyid1_rsasha256)); + CCNxTlvDictionary *dict = _decodeV1(packetBuffer); + assertNotNull(dict, "Error decoding good packet"); + + CCNxName *name = ccnxContentObject_GetName(dict); + assertNotNull(name, "Null name in decoded Interest"); + + ccnxTlvDictionary_Release(&dict); + parcBuffer_Release(&packetBuffer); +} + +LONGBOW_TEST_CASE(Global, _decodeV1_Control) +{ + testUnimplemented("V1 control not implemented yet"); +} + +LONGBOW_TEST_CASE(Global, _decodeV1_Unknown) +{ + uint8_t encoded[] = { + 0x01, 0xFF, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + + PARCBuffer *packetBuffer = parcBuffer_Wrap(encoded, sizeof(encoded), 0, sizeof(encoded)); + CCNxTlvDictionary *dict = _decodeV1(packetBuffer); + assertNull(dict, "Should have gotten NULL dictionary from unknown packet type"); + + parcBuffer_Release(&packetBuffer); +} + +LONGBOW_TEST_CASE(Global, _decodeV1_Error) +{ + PARCBuffer *packetBuffer = parcBuffer_Wrap(v1_interest_bad_message_length, sizeof(v1_interest_bad_message_length), 0, sizeof(v1_interest_bad_message_length)); + CCNxTlvDictionary *dict = _decodeV1(packetBuffer); + assertNull(dict, "Should have gotten NULL dictionary from unknown packet type"); + + parcBuffer_Release(&packetBuffer); +} + +// ================================================================= + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(rta_TlvPacket); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-common/ccnx/common/codec/test/test_ccnxCodec_TlvUtilities.c b/libccnx-common/ccnx/common/codec/test/test_ccnxCodec_TlvUtilities.c new file mode 100644 index 00000000..b8215822 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/test/test_ccnxCodec_TlvUtilities.c @@ -0,0 +1,496 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../ccnxCodec_TlvUtilities.c" +#include <parc/algol/parc_SafeMemory.h> + +#include <LongBow/unit-test.h> + +#include <inttypes.h> + +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_FixedHeader.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_ValidationDecoder.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_TlvDictionary.h> + + +typedef struct test_data { + uint8_t *packet; + PARCBuffer *fixedHeader; + CCNxCodecTlvDecoder *decoder; + CCNxTlvDictionary *dictionary; + + // truth table + uint8_t version; + uint8_t packetType; + uint16_t packetLength; + uint8_t headerLength; +} TestData; + +static TestData * +_commonSetup(void) +{ + TestData *data = parcMemory_AllocateAndClear(sizeof(TestData)); + assertNotNull(data, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(TestData)); + + data->packet = parcMemory_Allocate(8); + assertNotNull(data->packet, "parcMemory_Allocate(%u) returned NULL", 8); + + // Make a V1 fixed header + memcpy(data->packet, &((uint8_t[]) { + 0x01, // version + 0x01, // packetType + 0x01, 0x02, // packetLength + 0x00, // hopLimit/hopCount + 0x00, // returnCode + 0x03, // flags + 0x04 // headerLength + }), 8); + + + data->fixedHeader = parcBuffer_Wrap(data->packet, 8, 0, 8); + data->version = 1; + data->packetType = 1; + data->packetLength = 0x0102; + data->headerLength = 0x04; + data->decoder = ccnxCodecTlvDecoder_Create(data->fixedHeader); + data->dictionary = ccnxTlvDictionary_Create(10, 10); + return data; +} + +static void +_commonTeardown(TestData *data) +{ + ccnxTlvDictionary_Release(&data->dictionary); + ccnxCodecTlvDecoder_Destroy(&data->decoder); + parcBuffer_Release(&data->fixedHeader); + parcMemory_Deallocate((void **) &(data->packet)); + parcMemory_Deallocate((void **) &data); +} + +LONGBOW_TEST_RUNNER(rta_TlvUtilities) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(rta_TlvUtilities) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(rta_TlvUtilities) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecTlvUtilities_GetVarInt); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecTlvUtilities_DecodeContainer); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecTlvUtilities_DecodeSubcontainer); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecTlvUtilities_PutAsName); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecTlvUtilities_PutAsBuffer); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecTlvUtilities_PutAsHash); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecTlvUtilities_PutAsListBuffer); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecTlvUtilities_NestedEncode); + LONGBOW_RUN_TEST_CASE(Global, ccnxCodecTlvUtilities_EncodeCustomList); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + longBowTestCase_SetClipBoardData(testCase, _commonSetup()); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + _commonTeardown(longBowTestCase_GetClipBoardData(testCase)); + + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, ccnxCodecTlvUtilities_PutAsBuffer) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + uint32_t type = 1; + uint32_t length = 8; + + bool success = ccnxCodecTlvUtilities_PutAsBuffer(data->decoder, data->dictionary, type, length, + CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_FixedHeader); + + assertTrue(success, "Failed to save buffer slice"); + + int version = ccnxCodecSchemaV1FixedHeaderDecoder_GetVersion(data->dictionary); + assertTrue(version == data->version, "Wrong version, got %d expected %d", version, data->version); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecTlvUtilities_PutAsHash) +{ + uint8_t encoded[] = { + 0x00, 0x01, 0x00, 0x20, // 0x01 = CCNxCodecSchemaV1Types_HashType_SHA256 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + + PARCBuffer *tlvBuffer = parcBuffer_Wrap(encoded, sizeof(encoded), 0, sizeof(encoded)); + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(tlvBuffer); + + uint16_t type = 0x01; + CCNxTlvDictionary *dictionary = ccnxTlvDictionary_Create(CCNxCodecSchemaV1TlvDictionary_MessageFastArray_END, 10); + bool success = ccnxCodecTlvUtilities_PutAsHash(decoder, dictionary, type, sizeof(encoded), + CCNxCodecSchemaV1TlvDictionary_MessageFastArray_OBJHASH_RESTRICTION); + assertTrue(success, "Failed to save hash"); + + parcBuffer_Release(&tlvBuffer); + ccnxCodecTlvDecoder_Destroy(&decoder); + ccnxTlvDictionary_Release(&dictionary); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecTlvUtilities_GetVarInt) +{ + struct test_vector { + uint64_t value; + bool valid; + int length; + uint8_t *array; + } vectors[] = { + // length 0 invalid + { .value = 0, .valid = false, .length = 0, .array = (uint8_t[]) { 0x00 } }, + { .value = 0, .valid = true, .length = 1, .array = (uint8_t[]) { 0x00 } }, + { .value = 0xFF, .valid = true, .length = 1, .array = (uint8_t[]) { 0xFF } }, + { .value = 0x0001, .valid = true, .length = 2, .array = (uint8_t[]) { 0x00, 0x01} }, + { .value = 0xFF01, .valid = true, .length = 2, .array = (uint8_t[]) { 0xFF, 0x01} }, + { .value = 0x000001, .valid = true, .length = 3, .array = (uint8_t[]) { 0x00, 0x00, 0x01} }, + { .value = 0xFF0001, .valid = true, .length = 3, .array = (uint8_t[]) { 0xFF, 0x00, 0x01} }, + { .value = 0x00000001, .valid = true, .length = 4, .array = (uint8_t[]) { 0x00, 0x00, 0x00, 0x01} }, + { .value = 0xFF002001, .valid = true, .length = 4, .array = (uint8_t[]) { 0xFF, 0x00, 0x20, 0x01} }, + { .value = 0xFF00200103040506ULL, .valid = true, .length = 8, .array = (uint8_t[]) { 0xFF, 0x00, 0x20, 0x01, 0x03, 0x04, 0x05, 0x06} }, + // length 9 invalid + { .value = 0, .valid = false, .length = 9, .array = (uint8_t[]) { 0xFF, 0x00, 0x20, 0x01, 0x03, 0x04, 0x05, 0x06, 0x07} }, + // sentinal is NULL array + { .value = 0, .valid = false, .length = 0, .array = NULL }, + }; + + for (int i = 0; vectors[i].array != NULL; i++) { + PARCBuffer *buffer = parcBuffer_Wrap(vectors[i].array, vectors[i].length, 0, vectors[i].length); + + uint64_t value; + bool success = ccnxCodecTlvUtilities_GetVarInt(buffer, vectors[i].length, &value); + parcBuffer_Release(&buffer); + + assertTrue(success == vectors[i].valid, "index %d: Wrong return, got %d expected %d", i, success, vectors[i].valid); + if (vectors[i].valid) { + assertTrue(value == vectors[i].value, "index %d: wrong value: got %" PRIu64 " expected %" PRIu64, i, value, vectors[i].value); + } + } +} + + +static bool +_decodeSubContainer(CCNxCodecTlvDecoder *decoder, CCNxTlvDictionary *packetDictionary) +{ + return true; +} + +LONGBOW_TEST_CASE(Global, ccnxCodecTlvUtilities_DecodeSubcontainer) +{ + uint8_t metadata[] = { + 0x00, 0x0B, 0x00, 17, // Object Metadata, length = 17 + 0x00, 0x0C, 0x00, 0x01, // Object Type, length = 1 + 0x04, // LINK + 0x00, 0x0D, 0x00, 8, // Creation Time + 0x00, 0x00, 0x01, 0x43, // 1,388,534,400,000 msec + 0x4B, 0x19, 0x84, 0x00, + }; + + PARCBuffer *buffer = parcBuffer_Wrap(metadata, sizeof(metadata), 0, sizeof(metadata)); + + // now decode that snippit + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(buffer); + CCNxTlvDictionary *dictionary = ccnxTlvDictionary_Create(20, 20); + + uint16_t key = ccnxCodecTlvDecoder_GetType(decoder); + uint16_t length = ccnxCodecTlvDecoder_GetLength(decoder); + + bool success = ccnxCodecTlvUtilities_DecodeSubcontainer(decoder, dictionary, key, length, _decodeSubContainer); + + assertTrue(success, "Failed to decode metadata container"); + + ccnxTlvDictionary_Release(&dictionary); + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&buffer); +} + + +static bool +testTypeDecoder(CCNxCodecTlvDecoder *decoder, CCNxTlvDictionary *packetDictionary, uint16_t type, uint16_t length) +{ + switch (type) { + case 0x000C: // fallthrough + case 0x000D: + ccnxCodecTlvDecoder_Advance(decoder, length); + return true; + default: + return false; + } +} + +LONGBOW_TEST_CASE(Global, ccnxCodecTlvUtilities_DecodeContainer) +{ + uint8_t metadataContainer[] = { + 0x00, 0x0C, 0x00, 0x01, // Object Type, length = 1 + 0x04, // LINK + 0x00, 0x0D, 0x00, 8, // Creation Time + 0x00, 0x00, 0x01, 0x43, // 1,388,534,400,000 msec + 0x4B, 0x19, 0x84, 0x00, + }; + + PARCBuffer *buffer = parcBuffer_Wrap(metadataContainer, sizeof(metadataContainer), 0, sizeof(metadataContainer)); + + // now decode that snippit + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(buffer); + CCNxTlvDictionary *dictionary = ccnxTlvDictionary_Create(10, 10); + + bool success = ccnxCodecTlvUtilities_DecodeContainer(decoder, dictionary, testTypeDecoder); + + ccnxTlvDictionary_Release(&dictionary); + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&buffer); + + assertTrue(success, "The TLV types were known to us"); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecTlvUtilities_PutAsName) +{ + // A list of 2 TLV containers (types 0x000C and 0x000D) + uint8_t nameContainer[] = { + 0x00, 0x00, 0x00, 9, // type = name, length = 9 + 0x00, 0x03, 0x00, 5, // type = binary, length = 5 + 'h', 'e', 'l', 'l', // "hello" + 'o', + }; + + PARCBuffer *buffer = parcBuffer_Wrap(nameContainer, sizeof(nameContainer), 0, sizeof(nameContainer)); + + // now decode that snippit + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(buffer); + CCNxTlvDictionary *dictionary = ccnxTlvDictionary_Create(10, 10); + + uint16_t tlvtype = ccnxCodecTlvDecoder_GetType(decoder); + uint16_t tlvlength = ccnxCodecTlvDecoder_GetLength(decoder); + + // Saves "lci:/3=hello" + bool success = ccnxCodecTlvUtilities_PutAsName(decoder, dictionary, tlvtype, tlvlength, 1); + + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&buffer); + + assertTrue(success, "The Name failed to decode or some other error"); + + CCNxName *truth = ccnxName_CreateFromCString("lci:/3=hello"); + CCNxName *test = ccnxTlvDictionary_GetName(dictionary, 1); + assertTrue(ccnxName_Equals(truth, test), "Names not equal") + { + ccnxName_Display(test, 3); + ccnxName_Display(truth, 3); + ccnxName_Release(&truth); + ccnxTlvDictionary_Release(&dictionary); + } + + ccnxName_Release(&truth); + ccnxTlvDictionary_Release(&dictionary); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecTlvUtilities_PutAsListBuffer) +{ + uint8_t array[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; + PARCBuffer *buffer = parcBuffer_Wrap(array, sizeof(array), 0, sizeof(array)); + + PARCBuffer *truth[3]; + truth[0] = parcBuffer_Wrap(array, sizeof(array), 0, 2); + truth[1] = parcBuffer_Wrap(array, sizeof(array), 2, 3); + truth[2] = parcBuffer_Wrap(array, sizeof(array), 3, 6); + + CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(buffer); + CCNxTlvDictionary *dictionary = ccnxTlvDictionary_Create(10, 10); + + // put 3 buffers of {0x01, 0x02} and {0x03} and {0x04, 0x05, x06} on the list + int listkey = 1; + ccnxCodecTlvUtilities_PutAsListBuffer(decoder, dictionary, 0, 2, listkey); + ccnxCodecTlvUtilities_PutAsListBuffer(decoder, dictionary, 1, 1, listkey); + ccnxCodecTlvUtilities_PutAsListBuffer(decoder, dictionary, 2, 3, listkey); + + assertTrue(ccnxTlvDictionary_ListSize(dictionary, listkey) == 3, + "Wrong list size, got %zu expected %u", + ccnxTlvDictionary_ListSize(dictionary, listkey), 3); + + // now make sure they are right + for (int i = 0; i < ccnxTlvDictionary_ListSize(dictionary, listkey); i++) { + PARCBuffer *test = ccnxTlvDictionary_ListGetByType(dictionary, listkey, i); + assertNotNull(test, "Failed to get index %d", i); + + assertTrue(parcBuffer_Equals(truth[i], test), "Buffers not equal for index %d", i) + { + parcBuffer_Display(test, 3); + parcBuffer_Display(truth[i], 3); + } + } + + ccnxCodecTlvDecoder_Destroy(&decoder); + parcBuffer_Release(&truth[0]); + parcBuffer_Release(&truth[1]); + parcBuffer_Release(&truth[2]); + parcBuffer_Release(&buffer); + ccnxTlvDictionary_Release(&dictionary); +} + + + +LONGBOW_TEST_CASE(Global, ccnxCodecTlvUtilities_NestedEncode) +{ +// TODO: This test needs to be updated with V1 data. +// See BugzID: 3919 + + +// uint8_t metadata[] = { +// 0x00, 0x0B, 0x00, 17, // Object Metadata, length = 17 +// 0x00, 0x0C, 0x00, 0x01, // Object Type, length = 1 +// 0x04, // LINK +// 0x00, 0x0D, 0x00, 8, // Creation Time +// 0x00, 0x00, 0x01, 0x43, // 1,388,534,400,000 msec +// 0x4B, 0x19, 0x84, 0x00, +// }; +// +// +// PARCBuffer *truth = parcBuffer_Wrap(metadata, sizeof(metadata), 0, sizeof(metadata)); +// +// // now decode that snippit +// CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(truth); +// CCNxTlvDictionary *dictionary = ccnxTlvDictionary_Create(20, 20); +// ccnxCodecTlvDecoder_Advance(decoder, 4); +// ccnxCodecTlvUtilities_DecodeContainer(decoder, dictionary, _ccnxCodecSchemaV0MetadataDecoder_DecodeType); +// +// // the dictionary should now be ready for encoding +// ccnxCodecTlvDecoder_Destroy(&decoder); +// +// CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); +// ssize_t length = ccnxCodecTlvUtilities_NestedEncode(encoder, dictionary, 0x000B, ccnxCodecSchemaV0MetadataEncoder_Encode); +// +// assertTrue(length == sizeof(metadata), "Wrong size, got %zu expected %zu", length, sizeof(metadata)); +// +// ccnxCodecTlvEncoder_Finalize(encoder); +// PARCBuffer *test = ccnxCodecTlvEncoder_CreateBuffer(encoder); +// assertTrue(parcBuffer_Equals(test, truth), "Buffers do not match") +// { +// parcBuffer_Display(test, 3); +// parcBuffer_Display(truth, 3); +// } +// +// ccnxCodecTlvEncoder_Destroy(&encoder); +// ccnxTlvDictionary_Release(&dictionary); +// parcBuffer_Release(&truth); +// parcBuffer_Release(&test); +} + +LONGBOW_TEST_CASE(Global, ccnxCodecTlvUtilities_EncodeCustomList) +{ + uint8_t truthArray[] = { 0x00, 0x00, 0x00, 0x02, 0x01, 0x02, + 0x00, 0x01, 0x00, 0x01, 0x03, + 0x00, 0x02, 0x00, 0x03, 0x04, 0x05, 0x06 }; + + PARCBuffer *truth = parcBuffer_Wrap(truthArray, sizeof(truthArray), 0, sizeof(truthArray)); + + PARCBuffer *buffers[3]; + buffers[0] = parcBuffer_Wrap(truthArray, sizeof(truthArray), 4, 6); + buffers[1] = parcBuffer_Wrap(truthArray, sizeof(truthArray), 10, 11); + buffers[2] = parcBuffer_Wrap(truthArray, sizeof(truthArray), 15, 18); + + CCNxTlvDictionary *dictionary = ccnxTlvDictionary_Create(10, 10); + + // put 3 buffers of {0x01, 0x02} and {0x03} and {0x04, 0x05, x06} on the list + int listkey = 1; + ccnxTlvDictionary_PutListBuffer(dictionary, listkey, 2, buffers[2]); + ccnxTlvDictionary_PutListBuffer(dictionary, listkey, 1, buffers[1]); + ccnxTlvDictionary_PutListBuffer(dictionary, listkey, 0, buffers[0]); + + CCNxCodecTlvEncoder *encoder = ccnxCodecTlvEncoder_Create(); + ccnxCodecTlvUtilities_EncodeCustomList(encoder, dictionary, listkey); + + ccnxCodecTlvEncoder_Finalize(encoder); + PARCBuffer *test = ccnxCodecTlvEncoder_CreateBuffer(encoder); + + assertTrue(parcBuffer_Equals(test, truth), "Buffers not equal") + { + parcBuffer_Display(test, 3); + parcBuffer_Display(truth, 3); + } + + ccnxCodecTlvEncoder_Destroy(&encoder); + parcBuffer_Release(&buffers[0]); + parcBuffer_Release(&buffers[1]); + parcBuffer_Release(&buffers[2]); + parcBuffer_Release(&truth); + parcBuffer_Release(&test); + ccnxTlvDictionary_Release(&dictionary); +} + +// ==================================================================================== + +LONGBOW_TEST_FIXTURE(Local) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(rta_TlvUtilities); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-common/ccnx/common/codec/test/test_random_bytes b/libccnx-common/ccnx/common/codec/test/test_random_bytes Binary files differnew file mode 100644 index 00000000..33a80af5 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/test/test_random_bytes diff --git a/libccnx-common/ccnx/common/codec/test/test_random_bytes.sig b/libccnx-common/ccnx/common/codec/test/test_random_bytes.sig Binary files differnew file mode 100644 index 00000000..9c395ce0 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/test/test_random_bytes.sig diff --git a/libccnx-common/ccnx/common/codec/test/test_rsa.p12 b/libccnx-common/ccnx/common/codec/test/test_rsa.p12 Binary files differnew file mode 100644 index 00000000..471c4006 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/test/test_rsa.p12 diff --git a/libccnx-common/ccnx/common/codec/test/test_rsa_key.pem b/libccnx-common/ccnx/common/codec/test/test_rsa_key.pem new file mode 100644 index 00000000..6c502b15 --- /dev/null +++ b/libccnx-common/ccnx/common/codec/test/test_rsa_key.pem @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXAIBAAKBgQCn1pPF8XPGErX6ecXvGIvvqs0EAY+Ddz+xZqFauTkqsj4w+xH8 +V/yd0S938Kt3rWYsJsibUW9pvyYQBinuy7AsXpEOJEKN5nWgTgRDDD5MBnRnrYTD +6PTFlHPEnyWoQga/RRnimBw2oUNNm3EI4YLf4k8qPD0PNZKucCf70uQeJwIDAQAB +AoGAVOYPA/7aIGSQlu4IOKTDDG3qnM8pSEgG+PbAQgMVrspQ+TfXZj0ftLj++P3N +zpDw8P6BVUfBQs2FNG/ZwEhaiZVgJAl7cIAxJ9Ac+1oZYSgGyJfb3u9iWvkbMOoj +83Inx5yyN+Qmk5zceH4pOC5D5cDAuGGZ740Euv4o2/2O3qECQQDTmWZw021PvEbA +r18O1YfZGxO3zFCwFXCpnHvtSMbP+MXAG5Gt47wZt4Vx1rX9k78beeCUitwqp3d3 +ZI+YlUu3AkEAyw5wssQsJty/n2FL8DbJN3UzUhkcaCFYrKz3RtFye9wu+Bw0TxPC +3jhFVcynm3nH3ZJN0JsnsPnHXuoQToShEQJATXC51hb6zZC5UDGel348fo9zUvP6 +n8bo+ZoknL3izSBdtyYf1cUgBUVuGDCdYFWfPn4HXDXJx+6MQWzTRON21wJBAMZL +U8M/z94jtP3wBjiPR/Dggz2pSBRofDAkuVZvM13BqByjbnHK2oIocY1YTlWGl6fJ +ODR/UEODqS8HZOVIoAECQANcuvVnqDixSIl2ySZvydQytv4DKTbvE0nYSRroYIlJ +PTOBPy8ynIUkJwc2E1BsLl7V8gO62a5O0ntTwBMnPSQ= +-----END RSA PRIVATE KEY----- diff --git a/libccnx-common/ccnx/common/codec/test/testrig_Compare.c b/libccnx-common/ccnx/common/codec/test/testrig_Compare.c new file mode 100755 index 00000000..3dc73a1c --- /dev/null +++ b/libccnx-common/ccnx/common/codec/test/testrig_Compare.c @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Utilities used by the Schema unit tests to compare buffers + * + */ + +/** + * Compares an encoding buffer to linear memory + * + * Will assert if the memory does not compare. The encoding buffer will be finalized. + * Will assert if the encoder has an error. + * + * @param [in] encoder The encoding buffer to compare + * @param [in] length The length of linear memory + * @param [in] memory The "truth" memory to compare against + * + * Example: + * @code + * <#example#> + * @endcode + */ +void +testCompareEncoderToLinearMemory(CCNxCodecTlvEncoder *encoder, size_t length, uint8_t memory[length]) +{ + assertFalse(ccnxCodecTlvEncoder_HasError(encoder), "Encoder has error") + { + printf("ERROR: %s\n", ccnxCodecError_ToString(ccnxCodecTlvEncoder_GetError(encoder))); + } + + PARCBuffer *truth = parcBuffer_Wrap(memory, length, 0, length); + ccnxCodecTlvEncoder_Finalize(encoder); + PARCBuffer *buffer = ccnxCodecTlvEncoder_CreateBuffer(encoder); + + assertTrue(parcBuffer_Equals(buffer, truth), "buffers not equal") + { + printf("Expected\n"); + parcBuffer_Display(truth, 3); + + printf("Got this\n"); + parcBuffer_Display(buffer, 3); + } + + parcBuffer_Release(&truth); + parcBuffer_Release(&buffer); +} + +/** + * Compares an encoding buffer to a PARCBuffer + * + * Will assert if the memory does not compare. The encoding buffer will be finalized. + * Will assert if the encoder has an error. + * + * @param [in] encoder The encoding buffer to compare + * @param [in] buffer The buffer to compare to, must be setup to be read (i.e. flipped) + * + * Example: + * @code + * <#example#> + * @endcode + */ +void +testCompareEncoderToBuffer(CCNxCodecTlvEncoder *encoder, PARCBuffer *buffer) +{ + assertFalse(ccnxCodecTlvEncoder_HasError(encoder), "Encoder has error") + { + printf("ERROR: %s\n", ccnxCodecError_ToString(ccnxCodecTlvEncoder_GetError(encoder))); + } + + + uint8_t *linearMemory = parcByteArray_Array(parcBuffer_Array(buffer)); + size_t offset = parcBuffer_ArrayOffset(buffer) + parcBuffer_Position(buffer); + size_t length = parcBuffer_Remaining(buffer); + + testCompareEncoderToLinearMemory(encoder, length, linearMemory + offset); +} + diff --git a/libccnx-common/ccnx/common/codec/testdata/testdata_common.h b/libccnx-common/ccnx/common/codec/testdata/testdata_common.h new file mode 100644 index 00000000..ac1d0b8e --- /dev/null +++ b/libccnx-common/ccnx/common/codec/testdata/testdata_common.h @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +/* + * testdata_common.h + * TransportRTA + */ + +#ifndef TransportRTA_testdata_common_h +#define TransportRTA_testdata_common_h + +#include <ccnx/common/codec/ccnxCodec_Error.h> + +typedef struct tlv_extent { + uint16_t offset; + uint16_t length; +} TlvExtent; + +// Equal to { 0xFFFF, 0xFFFF } +extern const TlvExtent TlvExtentNotFound; + +/** + * Determine if two TlvExtent instances are equal. + * + * The following equivalence relations on non-null `TlvExtent` instances are maintained: + * + * * It is reflexive: for any non-null reference value x, `TlvExtent_Equals(x, x)` + * must return true. + * + * * It is symmetric: for any non-null reference values x and y, + * `tlvExtent_Equals(x, y)` must return true if and only if + * `tlvExtent_Equals(y, x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `tlvExtent_Equals(x, y)` returns true and + * `tlvExtent_Equals(y, z)` returns true, + * then `tlvExtent_Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple + * invocations of `tlvExtent_Equals(x, y)` consistently return true or + * consistently return false. + * + * * For any non-null reference value x, `tlvExtent_Equals(x, NULL)` must + * return false. + * + * @param a A pointer to a `TlvExtent` instance. + * @param b A pointer to a `TlvExtent` instance. + * @return true if the two `TlvExtent` instances are equal. + * + * Example: + * @code + * { + * TlvExtent *a = tlvExtent_Create(); + * TlvExtent *b = tlvExtent_Create(); + * + * if (tlvExtent_Equals(a, b)) { + * // true + * } else { + * // false + * } + * } + * @endcode + */ +bool tlvExtent_Equals(const TlvExtent *a, const TlvExtent *b); + +#define TRUTHTABLENAME(NAME) NAME ## _truthTableEntries + +// easy way to generate table entries, so long as you use the standard naming +// convention for the the TruthTableEntry array +#define TABLEENTRY(NAME, ERROR) { .testname = #NAME, .packet = NAME, .length = sizeof(NAME), .expectedError = ERROR, .entry = TRUTHTABLENAME(NAME) } + +typedef struct testrig_truth_table_entry { + bool wellKnownType; + + // is the wellKnownType in the body manifest? or the header? + bool bodyManifest; + + // if its a well known type, this is the manifest array index + // otherwise, its the unknown type value + int indexOrKey; + + TlvExtent extent; +} TruthTableEntry; + + +typedef struct testrig_truth_table { + const char *testname; + uint8_t *packet; + size_t length; + + CCNxCodecErrorCodes expectedError; + + // the array is terminated by a T_INVALID value + // for "arrayIndexOrTypeKey" + TruthTableEntry *entry; +} TruthTable; + +#endif diff --git a/libccnx-common/ccnx/common/codec/testdata/tlv_Schema.h b/libccnx-common/ccnx/common/codec/testdata/tlv_Schema.h new file mode 100755 index 00000000..ddd8df7d --- /dev/null +++ b/libccnx-common/ccnx/common/codec/testdata/tlv_Schema.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * This is from the version 0 codec. All the test vectors in this directory (e.g. interest_nameA.h) + * are encoded using these constants. These are no longer used for any functional code, only to interpret the test vectors. + * + */ + +#ifndef Libccnx_tlv_Schema_h +#define Libccnx_tlv_Schema_h + +#define T_INVALID 0xFFFF + +// not an actual type, but a virtual group +#define T_VIRTUAL 0xFFFE +#endif // Libccnx_tlv_Schema_h diff --git a/libccnx-common/ccnx/common/config.h.in b/libccnx-common/ccnx/common/config.h.in new file mode 100644 index 00000000..5d047cfc --- /dev/null +++ b/libccnx-common/ccnx/common/config.h.in @@ -0,0 +1,4 @@ +/* CPU Cache line size */ +#define LEVEL1_DCACHE_LINESIZE @LEVEL1_DCACHE_LINESIZE@ + +#define _GNU_SOURCE diff --git a/libccnx-common/ccnx/common/internal/ccnx_ChunkingFacadeV1.c b/libccnx-common/ccnx/common/internal/ccnx_ChunkingFacadeV1.c new file mode 100755 index 00000000..5af1b5ca --- /dev/null +++ b/libccnx-common/ccnx/common/internal/ccnx_ChunkingFacadeV1.c @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <LongBow/runtime.h> + +#include <ccnx/common/internal/ccnx_ChunkingFacadeV1.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_TlvDictionary.h> + +static void +_assertInvariants(const CCNxTlvDictionary *dictionary) +{ + assertNotNull(dictionary, "Parameter contentObjectDictionary must be non-null"); + trapIllegalValueIf(ccnxTlvDictionary_GetSchemaVersion(dictionary) != CCNxTlvDictionary_SchemaVersion_V1, + "Wrong schema version, expected %d got %d", + CCNxTlvDictionary_SchemaVersion_V1, + ccnxTlvDictionary_GetSchemaVersion(dictionary)); +} + +bool +ccnxChunkingFacadeV1_HasEndChunkNumber(const CCNxTlvDictionary *contentObjectDictionary) +{ + _assertInvariants(contentObjectDictionary); + return ccnxTlvDictionary_IsValueInteger(contentObjectDictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_ENDSEGMENT); +} + +uint64_t +ccnxChunkingFacadeV1_GetEndChunkNumber(const CCNxTlvDictionary *contentObjectDictionary) +{ + _assertInvariants(contentObjectDictionary); + return ccnxTlvDictionary_GetInteger(contentObjectDictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_ENDSEGMENT); +} + +bool +ccnxChunkingFacadeV1_SetEndChunkNumber(CCNxTlvDictionary *contentObjectDictionary, uint64_t endChunkNumber) +{ + _assertInvariants(contentObjectDictionary); + trapIllegalValueIf(!ccnxTlvDictionary_IsContentObject(contentObjectDictionary), "Dictionary is not a ContentObject"); + return ccnxTlvDictionary_PutInteger(contentObjectDictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_ENDSEGMENT, endChunkNumber); +} diff --git a/libccnx-common/ccnx/common/internal/ccnx_ChunkingFacadeV1.h b/libccnx-common/ccnx/common/internal/ccnx_ChunkingFacadeV1.h new file mode 100755 index 00000000..a4d2010c --- /dev/null +++ b/libccnx-common/ccnx/common/internal/ccnx_ChunkingFacadeV1.h @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file ccnx_ChunkingFacade.h + * @brief <#Brief Description#> + * + * <#Detailed Description#> + * + */ + +#ifndef libccnx_ccnx_ChunkingFacadeV1_h +#define libccnx_ccnx_ChunkingFacadeV1_h + +#include <ccnx/common/internal/ccnx_TlvDictionary.h> + +/** + * Determines if an EndChunkNumber exists in the metadata + * + * Compatible with V0 and V1 schemas. + * + * @param [in] contentObjectDictionary A dictionary to check + * + * @return false There is no EndChunkNumber specified or the dictinoary is not a ContentObject + * @return true There is an EndChunkNumber specified + * + * Example: + * @code + * static PARCElasticBuffer * + * _ccnxContentObjectFacade_CreateFinalBlockId(const CCNxTlvDictionary *contentObjectDictionary) + * { + * if (ccnxChunkingFacade_HasEndChunkNumber(contentObjectDictionary)) { + * uint64_t endChunkNumber = ccnxChunkingFacade_GetEndChunkNumber(contentObjectDictionary); + * PARCElasticBuffer *fbid = _ccnxContentObjectFacade_EncodeFinalBlockId(endChunkNumber); + * return fbid; + * } + * return NULL; + * } + * @endcode + */ +bool ccnxChunkingFacadeV1_HasEndChunkNumber(const CCNxTlvDictionary *contentObjectDictionary); + +/** + * Retrieves the end chunk number + * + * Retieves the end chunk number as an unsigned 64-bit integer. + * This function will trapIllegalValue if there is not an EndChunkNumber present. + * The EndChunkNumber is the chunk number of the last Content Object in a chunked series. + * + * @param [in] contentObjectDictionary A dictionary to check + * + * @return number The ending chunk number + * + * Example: + * @code + * static PARCElasticBuffer * + * _ccnxContentObjectFacade_CreateFinalBlockId(const CCNxTlvDictionary *contentObjectDictionary) + * { + * if (ccnxChunkingFacade_HasEndChunkNumber(contentObjectDictionary)) { + * uint64_t endChunkNumber = ccnxChunkingFacade_GetEndChunkNumber(contentObjectDictionary); + * PARCElasticBuffer *fbid = _ccnxContentObjectFacade_EncodeFinalBlockId(endChunkNumber); + * return fbid; + * } + * return NULL; + * } + * @endcode + */ +uint64_t ccnxChunkingFacadeV1_GetEndChunkNumber(const CCNxTlvDictionary *contentObjectDictionary); + +/** + * Sets the EndChunkNumber of a ContentObject + * + * The dictionary must be a ContentObject, otherwise this function will trapIllegalValue. + * If an EndChunkNumber already exits, will not update but return false. + * + * @param [in] contentObjectDictionary A dictionary to check + * @param [in] endChunkNumber The ending chunk number + * + * @return true The value was set in the dictionary + * @return false A failure (likely value already set) + * + * Example: + * @code + * { + * CCNxTlvDictionary *obj = ccnxContentObjectFacade_Create(...); + * ccnxChunkingFacade_SetEndChunkNumber(obj, 74); + * } + * @endcode + */ +bool ccnxChunkingFacadeV1_SetEndChunkNumber(CCNxTlvDictionary *contentObjectDictionary, uint64_t endChunkNumber); +#endif // libccnx_ccnx_ChunkingFacadeV1_h diff --git a/libccnx-common/ccnx/common/internal/ccnx_ContentObjectFacadeV1.c b/libccnx-common/ccnx/common/internal/ccnx_ContentObjectFacadeV1.c new file mode 100644 index 00000000..1ca0c5af --- /dev/null +++ b/libccnx-common/ccnx/common/internal/ccnx_ContentObjectFacadeV1.c @@ -0,0 +1,294 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> + +#include <LongBow/runtime.h> + +#include <parc/security/parc_SigningAlgorithm.h> + +#include <ccnx/common/internal/ccnx_ContentObjectFacadeV1.h> +#include <ccnx/common/internal/ccnx_ValidationFacadeV1.h> +#include <ccnx/common/internal/ccnx_ChunkingFacadeV1.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_TlvDictionary.h> + +#include <ccnx/common/codec/ccnxCodec_TlvEncoder.h> + +static void +_assertInvariants(const CCNxTlvDictionary *contentObjectDictionary) +{ + assertNotNull(contentObjectDictionary, "Dictionary is null"); + assertTrue(ccnxTlvDictionary_IsContentObject(contentObjectDictionary), "Dictionary is not a content object"); + assertTrue(ccnxTlvDictionary_GetSchemaVersion(contentObjectDictionary) == CCNxTlvDictionary_SchemaVersion_V1, + "Dictionary is wrong schema version, got %d expected %d", + ccnxTlvDictionary_GetSchemaVersion(contentObjectDictionary), CCNxTlvDictionary_SchemaVersion_V1); +} + +// ========================= +// Creation + +static CCNxTlvDictionary * +_ccnxContentObjectFacadeV1_CreateWithNameAndPayload(const CCNxName *name, // required + const CCNxPayloadType payloadType, // required + const PARCBuffer *payload) // may be null +{ + assertNotNull(name, "Parameter name must be non-null"); + + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateContentObject(); + + if (dictionary) { + ccnxTlvDictionary_PutName(dictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_NAME, name); + + if (payloadType != CCNxPayloadType_DATA) { + ccnxTlvDictionary_PutInteger(dictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_PAYLOADTYPE, payloadType); + } + + if (payload) { + ccnxTlvDictionary_PutBuffer(dictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_PAYLOAD, payload); + } + } else { + trapOutOfMemory("Could not allocate ContentObject"); + } + + return dictionary; +} + +static CCNxTlvDictionary * +_ccnxContentObjectFacadeV1_CreateWithPayload(const CCNxPayloadType payloadType, // required + const PARCBuffer *payload) // may be null +{ + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateContentObject(); + + if (dictionary) { + if (payloadType != CCNxPayloadType_DATA) { + ccnxTlvDictionary_PutInteger(dictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_PAYLOADTYPE, payloadType); + } + + if (payload) { + ccnxTlvDictionary_PutBuffer(dictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_PAYLOAD, payload); + } + } else { + trapOutOfMemory("Could not allocate ContentObject"); + } + + return dictionary; +} + +// ========================= +// Getters + +static CCNxName * +_ccnxContentObjectFacadeV1_GetName(const CCNxTlvDictionary *contentObjectDictionary) +{ + _assertInvariants(contentObjectDictionary); + return ccnxTlvDictionary_GetName(contentObjectDictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_NAME); +} + +static bool +_ccnxContentObjectFacadeV1_HasExpiryTime(const CCNxTlvDictionary *packetDictionary) +{ + if (ccnxTlvDictionary_IsValueInteger(packetDictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_EXPIRY_TIME)) { + return true; + } + return false; +} + +static uint64_t +_ccnxContentObjectFacadeV1_GetExpiryTime(const CCNxTlvDictionary *packetDictionary) +{ + if (ccnxTlvDictionary_IsValueInteger(packetDictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_EXPIRY_TIME)) { + return ccnxTlvDictionary_GetInteger(packetDictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_EXPIRY_TIME); + } + trapUnexpectedState("The dictionary does not contain an Expiry Time"); +} + +static bool +_ccnxContentObjectFacadeV1_HasPathLabel(const CCNxTlvDictionary *packetDictionary) +{ + if (ccnxTlvDictionary_IsValueInteger(packetDictionary, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_PathLabel)) { + return true; + } + return false; +} + + +static uint64_t +_ccnxContentObjectFacadeV1_GetPathLabel(const CCNxTlvDictionary *packetDictionary) +{ + if (ccnxTlvDictionary_IsValueInteger(packetDictionary, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_PathLabel)) { + return ccnxTlvDictionary_GetInteger(packetDictionary, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_PathLabel); + } + trapUnexpectedState("The dictionary does not contain a Path Label"); +} + +static PARCBuffer * +_ccnxContentObjectFacadeV1_GetPayload(const CCNxTlvDictionary *contentObjectDictionary) +{ + _assertInvariants(contentObjectDictionary); + return ccnxTlvDictionary_GetBuffer(contentObjectDictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_PAYLOAD); +} + +static CCNxPayloadType +_ccnxContentObjectFacadeV1_GetPayloadType(const CCNxTlvDictionary *contentObjectDictionary) +{ + CCNxPayloadType result = CCNxPayloadType_DATA; + + _assertInvariants(contentObjectDictionary); + if (ccnxTlvDictionary_IsValueInteger(contentObjectDictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_PAYLOADTYPE)) { + result = (CCNxPayloadType) ccnxTlvDictionary_GetInteger(contentObjectDictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_PAYLOADTYPE); + } + + return result; +} + +// ========================= +// Setters + +static bool +_ccnxContentObjectFacadeV1_SetSignature(CCNxTlvDictionary *contentObject, const PARCBuffer *keyId, + const PARCSignature *signature, const CCNxKeyLocator *keyLocator) +{ + bool result = false; + + CCNxTlvDictionary *contentObjectDictionary = (CCNxTlvDictionary *) contentObject; + + if (parcSignature_GetSigningAlgorithm(signature) == PARCSigningAlgorithm_RSA + && parcSignature_GetHashType(signature) == PARCCryptoHashType_SHA256) { + ccnxValidationRsaSha256_Set(contentObjectDictionary, keyId, keyLocator); + } else if (parcSignature_GetSigningAlgorithm(signature) == PARCSigningAlgorithm_HMAC + && parcSignature_GetHashType(signature) == PARCCryptoHashType_SHA256) { + ccnxValidationHmacSha256_Set(contentObjectDictionary, keyId); + } else { + trapNotImplemented("Have not implemented the signature parameters"); + } + + PARCBuffer *sigbits = parcSignature_GetSignature(signature); + + result = ccnxValidationFacadeV1_SetPayload(contentObjectDictionary, sigbits); + + return result; +} + +static PARCBuffer * +_ccnxContentObjectFacadeV1_GetKeyId(const CCNxTlvDictionary *contentObject) +{ + return ccnxValidationFacadeV1_GetKeyId(contentObject); +} + +static bool +_ccnxContentObjectFacadeV1_SetExpiryTime(CCNxTlvDictionary *contentObjectDictionary, uint64_t expiryTime) +{ + bool success = ccnxTlvDictionary_PutInteger(contentObjectDictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_EXPIRY_TIME, expiryTime); + trapUnexpectedStateIf(!success, "Could not set integer in dictionary"); + return success; +} + +static bool +_ccnxContentObjectFacadeV1_SetPathLabel(CCNxTlvDictionary *contentObjectDictionary, uint64_t pathLabel) +{ + bool success = ccnxTlvDictionary_PutInteger(contentObjectDictionary, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_PathLabel, pathLabel); + trapUnexpectedStateIf(!success, "Could not set integer in dictionary (path label)"); + return success; +} + +static bool +_ccnxContentObjectFacadeV1_SetPayload(CCNxTlvDictionary *contentObjectDictionary, CCNxPayloadType payloadType, const PARCBuffer *payload) +{ + bool result = false; + + if (payload != NULL) { + PARCBuffer *originalPayload = _ccnxContentObjectFacadeV1_GetPayload(contentObjectDictionary); + + result = ccnxTlvDictionary_PutBuffer(contentObjectDictionary, + CCNxCodecSchemaV1TlvDictionary_MessageFastArray_PAYLOAD, payload); + + if (result) { + if (_ccnxContentObjectFacadeV1_GetPayloadType(contentObjectDictionary) != payloadType) { + ccnxTlvDictionary_PutInteger(contentObjectDictionary, + CCNxCodecSchemaV1TlvDictionary_MessageFastArray_PAYLOADTYPE, payloadType); + } + + if (originalPayload != NULL) { + parcBuffer_Release(&originalPayload); + } + } + } + + return result; +} + +// ========================= +// Miscellaneous functions + +static bool +_ccnxContentObjectFacadeV1_Equals(const CCNxTlvDictionary *objectA, const CCNxTlvDictionary *objectB) +{ + return ccnxTlvDictionary_Equals(objectA, objectB); +} + +static char * +_ccnxContentObjectFacadeV1_ToString(const CCNxTlvDictionary *contentObjectDictionary) +{ + trapNotImplemented("_ccnxContentObjectFacadeV1_ToString(): not yet implemented"); +} + +static void +_ccnxContentObjectFacadeV1_Display(const CCNxTlvDictionary *contentObjectDictionary, size_t indentation) +{ + _assertInvariants(contentObjectDictionary); + ccnxTlvDictionary_Display(contentObjectDictionary, (unsigned) indentation); +} + +/** + * `CCNxContentObjectFacadeV1_Implementation` is the structure containing the pointers to the + * V1 schema ContentObject implementation. + */ +CCNxContentObjectInterface CCNxContentObjectFacadeV1_Implementation = { + .description = "CCNxContentObjectFacadeV1_Implementation", + + .createWithNameAndPayload = &_ccnxContentObjectFacadeV1_CreateWithNameAndPayload, + .createWithPayload = &_ccnxContentObjectFacadeV1_CreateWithPayload, + + .setSignature = &_ccnxContentObjectFacadeV1_SetSignature, + .getKeyId = &_ccnxContentObjectFacadeV1_GetKeyId, + + .getName = &_ccnxContentObjectFacadeV1_GetName, + .getPayload = &_ccnxContentObjectFacadeV1_GetPayload, + .setPayload = &_ccnxContentObjectFacadeV1_SetPayload, + .getPayloadType = &_ccnxContentObjectFacadeV1_GetPayloadType, + + .getFinalChunkNumber = &ccnxChunkingFacadeV1_GetEndChunkNumber, + .setFinalChunkNumber = &ccnxChunkingFacadeV1_SetEndChunkNumber, + .hasFinalChunkNumber = &ccnxChunkingFacadeV1_HasEndChunkNumber, + + .getExpiryTime = &_ccnxContentObjectFacadeV1_GetExpiryTime, + .setExpiryTime = &_ccnxContentObjectFacadeV1_SetExpiryTime, + .hasExpiryTime = &_ccnxContentObjectFacadeV1_HasExpiryTime, + + .getPathLabel = &_ccnxContentObjectFacadeV1_GetPathLabel, + .setPathLabel = &_ccnxContentObjectFacadeV1_SetPathLabel, + .hasPathLabel = &_ccnxContentObjectFacadeV1_HasPathLabel, + + .toString = &_ccnxContentObjectFacadeV1_ToString, + .display = &_ccnxContentObjectFacadeV1_Display, + .equals = &_ccnxContentObjectFacadeV1_Equals, + + .assertValid = &_assertInvariants, +}; diff --git a/libccnx-common/ccnx/common/internal/ccnx_ContentObjectFacadeV1.h b/libccnx-common/ccnx/common/internal/ccnx_ContentObjectFacadeV1.h new file mode 100755 index 00000000..d95c56b3 --- /dev/null +++ b/libccnx-common/ccnx/common/internal/ccnx_ContentObjectFacadeV1.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file ccnx_ContentObjectFacade.h + * @ingroup ContentObject + * @brief This facade is used to access fields within an RTA-encoded content object. + * + * Content objects are encoded and transmitted through the transport stack before being sent over the wire. + * This facade acts an interface to this transport-specific encoding of the content object. It enables + * the user to directly access fields within the content object without having any knowledge about the + * particular schema-specific encoding. + * + */ +#ifndef libccnx_ccnx_ContentObjectFacadeV1_h +#define libccnx_ccnx_ContentObjectFacadeV1_h + +#include <ccnx/common/internal/ccnx_ContentObjectInterface.h> + +extern CCNxContentObjectInterface CCNxContentObjectFacadeV1_Implementation; + +#endif // libccnx_ccnx_ContentObjectFacadeV1_h diff --git a/libccnx-common/ccnx/common/internal/ccnx_ContentObjectInterface.c b/libccnx-common/ccnx/common/internal/ccnx_ContentObjectInterface.c new file mode 100755 index 00000000..ac33c2ab --- /dev/null +++ b/libccnx-common/ccnx/common/internal/ccnx_ContentObjectInterface.c @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +#include <config.h> + +#include <LongBow/runtime.h> + +#include <ccnx/common/internal/ccnx_ContentObjectInterface.h> + + +CCNxContentObjectInterface * +ccnxContentObjectInterface_GetInterface(const CCNxTlvDictionary *dictionary) +{ + assertTrue(ccnxTlvDictionary_IsContentObject(dictionary), "Expected a ContentObject"); + + CCNxContentObjectInterface *impl = (CCNxContentObjectInterface *) ccnxTlvDictionary_GetMessageInterface(dictionary); + + if (impl == NULL) { + // If we're here, we need to update the implementation pointer. + // Figure out what the typeImplementation should be, based on the attributes we know. + int schemaVersion = ccnxTlvDictionary_GetSchemaVersion(dictionary); + + switch (schemaVersion) { + case CCNxTlvDictionary_SchemaVersion_V1: + impl = &CCNxContentObjectFacadeV1_Implementation; + break; + default: + trapUnexpectedState("Unknown SchemaVersion encountered in ccnxContentObjectInterface_GetInterface()"); + break; + } + + if (impl != NULL) { + // The cast to (CCNxTlvDictionary *) is to break the const. + ccnxTlvDictionary_SetMessageInterface((CCNxTlvDictionary *) dictionary, (CCNxMessageInterface *) impl); + } + } + + return impl; +} diff --git a/libccnx-common/ccnx/common/internal/ccnx_ContentObjectInterface.h b/libccnx-common/ccnx/common/internal/ccnx_ContentObjectInterface.h new file mode 100644 index 00000000..c2d67680 --- /dev/null +++ b/libccnx-common/ccnx/common/internal/ccnx_ContentObjectInterface.h @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @brief A structure of functions representing a ContentObject implementation. + * + * The underlying implementation should support the CCNxContentObject API. + * + */ + +#ifndef CCNx_Common_ccnx_internal_ContentObjectInterface_h +#define CCNx_Common_ccnx_internal_ContentObjectInterface_h + +#include <parc/algol/parc_Buffer.h> + +#include <ccnx/common/internal/ccnx_TlvDictionary.h> + +#include <ccnx/common/ccnx_Name.h> +#include <ccnx/common/ccnx_KeyLocator.h> +#include <ccnx/common/ccnx_PayloadType.h> + +typedef struct ccnx_contentobject_interface { + char *description; // A human-readable label for this implementation + + /** @see ccnxContentObject_CreateWithNameAndPayload */ + CCNxTlvDictionary *(*createWithNameAndPayload)(const CCNxName * contentName, + const CCNxPayloadType contentType, + const PARCBuffer * payload); + + /** @see ccnxContentObject_CreateWithPayload */ + CCNxTlvDictionary *(*createWithPayload)(const CCNxPayloadType contentType, + const PARCBuffer * payload); + + /** @see ccnxContentObject_GetName */ + CCNxName *(*getName)(const CCNxTlvDictionary * dict); + + /** @see ccnxContentObject_SetSignature */ + bool (*setSignature)(CCNxTlvDictionary *dict, + const PARCBuffer *keyId, + const PARCSignature *signature, + const CCNxKeyLocator *keyLocator); + + /** @see ccnxContentObject_GetKeyId */ + PARCBuffer *(*getKeyId)(const CCNxTlvDictionary * dict); + + /** @see ccnxContentObject_GetPayload */ + PARCBuffer *(*getPayload)(const CCNxTlvDictionary * dict); + + /** @see ccnxContentObject_GetPayloadType */ + CCNxPayloadType (*getPayloadType)(const CCNxTlvDictionary *dict); + + /** @see ccnxContentObject_SetPayload */ + bool (*setPayload)(CCNxTlvDictionary *dict, CCNxPayloadType payloadType, const PARCBuffer *payload); + + /** @see ccnxContentObject_SetFinalChunkNumber */ + bool (*setFinalChunkNumber)(CCNxTlvDictionary *dict, const uint64_t finalChunkNumber); + + /** @see ccnxContentObject_GetFinalChunkNumber */ + uint64_t (*getFinalChunkNumber)(const CCNxTlvDictionary *dict); + + /** @see ccnxContentObject_HasFinalChunkNumber */ + bool (*hasFinalChunkNumber)(const CCNxTlvDictionary *dict); + + /** @see ccnxContentObject_SetExpiryTime */ + bool (*setExpiryTime)(CCNxTlvDictionary *dict, const uint64_t expiryTime); + + /** @see ccnxContentObject_GetExpiryTime */ + uint64_t (*getExpiryTime)(const CCNxTlvDictionary *dict); + + /** @see ccnxContentObject_HasExpiryTime */ + bool (*hasExpiryTime)(const CCNxTlvDictionary *dict); + + /** @see ccnxContentObject_Equals */ + bool (*equals)(const CCNxTlvDictionary *objectA, const CCNxTlvDictionary *objectB); + + /** @see ccnxContentObject_AssertValid */ + void (*assertValid)(const CCNxTlvDictionary *dict); + + /** @see ccnxContentObject_ToString */ + char *(*toString)(const CCNxTlvDictionary * dict); + + /** @see ccnxContentObject_CreateWithPayload */ + void (*display)(const CCNxTlvDictionary *interestDictionary, size_t indentation); + + bool (*setPathLabel)(CCNxTlvDictionary *dict, const uint64_t pathLabel); + uint64_t (*getPathLabel)(const CCNxTlvDictionary *dict); + bool (*hasPathLabel)(const CCNxTlvDictionary *dict); +} CCNxContentObjectInterface; + +/** + * The SchemaV1 ContentObject implementaton + */ +extern CCNxContentObjectInterface CCNxContentObjectFacadeV1_Implementation; + +/** + * Given a CCNxTlvDictionary representing a CCNxContentObject, return the address of the CCNxContentObjectInterface + * instance that should be used to access the ContentObject. This will also update the CCNxTlvDictionary's implementation + * pointer for future references. + * + * @param contentDictionary - a {@link CCNxTlvDictionary} representing a CCNxContentObject. + * @return the address of the `CCNxContentObjectInterface` instance that should be used to access the CCNxContentObject. + * + * Example: + * @code + * { + * CCNxName *name = ccnxName_CreateFromCString("lci:/boose/roo/pie"); + * + * CCNxContentObject *contentObjectV0 = + * ccnxContentObject_CreateWithImplAndPayload(&CCNxContentObjectFacadeV0_Implementation, + * name, + * CCNxPayloadType_DATA, + * NULL); + * + * CCNxContentObject *contentObjectV1 = + * ccnxContentObject_CreateWithImplAndPayload(&CCNxContentObjectFacadeV1_Implementation, + * name, + * CCNxPayloadType_DATA, + * NULL); + * + * assertTrue(ccnxContentObjectInterface_GetInterface(contentObjectV0) == &CCNxContentObjectFacadeV0_Implementation, + * "Expected V0 Implementation"); + * + * assertTrue(ccnxContentObjectInterface_GetInterface(contentObjectV1) == &CCNxContentObjectFacadeV1_Implementation, + * "Expected V1 Implementation"); + * + * ccnxName_Release(&name); + * ccnxContentObject_Release(&contentObjectV0); + * ccnxContentObject_Release(&contentObjectV1); + * } + * @endcode + */ +CCNxContentObjectInterface *ccnxContentObjectInterface_GetInterface(const CCNxTlvDictionary *contentDictionary); +#endif diff --git a/libccnx-common/ccnx/common/internal/ccnx_InterestDefault.c b/libccnx-common/ccnx/common/internal/ccnx_InterestDefault.c new file mode 100755 index 00000000..a0bb134a --- /dev/null +++ b/libccnx-common/ccnx/common/internal/ccnx_InterestDefault.c @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +#include "ccnx_InterestDefault.h" + +const uint32_t CCNxInterestDefault_LifetimeMilliseconds = -1; +const uint32_t CCNxInterestDefault_HopLimit = 255; +const CCNxInterestPayloadIdMethod CCNxInterestDefault_PayloadIdMethod = CCNxInterestPayloadIdMethod_App; + diff --git a/libccnx-common/ccnx/common/internal/ccnx_InterestDefault.h b/libccnx-common/ccnx/common/internal/ccnx_InterestDefault.h new file mode 100755 index 00000000..ebfc859a --- /dev/null +++ b/libccnx-common/ccnx/common/internal/ccnx_InterestDefault.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file ccnx_InterestDefault.h + * @brief Default values for various fields in an Interest + * + * Declares several constants that may be used when creating an Interest with default values. + * These may be used in a V0 or V1 Interest. + * + */ + +#ifndef __CCNx_Common__ccnx_InterestDefault__ +#define __CCNx_Common__ccnx_InterestDefault__ + +#include <inttypes.h> +#include <ccnx/common/internal/ccnx_InterestPayloadIdMethod.h> + +/** + * May be used in calls to create an Interest with the default lifetime. An Interest with + * the default lifetime does not encode the field in the wire format. + */ +extern const uint32_t CCNxInterestDefault_LifetimeMilliseconds; + +/** + * May be used in calls to create an Interest with the default hoplimit. + */ +extern const uint32_t CCNxInterestDefault_HopLimit; + +/** + * May be used in calls to create an Interest with the default payload id method. + * + * The PayloadIdMethod is a field inside the Interest that describes how the InterestPayloadId in the Name + * was calcuated. + */ +extern const CCNxInterestPayloadIdMethod CCNxInterestDefault_PayloadIdMethod; + +#endif /* defined(__CCNx_Common__ccnx_InterestDefault__) */ diff --git a/libccnx-common/ccnx/common/internal/ccnx_InterestFacadeV1.c b/libccnx-common/ccnx/common/internal/ccnx_InterestFacadeV1.c new file mode 100644 index 00000000..5fca3cea --- /dev/null +++ b/libccnx-common/ccnx/common/internal/ccnx_InterestFacadeV1.c @@ -0,0 +1,295 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <LongBow/runtime.h> + +#include <ccnx/common/internal/ccnx_InterestFacadeV1.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_TlvDictionary.h> +#include <ccnx/common/internal/ccnx_InterestDefault.h> + + +// ===================== + +static void +_assertInvariants(const CCNxTlvDictionary *interestDictionary) +{ + assertNotNull(interestDictionary, "Dictionary is null"); + assertTrue((ccnxTlvDictionary_IsInterest(interestDictionary) || + ccnxTlvDictionary_IsInterestReturn(interestDictionary)), "Dictionary is not an interest"); + assertTrue(ccnxTlvDictionary_GetSchemaVersion(interestDictionary) == CCNxTlvDictionary_SchemaVersion_V1, + "Dictionary is wrong schema Interest, got %d expected %d", + ccnxTlvDictionary_GetSchemaVersion(interestDictionary), CCNxTlvDictionary_SchemaVersion_V1); +} + +static uint32_t +_fetchUint32(const CCNxTlvDictionary *interestDictionary, uint32_t key, uint32_t defaultValue) +{ + if (ccnxTlvDictionary_IsValueInteger(interestDictionary, key)) { + return (uint32_t) ccnxTlvDictionary_GetInteger(interestDictionary, key); + } + return defaultValue; +} + +static bool +_ccnxInterestFacadeV1_SetContentObjectHashRestriction(CCNxTlvDictionary *interestDictionary, const PARCBuffer *contentObjectHash) +{ + _assertInvariants(interestDictionary); + PARCCryptoHash *hash = parcCryptoHash_Create(PARCCryptoHashType_SHA256, contentObjectHash); + bool result = ccnxTlvDictionary_PutObject(interestDictionary, + CCNxCodecSchemaV1TlvDictionary_MessageFastArray_OBJHASH_RESTRICTION, + hash); + parcCryptoHash_Release(&hash); + return result; +} + +// forward declaration. Body below, in Getters. +static CCNxName * +_ccnxInterestFacadeV1_GetName(const CCNxTlvDictionary *interestDictionary); + +static bool +_ccnxInterestFacadeV1_SetPayloadWithId(CCNxTlvDictionary *interestDictionary, const PARCBuffer *payload, const CCNxInterestPayloadId *payloadId) +{ + bool result = false; + + if (payload) { + result = ccnxTlvDictionary_PutBuffer(interestDictionary, + CCNxCodecSchemaV1TlvDictionary_MessageFastArray_PAYLOAD, payload); + if (payloadId != NULL) { + CCNxName *name = _ccnxInterestFacadeV1_GetName(interestDictionary); + ccnxName_Append(name, ccnxInterestPayloadId_GetNameSegment(payloadId)); + } + } + return result; +} + +static bool +_ccnxInterestFacadeV1_SetPayloadAndId(CCNxTlvDictionary *interestDictionary, const PARCBuffer *payload) +{ + bool result = false; + + if (payload) { + CCNxInterestPayloadId *pid = ccnxInterestPayloadId_CreateAsSHA256Hash(payload); + _ccnxInterestFacadeV1_SetPayloadWithId(interestDictionary, payload, pid); + ccnxInterestPayloadId_Release(&pid); + } + return result; +} + +static bool +_ccnxInterestFacadeV1_SetPayload(CCNxTlvDictionary *interestDictionary, const PARCBuffer *payload) +{ + bool result = false; + + if (payload) { + _ccnxInterestFacadeV1_SetPayloadWithId(interestDictionary, payload, NULL); + } + return result; +} + +static bool +_ccnxInterestFacadeV1_SetLifetime(CCNxTlvDictionary *interestDictionary, uint32_t lifetimeInMillis) +{ + return ccnxTlvDictionary_PutInteger(interestDictionary, + CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_InterestLifetime, lifetimeInMillis); +} + +static bool +_ccnxInterestFacadeV1_SetKeyIdRestriction(CCNxTlvDictionary *interestDictionary, const PARCBuffer *keyId) +{ + _assertInvariants(interestDictionary); + PARCCryptoHash *hash = parcCryptoHash_Create(PARCCryptoHashType_SHA256, keyId); + bool result = ccnxTlvDictionary_PutObject(interestDictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_KEYID_RESTRICTION, hash); + parcCryptoHash_Release(&hash); + return result; +} + +static bool +_ccnxInterestFacadeV1_SetHopLimit(CCNxTlvDictionary *interestDictionary, uint32_t hopLimit) +{ + _assertInvariants(interestDictionary); + return ccnxTlvDictionary_PutInteger(interestDictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_HOPLIMIT, hopLimit); +} + +// ===================== +// Getters + +static CCNxName * +_ccnxInterestFacadeV1_GetName(const CCNxTlvDictionary *interestDictionary) +{ + int key = CCNxCodecSchemaV1TlvDictionary_MessageFastArray_NAME; + _assertInvariants(interestDictionary); + if (ccnxTlvDictionary_IsValueName(interestDictionary, key)) { + return ccnxTlvDictionary_GetName(interestDictionary, key); + } + return NULL; +} + +static uint32_t +_ccnxInterestFacadeV1_GetLifetime(const CCNxTlvDictionary *interestDictionary) +{ + _assertInvariants(interestDictionary); + return _fetchUint32(interestDictionary, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_InterestLifetime, CCNxInterestDefault_LifetimeMilliseconds); +} + +static PARCBuffer * +_ccnxInterestFacadeV1_GetKeyIdRestriction(const CCNxTlvDictionary *interestDictionary) +{ + _assertInvariants(interestDictionary); + PARCCryptoHash *hash = ccnxTlvDictionary_GetObject(interestDictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_KEYID_RESTRICTION); + if (hash != NULL) { + return parcCryptoHash_GetDigest(hash); + } else { + return NULL; + } +} + +static PARCBuffer * +_ccnxInterestFacadeV1_GetContentObjectHashRestriction(const CCNxTlvDictionary *interestDictionary) +{ + _assertInvariants(interestDictionary); + PARCCryptoHash *hash = ccnxTlvDictionary_GetObject(interestDictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_OBJHASH_RESTRICTION); + if (hash != NULL) { + return parcCryptoHash_GetDigest(hash); + } else { + return NULL; + } +} + +static PARCBuffer * +_ccnxInterestFacadeV1_GetPayload(const CCNxTlvDictionary *interestDictionary) +{ + _assertInvariants(interestDictionary); + return ccnxTlvDictionary_GetBuffer(interestDictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_PAYLOAD); +} + +static uint32_t +_ccnxInterestFacadeV1_GetHopLimit(const CCNxTlvDictionary *interestDictionary) +{ + _assertInvariants(interestDictionary); + return _fetchUint32(interestDictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_HOPLIMIT, CCNxInterestDefault_HopLimit); +} + +// ===================== +// Miscellaneous + +static void +_ccnxInterestFacadeV1_AssertValid(const CCNxTlvDictionary *interestDictionary) +{ + _assertInvariants(interestDictionary); + assertTrue(ccnxTlvDictionary_IsValueName(interestDictionary, + CCNxCodecSchemaV1TlvDictionary_MessageFastArray_NAME), "Name field is not a name"); +} + +static bool +_ccnxInterestFacadeV1_Equals(const CCNxTlvDictionary *a, const CCNxTlvDictionary *b) +{ + return ccnxTlvDictionary_Equals(a, b); +} + +static void +_ccnxInterestFacadeV1_Display(const CCNxTlvDictionary *interestDictionary, size_t indentation) +{ + _assertInvariants(interestDictionary); + ccnxTlvDictionary_Display(interestDictionary, (unsigned) indentation); +} + + +// ===================== +// Creation + +static CCNxTlvDictionary * +_ccnxInterestFacadeV1_Create(const CCNxName *name, // required + const uint32_t lifetimeMilliseconds, // may use DefaultLimetimeMilliseconds + const PARCBuffer *keyId, // may be NULL + const PARCBuffer *contentObjectHash, // may be NULL + const uint32_t hopLimit) // may be DefaultHopLimit +{ + assertNotNull(name, "Parameter name must be non-null"); + + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + + if (dictionary) { + ccnxTlvDictionary_PutName(dictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_NAME, name); + + if (lifetimeMilliseconds != CCNxInterestDefault_LifetimeMilliseconds) { + _ccnxInterestFacadeV1_SetLifetime(dictionary, lifetimeMilliseconds); + } + + if (keyId) { + _ccnxInterestFacadeV1_SetKeyIdRestriction(dictionary, keyId); + } + + if (contentObjectHash) { + _ccnxInterestFacadeV1_SetContentObjectHashRestriction(dictionary, contentObjectHash); + } + + if (hopLimit != CCNxInterestDefault_HopLimit) { + _ccnxInterestFacadeV1_SetHopLimit(dictionary, hopLimit); + } + } else { + trapOutOfMemory("Could not allocate an Interest"); + } + + return dictionary; +} + +static CCNxTlvDictionary * +_ccnxInterestFacadeV1_CreateSimple(const CCNxName *name) +{ + return _ccnxInterestFacadeV1_Create(name, + CCNxInterestDefault_LifetimeMilliseconds, + NULL, // keyid + NULL, // content object hash + CCNxInterestDefault_HopLimit); +} + +CCNxInterestInterface CCNxInterestFacadeV1_Implementation = { + .description = "CCNxInterestFacadeV1_Implementation", + + .createSimple = &_ccnxInterestFacadeV1_CreateSimple, + .create = &_ccnxInterestFacadeV1_Create, + + .getName = &_ccnxInterestFacadeV1_GetName, + + .setContentObjectHashRestriction = &_ccnxInterestFacadeV1_SetContentObjectHashRestriction, + .getContentObjectHashRestriction = &_ccnxInterestFacadeV1_GetContentObjectHashRestriction, + + .setLifetime = &_ccnxInterestFacadeV1_SetLifetime, + .getLifetime = &_ccnxInterestFacadeV1_GetLifetime, + + .setKeyIdRestriction = &_ccnxInterestFacadeV1_SetKeyIdRestriction, + .getKeyIdRestriction = &_ccnxInterestFacadeV1_GetKeyIdRestriction, + + .getHopLimit = &_ccnxInterestFacadeV1_GetHopLimit, + .setHopLimit = &_ccnxInterestFacadeV1_SetHopLimit, + + .getPayload = &_ccnxInterestFacadeV1_GetPayload, + + .setPayload = &_ccnxInterestFacadeV1_SetPayload, + .setPayloadAndId = &_ccnxInterestFacadeV1_SetPayloadAndId, + .setPayloadWithId = &_ccnxInterestFacadeV1_SetPayloadWithId, + + .toString = NULL, + .equals = &_ccnxInterestFacadeV1_Equals, + .display = &_ccnxInterestFacadeV1_Display, + + .assertValid = &_ccnxInterestFacadeV1_AssertValid, +}; diff --git a/libccnx-common/ccnx/common/internal/ccnx_InterestFacadeV1.h b/libccnx-common/ccnx/common/internal/ccnx_InterestFacadeV1.h new file mode 100755 index 00000000..29739f2c --- /dev/null +++ b/libccnx-common/ccnx/common/internal/ccnx_InterestFacadeV1.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file ccnx_InterestFacadeV1.h + * @ingroup Interest + * @brief A CCN Interest facade for an Interest over a CCNxTlvDictionary, using v1 schema. + * + */ +#ifndef libccnx_ccnx_InterestFacadeV1_h +#define libccnx_ccnx_InterestFacadeV1_h + +#include <ccnx/common/internal/ccnx_InterestInterface.h> + +extern CCNxInterestInterface CCNxInterestFacadeV1_Implementation; + +#endif // libccnx_ccnx_InterestFacadeV1_h diff --git a/libccnx-common/ccnx/common/internal/ccnx_InterestInterface.c b/libccnx-common/ccnx/common/internal/ccnx_InterestInterface.c new file mode 100755 index 00000000..a507d69d --- /dev/null +++ b/libccnx-common/ccnx/common/internal/ccnx_InterestInterface.c @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ +#include <config.h> + +#include <LongBow/runtime.h> + +#include <ccnx/common/internal/ccnx_InterestInterface.h> + +CCNxInterestInterface * +ccnxInterestInterface_GetInterface(const CCNxTlvDictionary *dictionary) +{ + assertTrue((ccnxTlvDictionary_IsInterest(dictionary) || + ccnxTlvDictionary_IsInterestReturn(dictionary)), "Expected an Interest or InterestReturn"); + + CCNxInterestInterface *impl = (CCNxInterestInterface *) ccnxTlvDictionary_GetMessageInterface(dictionary); + + if (impl == NULL) { + // If we're here, we need to update the interface pointer. + // Figure out what the typeInterface should be, based on the attributes we know. + int schemaVersion = ccnxTlvDictionary_GetSchemaVersion(dictionary); + + switch (schemaVersion) { + case CCNxTlvDictionary_SchemaVersion_V1: + impl = &CCNxInterestFacadeV1_Implementation; + break; + default: + trapUnexpectedState("Unknown SchemaVersion encountered in ccnxInterestInterface_GetInterface()"); + break; + } + + if (impl == NULL) { + // The cast to (CCNxTlvDictionary *) is to break the const. + ccnxTlvDictionary_SetMessageInterface((CCNxTlvDictionary *) dictionary, (CCNxMessageInterface *) impl); + } + } + return impl; +} + diff --git a/libccnx-common/ccnx/common/internal/ccnx_InterestInterface.h b/libccnx-common/ccnx/common/internal/ccnx_InterestInterface.h new file mode 100644 index 00000000..bb78dfc3 --- /dev/null +++ b/libccnx-common/ccnx/common/internal/ccnx_InterestInterface.h @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @brief A structure of functions representing an Interest implementation. + * + * The underlying implementation should support the CCNxInterest API. + * + */ + +#ifndef CCNx_Common_ccnx_internal_InterestInterface_h +#define CCNx_Common_ccnx_internal_InterestInterface_h + +#include <ccnx/common/internal/ccnx_TlvDictionary.h> + +#include <ccnx/common/internal/ccnx_InterestPayloadIdMethod.h> +#include <ccnx/common/ccnx_InterestPayloadId.h> + +typedef struct ccnx_interest_interface { + /** A human-readable label for this implementation */ + char *description; + + /** @see ccnxInterest_Create */ + CCNxTlvDictionary *(*create)(const CCNxName * name, // required + const uint32_t lifetimeMilliseconds, // may use DefaultLimetimeMilliseconds + const PARCBuffer * keyId, // may be NULL + const PARCBuffer * contentObjectHash, // may be NULL + const uint32_t hopLimit); + + /** @see ccnxInterest_CreateSimple */ + CCNxTlvDictionary *(*createSimple)(const CCNxName * name); + + /** @see ccnxInterest_GetName */ + CCNxName *(*getName)(const CCNxTlvDictionary * dict); + + /** @see ccnxInterest_SetLifetime */ + bool (*setLifetime)(CCNxTlvDictionary *dict, uint32_t lifetime); + + /** @see ccnxInterest_GetLifetime */ + uint32_t (*getLifetime)(const CCNxTlvDictionary *dict); + + /** @see ccnxInterest_SetHopLimit */ + bool (*setHopLimit)(CCNxTlvDictionary *dict, uint32_t hopLimit); + + /** @see ccnxInterest_GetHopLimit */ + uint32_t (*getHopLimit)(const CCNxTlvDictionary *dict); + + /** @see ccnxInterest_SetKeyIdRestriction */ + bool (*setKeyIdRestriction)(CCNxTlvDictionary *dict, const PARCBuffer *keyId); + + /** @see ccnxInterest_GetKeyIdRestriction */ + PARCBuffer *(*getKeyIdRestriction)(const CCNxTlvDictionary * dict); + + /** @see ccnxInterest_SetPayload */ + bool (*setPayload)(CCNxTlvDictionary *dict, const PARCBuffer *payload); + + /** @see ccnxInterest_SetPayloadAndId */ + bool (*setPayloadAndId)(CCNxTlvDictionary *dict, const PARCBuffer *payload); + + /** @see ccnxInterest_SetPayloadWithId */ + bool (*setPayloadWithId)(CCNxTlvDictionary *dict, const PARCBuffer *payload, const CCNxInterestPayloadId *id); + + /** @see ccnxInterest_GetPayload */ + PARCBuffer *(*getPayload)(const CCNxTlvDictionary * dict); + + /** @see ccnxInterest_SetContentObjectHashRestriction */ + bool (*setContentObjectHashRestriction)(CCNxTlvDictionary *dict, const PARCBuffer *contentObjectHash); + + /** @see ccnxInterest_GetContentObjectHashRestriction */ + PARCBuffer *(*getContentObjectHashRestriction)(const CCNxTlvDictionary * dict); + + /** @see ccnxInterest_Equals */ + bool (*equals)(const CCNxTlvDictionary *objectA, const CCNxTlvDictionary *objectB); + /** @see ccnxInterest_AssertValid */ + void (*assertValid)(const CCNxTlvDictionary *dict); + + /** @see ccnxInterest_ToString */ + char *(*toString)(const CCNxTlvDictionary * dict); + /** @see ccnxInterest_Display */ + void (*display)(const CCNxTlvDictionary *interestDictionary, size_t indentation); +} CCNxInterestInterface; + + +/** + * The SchemaV1 Interest implementaton + */ +extern CCNxInterestInterface CCNxInterestFacadeV1_Implementation; + +/** + * Given a CCNxTlvDictionary representing a CCNxInterest, return the address of the CCNxInterestInterface + * instance that should be used to access the Interest. This will also update the CCNxTlvDictionary's interface + * pointer for future references. + * + * @param interestDictionary - a {@link CCNxTlvDictionary} representing a CCNxInterest. + * @return the address of the `CCNxContentObjectInterface` instance that should be used to access the CCNxInterest. + * + * Example: + * @code + * { + * CCNxName *name = ccnxName_CreateFromCString("lci:/boose/roo/pie"); + * + * CCNxInterest *interestV1 = + * ccnxInterest_CreateWithImpl(&CCNxInterestFacadeV1_Implementation, + * name, + * CCNxInterestDefault_LifetimeMilliseconds, + * NULL, + * NULL, + * CCNxInterestDefault_HopLimit); + * + * assertTrue(ccnxInterestInterface_GetInterface(interestV1) == &CCNxInterestFacadeV1_Implementation, + * "Expected V1 Implementation"); + * + * ccnxName_Release(&name); + * ccnxInterest_Release(&interestV1); + * } * @endcode + */ +CCNxInterestInterface *ccnxInterestInterface_GetInterface(const CCNxTlvDictionary *interestDictionary); +#endif diff --git a/libccnx-common/ccnx/common/internal/ccnx_InterestPayloadIdMethod.h b/libccnx-common/ccnx/common/internal/ccnx_InterestPayloadIdMethod.h new file mode 100755 index 00000000..a29ddb9e --- /dev/null +++ b/libccnx-common/ccnx/common/internal/ccnx_InterestPayloadIdMethod.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +#ifndef CCNx_Common_ccnx_InterestPayloadIdMethod_h +#define CCNx_Common_ccnx_InterestPayloadIdMethod_h + +typedef enum { + CCNxInterestPayloadIdMethod_App = 0, + CCNxInterestPayloadIdMethod_Nonce = 1, + CCNxInterestPayloadIdMethod_RFC6920 = 2 +} CCNxInterestPayloadIdMethod; + +#endif diff --git a/libccnx-common/ccnx/common/internal/ccnx_InterestReturnFacadeV1.c b/libccnx-common/ccnx/common/internal/ccnx_InterestReturnFacadeV1.c new file mode 100755 index 00000000..80c1b718 --- /dev/null +++ b/libccnx-common/ccnx/common/internal/ccnx_InterestReturnFacadeV1.c @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <LongBow/runtime.h> + +#include <ccnx/common/internal/ccnx_InterestReturnFacadeV1.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_TlvDictionary.h> + +// ===================== + +static void +_assertInvariants(const CCNxTlvDictionary *interestDictionary) +{ + assertNotNull(interestDictionary, "Dictionary is null"); + assertTrue(ccnxTlvDictionary_IsInterestReturn(interestDictionary), "Dictionary is not an interest"); + assertTrue(ccnxTlvDictionary_GetSchemaVersion(interestDictionary) == CCNxTlvDictionary_SchemaVersion_V1, + "Dictionary is wrong schema InterestReturn, got %d expected %d", + ccnxTlvDictionary_GetSchemaVersion(interestDictionary), CCNxTlvDictionary_SchemaVersion_V1); +} + +static uint32_t +_fetchUint32(const CCNxTlvDictionary *interestDictionary, uint32_t key, uint32_t defaultValue) +{ + if (ccnxTlvDictionary_IsValueInteger(interestDictionary, key)) { + return (uint32_t) ccnxTlvDictionary_GetInteger(interestDictionary, key); + } + return defaultValue; +} + +// ===================== +// Creation + +static CCNxTlvDictionary * +_ccnxInterestReturnFacadeV1_Create( + const CCNxInterest *interest, + CCNxInterestReturn_ReturnCode code) +{ + assertNotNull(interest, "Parameter name must be non-null"); + + assertTrue(ccnxInterestInterface_GetInterface(interest) == &CCNxInterestFacadeV1_Implementation, + "Non-V1 CCNxInterest passed to V1 ccnxInterestReturn_Create()"); + + CCNxInterestReturnFacadeV1_Implementation.interestImpl = CCNxInterestFacadeV1_Implementation; + + + CCNxTlvDictionary *dictionary = ccnxTlvDictionary_ShallowCopy(interest); + + //Add InterestReturn specific stuff + ccnxTlvDictionary_SetMessageType_InterestReturn(dictionary, + CCNxTlvDictionary_SchemaVersion_V1); + + ccnxTlvDictionary_PutInteger(dictionary, + CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_InterestReturnCode, + code); + + return dictionary; +} + +static void +_ccnxInterestReturnFacadeV1_AssertValid(const CCNxTlvDictionary *interestDictionary) +{ + _assertInvariants(interestDictionary); + assertTrue(ccnxTlvDictionary_IsValueName(interestDictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_NAME), "Name field is not a name"); +} + +static CCNxInterestReturn_ReturnCode +_ccnxInterestReturnFacadeV1_GetReturnCode(const CCNxTlvDictionary *interestDictionary) +{ + _assertInvariants(interestDictionary); + CCNxInterestReturn_ReturnCode code = _fetchUint32(interestDictionary, + CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_InterestReturnCode, + 0); + assertTrue(((code > 0) && (code < CCNxInterestReturn_ReturnCode_END)), "InterestReturn ReturnCode is out of ranage"); + + return code; +} + +CCNxInterestReturnInterface CCNxInterestReturnFacadeV1_Implementation = { + .Create = &_ccnxInterestReturnFacadeV1_Create, + .AssertValid = &_ccnxInterestReturnFacadeV1_AssertValid, + .GetReturnCode = &_ccnxInterestReturnFacadeV1_GetReturnCode, +}; diff --git a/libccnx-common/ccnx/common/internal/ccnx_InterestReturnFacadeV1.h b/libccnx-common/ccnx/common/internal/ccnx_InterestReturnFacadeV1.h new file mode 100755 index 00000000..e8aa0e37 --- /dev/null +++ b/libccnx-common/ccnx/common/internal/ccnx_InterestReturnFacadeV1.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file ccnx_InterestReturnFacadeV1.h + * @ingroup InterestReturn + * @brief A CCN InterestReturn facade over a CCNxTlvDictionary, using v1 schema. + * + */ +#ifndef libccnx_ccnx_InterestReturnFacadeV1_h +#define libccnx_ccnx_InterestReturnFacadeV1_h + +#include <ccnx/common/internal/ccnx_InterestReturnInterface.h> + +extern CCNxInterestReturnInterface CCNxInterestReturnFacadeV1_Implementation; + +#endif // libccnx_ccnx_InterestReturnFacadeV1_h diff --git a/libccnx-common/ccnx/common/internal/ccnx_InterestReturnInterface.c b/libccnx-common/ccnx/common/internal/ccnx_InterestReturnInterface.c new file mode 100755 index 00000000..2c9aa111 --- /dev/null +++ b/libccnx-common/ccnx/common/internal/ccnx_InterestReturnInterface.c @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ +#include <config.h> +#include <ccnx/common/internal/ccnx_InterestReturnInterface.h> + +#include <LongBow/runtime.h> + +CCNxInterestReturnInterface * +ccnxInterestReturnInterface_GetInterface(const CCNxTlvDictionary *dictionary) +{ + assertTrue(ccnxTlvDictionary_IsInterestReturn(dictionary), "Expected an InterestReturn"); + + CCNxInterestReturnInterface *impl = ccnxTlvDictionary_GetMessageInterface(dictionary); + + if (!impl) { + // If we're here, we need to update the interface pointer. Break the const. + // We're not changing data values, just initializing the Interface pointer. + + // Figure out what the typeInterface should be, based on the attributes we know. + int schemaVersion = ccnxTlvDictionary_GetSchemaVersion(dictionary); + + switch (schemaVersion) { + case CCNxTlvDictionary_SchemaVersion_V0: + trapUnexpectedState("ccnxInterestReturnInterface_GetInterface() not implemented for V0"); + case CCNxTlvDictionary_SchemaVersion_V1: + impl = &CCNxInterestReturnFacadeV1_Implementation; + break; + default: + trapUnexpectedState("Unknown SchemaVersion encountered in ccnxInterestReturnInterface_GetInterface()"); + break; + } + + if (impl) { + ccnxTlvDictionary_SetMessageInterface((CCNxTlvDictionary *) dictionary, impl); // break the const. + } + } + + return impl; +} + diff --git a/libccnx-common/ccnx/common/internal/ccnx_InterestReturnInterface.h b/libccnx-common/ccnx/common/internal/ccnx_InterestReturnInterface.h new file mode 100644 index 00000000..3f36fb90 --- /dev/null +++ b/libccnx-common/ccnx/common/internal/ccnx_InterestReturnInterface.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @brief <#Brief Description#> + * + * <#Detailed Description#> + * + */ + +#ifndef CCNx_Common_ccnx_internal_InterestReturnInterface_h +#define CCNx_Common_ccnx_internal_InterestReturnInterface_h + +#include <ccnx/common/internal/ccnx_TlvDictionary.h> +#include <ccnx/common/ccnx_InterestReturn.h> +#include <ccnx/common/ccnx_Interest.h> + + +typedef struct ccnx_interest_return_interface { + CCNxInterestInterface interestImpl; + + CCNxTlvDictionary *(*Create)(const CCNxInterest * interest, CCNxInterestReturn_ReturnCode code); + + bool (*Equals)(const CCNxTlvDictionary *objectA, const CCNxTlvDictionary *objectB); + void (*AssertValid)(const CCNxTlvDictionary *dict); + char *(*ToString)(const CCNxTlvDictionary * dict); + + uint32_t (*GetReturnCode)(const CCNxTlvDictionary *dict); +} CCNxInterestReturnInterface; + +extern CCNxInterestReturnInterface CCNxInterestReturnFacadeV1_Implementation; + +/** + * Given a CCNxTlvDictionary representing a CCNxInterestReturn, return the address of the CCNxInterestReturnInterface + * instance that should be used to access the InterestReturn. + * + * @param interestDictionary - a {@link CCNxTlvDictionary} representing a CCNxInterestReturn. + * @return the address of the `CCNxInterestReturnInterface` instance that should be used to access the CCNxInterestReturn. + * + * Example: + * @code + * { + * + * CCNxInterest *interest = ...; + * CCNxInterestReturn *interestReturn = + * ccnxInterestReturn_Create(interest, CCNxInterestReturn_ReturnCode_NoRoute); + * + * //V1 test + * if (ccnxInterestReturnInterface_GetInterface(interestReturn) == &CCNxInterestReturnFacadeV1_Implementation) { + * printf("Using a V1 CCNxInterestReturnInterface \n"); + * } + * + * ... + * + * ccnxInterestReturn_Release(&interestReturn); + * } * @endcode + */ +CCNxInterestReturnInterface *ccnxInterestReturnInterface_GetInterface(const CCNxTlvDictionary *interestDictionary); +#endif diff --git a/libccnx-common/ccnx/common/internal/ccnx_ManifestFacadeV1.c b/libccnx-common/ccnx/common/internal/ccnx_ManifestFacadeV1.c new file mode 100644 index 00000000..e419d18e --- /dev/null +++ b/libccnx-common/ccnx/common/internal/ccnx_ManifestFacadeV1.c @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +/** + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> + +#include <LongBow/runtime.h> + +#include <parc/algol/parc_JSON.h> + +#include <ccnx/common/ccnx_Manifest.h> +#include <ccnx/common/internal/ccnx_ManifestInterface.h> + +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_TlvDictionary.h> + +static size_t _ccnxManifestFacadeV1_GetNumberOfHashGroups(const CCNxTlvDictionary *dict); + +static CCNxTlvDictionary * +_ccnxManifestFacadeV1_Create(const CCNxName *name) +{ + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateManifest(); + + if (dictionary != NULL) { + if (name != NULL) { + ccnxTlvDictionary_PutName(dictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_NAME, name); + } + } else { + trapOutOfMemory("Could not allocate an Manifest"); + } + + return dictionary; +} + +static const CCNxName * +_ccnxManifestFacadeV1_GetName(const CCNxTlvDictionary *dict) +{ + return ccnxTlvDictionary_GetName(dict, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_NAME); +} + +static void +_ccnxManifestFacadeV1_AddHashGroup(CCNxTlvDictionary *dict, const CCNxManifestHashGroup *group) +{ + PARCJSON *json = ccnxManifestHashGroup_ToJson(group); + size_t numGroups = _ccnxManifestFacadeV1_GetNumberOfHashGroups(dict); + + char *jsonString = parcJSON_ToString(json); + PARCBuffer *buffer = parcBuffer_AllocateCString(jsonString); + parcMemory_Deallocate(&jsonString); + parcJSON_Release(&json); + + ccnxTlvDictionary_PutListBuffer(dict, CCNxCodecSchemaV1TlvDictionary_Lists_HASH_GROUP_LIST, (uint32_t) numGroups, buffer); + parcBuffer_Release(&buffer); +} + +static CCNxManifestHashGroup * +_ccnxManifestFacadeV1_GetHashGroup(const CCNxTlvDictionary *dict, size_t index) +{ + PARCBuffer *buffer = NULL; + uint32_t key; + ccnxTlvDictionary_ListGetByPosition(dict, CCNxCodecSchemaV1TlvDictionary_Lists_HASH_GROUP_LIST, index, &buffer, &key); + + char *jsonString = parcBuffer_ToString(buffer); + PARCJSON *json = parcJSON_ParseString(jsonString); + parcMemory_Deallocate(&jsonString); + + CCNxManifestHashGroup *group = ccnxManifestHashGroup_CreateFromJson(json); + parcJSON_Release(&json); + + return group; +} + +static size_t +_ccnxManifestFacadeV1_GetNumberOfHashGroups(const CCNxTlvDictionary *dict) +{ + size_t numHashGroups = ccnxTlvDictionary_ListSize(dict, CCNxCodecSchemaV1TlvDictionary_Lists_HASH_GROUP_LIST); + return numHashGroups; +} + +static bool +_ccnxManifestFacadeV1_Equals(const CCNxTlvDictionary *dictA, const CCNxTlvDictionary *dictB) +{ + if (dictA == dictB) { + return true; + } + if (dictA == NULL || dictB == NULL) { + return false; + } + + if (ccnxName_Equals(_ccnxManifestFacadeV1_GetName(dictA), _ccnxManifestFacadeV1_GetName(dictB))) { + if (_ccnxManifestFacadeV1_GetNumberOfHashGroups(dictA) == _ccnxManifestFacadeV1_GetNumberOfHashGroups(dictB)) { + for (size_t i = 0; i < _ccnxManifestFacadeV1_GetNumberOfHashGroups(dictA); i++) { + CCNxManifestHashGroup *groupA = _ccnxManifestFacadeV1_GetHashGroup(dictA, i); + CCNxManifestHashGroup *groupB = _ccnxManifestFacadeV1_GetHashGroup(dictB, i); + + if (!ccnxManifestHashGroup_Equals(groupA, groupB)) { + ccnxManifestHashGroup_Release(&groupA); + ccnxManifestHashGroup_Release(&groupB); + return false; + } + } + return true; + } + } + return false; +}; + +CCNxManifestInterface CCNxManifestFacadeV1_Interface = { + .description = "CCNxManifestFacadeV1_Implementation", + .create = &_ccnxManifestFacadeV1_Create, + .getName = &_ccnxManifestFacadeV1_GetName, + .addHashGroup = &_ccnxManifestFacadeV1_AddHashGroup, + .getHashGroup = &_ccnxManifestFacadeV1_GetHashGroup, + .getNumberOfHashGroups = &_ccnxManifestFacadeV1_GetNumberOfHashGroups, + .equals = &_ccnxManifestFacadeV1_Equals, +}; diff --git a/libccnx-common/ccnx/common/internal/ccnx_ManifestFacadeV1.h b/libccnx-common/ccnx/common/internal/ccnx_ManifestFacadeV1.h new file mode 100755 index 00000000..0b5aab5a --- /dev/null +++ b/libccnx-common/ccnx/common/internal/ccnx_ManifestFacadeV1.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +/** + * @file ccnx_ManifestFacadeV1.h + * @ingroup Manifest + * @brief A CCN Manifest facade over a CCNxTlvDictionary, using V1 schema. + * + */ +#ifndef libccnx_ccnx_ManifestFacadeV1_h +#define libccnx_ccnx_ManifestFacadeV1_h + +#include <ccnx/common/internal/ccnx_ManifestInterface.h> + +extern CCNxManifestInterface CCNxManifestFacadeV1_Interface; + +#endif // libccnx_ccnx_ManifestFacadeV1_h diff --git a/libccnx-common/ccnx/common/internal/ccnx_ManifestInterface.c b/libccnx-common/ccnx/common/internal/ccnx_ManifestInterface.c new file mode 100755 index 00000000..581eb93c --- /dev/null +++ b/libccnx-common/ccnx/common/internal/ccnx_ManifestInterface.c @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +/** + */ +#include <config.h> + +#include <LongBow/runtime.h> + +#include <ccnx/common/ccnx_PayloadType.h> +#include <ccnx/common/internal/ccnx_ManifestInterface.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_TlvDictionary.h> + +CCNxManifestInterface * +ccnxManifestInterface_GetInterface(const CCNxTlvDictionary *dictionary) +{ + assertTrue(ccnxTlvDictionary_IsManifest(dictionary), "Expected a Manifest"); + + CCNxManifestInterface *impl = (CCNxManifestInterface *) ccnxTlvDictionary_GetMessageInterface(dictionary); + + if (impl == NULL) { + // If we're here, we need to update the implementation pointer. + // Figure out what the typeImplementation should be, based on the attributes we know. + int schemaVersion = ccnxTlvDictionary_GetSchemaVersion(dictionary); + + switch (schemaVersion) { + case CCNxTlvDictionary_SchemaVersion_V1: + impl = &CCNxManifestFacadeV1_Interface; + break; + default: + trapUnexpectedState("Unknown SchemaVersion encountered in ccnxInterestImplementation_GetImplementation()"); + break; + } + + if (impl == NULL) { + // The cast to (CCNxTlvDictionary *) is to break the const. + ccnxTlvDictionary_SetMessageInterface((CCNxTlvDictionary *) dictionary, (CCNxMessageInterface *) impl); + } + } + + return impl; +} diff --git a/libccnx-common/ccnx/common/internal/ccnx_ManifestInterface.h b/libccnx-common/ccnx/common/internal/ccnx_ManifestInterface.h new file mode 100644 index 00000000..75855b0c --- /dev/null +++ b/libccnx-common/ccnx/common/internal/ccnx_ManifestInterface.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +/** + * @brief A structure of functions representing a Manifest implementation. + * + * The underlying implementation should support the CCNxManifest API. + * + */ + +#ifndef CCNx_Common_ccnx_internal_ManifestImpl_h +#define CCNx_Common_ccnx_internal_ManifestImpl_h + +#include <ccnx/common/internal/ccnx_TlvDictionary.h> +#include <ccnx/common/ccnx_ManifestHashGroup.h> +#include <ccnx/common/ccnx_Name.h> +#include <ccnx/common/ccnx_KeyLocator.h> + +#include "ccnx_TlvDictionary.h" + +typedef struct ccnx_manifest_interface { + /** A human-readable label for this implementation */ + char *description; + + /** @see ccnxManifest_Create */ + CCNxTlvDictionary *(*create)(const CCNxName * name); + + /** @see ccnxManifest_AddHashGroup */ + void (*addHashGroup)(CCNxTlvDictionary *dict, const CCNxManifestHashGroup *group); + + /** @see ccnxManifest_GetHashGroup */ + CCNxManifestHashGroup *(*getHashGroup)(const CCNxTlvDictionary * dict, size_t index); + + /** @see ccnxManifest_GetNumberOfHashGroups */ + size_t (*getNumberOfHashGroups)(const CCNxTlvDictionary *dict); + + /** @see ccnxManifest_Equals */ + bool (*equals)(const CCNxTlvDictionary *objectA, const CCNxTlvDictionary *objectB); + + /** @see ccnxManifest_GetName */ + const CCNxName *(*getName)(const CCNxTlvDictionary * dict); +} CCNxManifestInterface; + +CCNxManifestInterface *ccnxManifestInterface_GetInterface(const CCNxTlvDictionary *dictionary); + +/** + * The SchemaV1 Manifest implementaton + */ +extern CCNxManifestInterface CCNxManifestFacadeV1_Interface; + +#endif diff --git a/libccnx-common/ccnx/common/internal/ccnx_MessageInterface.h b/libccnx-common/ccnx/common/internal/ccnx_MessageInterface.h new file mode 100755 index 00000000..1f438348 --- /dev/null +++ b/libccnx-common/ccnx/common/internal/ccnx_MessageInterface.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +#ifndef CCNx_Common_ccnx_MessageInterface_h +#define CCNx_Common_ccnx_MessageInterface_h + +/** + * A type to represent the ContentObject/Interest/Control Interface pointer. The CCNxTlvDictionary + * maintains one that points to the appropriate interface implementation to reference the data + * contained in the CCNxTlvDictionary. For example, it might point to a CCNxContentObjectInterface, + * which would enable accessing the CCNxTlvDictionary as a ContentObject. + * + * + * @see CCNxContentObjectInterface + * @see CCNxInterestInterface + */ +typedef void CCNxMessageInterface; +#endif diff --git a/libccnx-common/ccnx/common/internal/ccnx_TlvDictionary.c b/libccnx-common/ccnx/common/internal/ccnx_TlvDictionary.c new file mode 100755 index 00000000..3c768529 --- /dev/null +++ b/libccnx-common/ccnx/common/internal/ccnx_TlvDictionary.c @@ -0,0 +1,1022 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * + */ + +#include <config.h> +#include <stdlib.h> +#include <sys/time.h> +#include <inttypes.h> +#include <stdio.h> + +#include <ccnx/common/ccnx_Name.h> + +#include <ccnx/common/internal/ccnx_TlvDictionary.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_TlvDictionary.h> + +#include <LongBow/runtime.h> + +#include <parc/algol/parc_DisplayIndented.h> +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Object.h> +#include <parc/algol/parc_JSON.h> + +#define DEBUG_ALLOCS 0 + +struct ccnx_tlv_dictionary_entry; +typedef struct ccnx_tlv_list_entry _CCNxTlvDictionaryListEntry; + +typedef enum { + CCNxTlvDictionaryType_Unknown, + CCNxTlvDictionaryType_Interest, + CCNxTlvDictionaryType_ContentObject, + CCNxTlvDictionaryType_Control, + CCNxTlvDictionaryType_InterestReturn, + CCNxTlvDictionaryType_Manifest +} _CCNxTlvDictionaryType; + +// These form a singly linked list +struct ccnx_tlv_list_entry { + _CCNxTlvDictionaryListEntry *next; + PARCBuffer *buffer; + uint16_t key; +}; + +#define ENTRY_UNSET ((int) 0) +#define ENTRY_BUFFER ((int) 1) +#define ENTRY_NAME ((int) 2) +#define ENTRY_INTEGER ((int) 3) +#define ENTRY_IOVEC ((int) 4) +#define ENTRY_JSON ((int) 5) +#define ENTRY_OBJECT ((int) 6) + +static struct dictionary_type_string { + _CCNxTlvDictionaryType type; + const char *string; +} ccnxTlvDictionaryTypeStrings [] = { + { .type = CCNxTlvDictionaryType_Unknown, .string = "Invalid" }, + { .type = CCNxTlvDictionaryType_Interest, .string = "Interest" }, + { .type = CCNxTlvDictionaryType_ContentObject, .string = "Content Object" }, + { .type = CCNxTlvDictionaryType_Control, .string = "Control" }, + { .type = CCNxTlvDictionaryType_InterestReturn, .string = "InterestReturn" }, + { .type = CCNxTlvDictionaryType_Manifest, .string = "Manifest" }, + { .type = UINT32_MAX, .string = NULL }, +}; + +static struct dictionary_entry_type_string { + int type; + const char *string; +} ccnxTlvDictionaryEntryTypeStrings [] = { + { .type = ENTRY_UNSET, .string = "Unset" }, + { .type = ENTRY_BUFFER, .string = "Buffer" }, + { .type = ENTRY_NAME, .string = "Name" }, + { .type = ENTRY_INTEGER, .string = "Integer" }, + { .type = ENTRY_IOVEC, .string = "IoVec" }, + { .type = ENTRY_JSON, .string = "JSON" }, + { .type = ENTRY_OBJECT, .string = "Object" }, + { .type = UINT32_MAX, .string = NULL }, +}; + +static const char *ccnxTlvDictionaryTypeUnknown = "Unknown"; + +static const char * +_ccnxTlvDictionaryEntryTypeToString(int entryType) +{ + for (int i = 0; ccnxTlvDictionaryEntryTypeStrings[i].string != NULL; i++) { + if (ccnxTlvDictionaryEntryTypeStrings[i].type == entryType) { + return ccnxTlvDictionaryEntryTypeStrings[i].string; + } + } + return ccnxTlvDictionaryTypeUnknown; +} + +static const char * +_ccnxTlvDictionaryTypeToString(_CCNxTlvDictionaryType dictionaryType) +{ + for (int i = 0; ccnxTlvDictionaryTypeStrings[i].string != NULL; i++) { + if (ccnxTlvDictionaryTypeStrings[i].type == dictionaryType) { + return ccnxTlvDictionaryTypeStrings[i].string; + } + } + return ccnxTlvDictionaryTypeUnknown; +} + + +typedef struct ccnx_tlv_dictionary_entry { + int entryType; + union u_entry { + PARCBuffer *buffer; + uint64_t integer; + CCNxName *name; + CCNxCodecNetworkBufferIoVec *vec; + PARCJSON *json; + PARCObject *object; + } _entry; +} _CCNxTlvDictionaryEntry; + +struct ccnx_tlv_dictionary { +#define FIXED_LIST_LENGTH 8 + // These are linked lists where we put unknown TLV types. This one static allocation should + // be enough for all the current packet formats. + _CCNxTlvDictionaryListEntry *fixedListHeads[FIXED_LIST_LENGTH]; + + // if we need to allocate beyond FIXED_LIST_LENGTH, put them here + _CCNxTlvDictionaryListEntry **extraListHeads; + + size_t fastArraySize; + size_t listSize; + + _CCNxTlvDictionaryType dictionaryType; + CCNxTlvDictionary_SchemaVersion schemaVersion; + + // Detects changes in the dictionary that were not caused by us + uint32_t generation; + + struct timeval creationTime; + + void (*infoFreeFunction)(void **infoPtr); + void *info; + + // A pointer to the implementation functions for the type contained by this dictionary. + // It's a runtime static, and is not encoded. Thus, when a dictionary is received over + // the wire, it will need to be initialized based on the dictionaryType and schemaVersion. + CCNxMessageInterface *messageInterface; + + // will be allocated as part of the ccnx_tlv_dictionary + _CCNxTlvDictionaryEntry directArray[CCNxCodecSchemaV1TlvDictionary_MessageFastArray_END]; +}; + +static _CCNxTlvDictionaryListEntry * +_ccnxTlvDictionaryListEntry_Create(uint32_t key, const PARCBuffer *buffer) +{ + _CCNxTlvDictionaryListEntry *entry = parcMemory_AllocateAndClear(sizeof(_CCNxTlvDictionaryListEntry)); + assertNotNull(entry, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(_CCNxTlvDictionaryListEntry)); + entry->key = key; + entry->buffer = parcBuffer_Acquire(buffer); + + return entry; +} + +static void +_ccnxTlvDictionaryListEntry_Release(_CCNxTlvDictionaryListEntry **entryPtr) +{ + _CCNxTlvDictionaryListEntry *entry = *entryPtr; + parcBuffer_Release(&entry->buffer); + parcMemory_Deallocate((void **) &entry); + *entryPtr = NULL; +} + +static void +_ccnxTlvDictionaryEntry_ListRelease(_CCNxTlvDictionaryListEntry **listHeadPtr) +{ + _CCNxTlvDictionaryListEntry *listHead = *listHeadPtr; + while (listHead) { + _CCNxTlvDictionaryListEntry *next = listHead->next; + _ccnxTlvDictionaryListEntry_Release(&listHead); + listHead = next; + } + *listHeadPtr = NULL; +} + +static void +_ccnxTlvDictionary_FinalRelease(CCNxTlvDictionary **dictionaryPtr) +{ + CCNxTlvDictionary *dictionary = *dictionaryPtr; + + // release any entries stored in the fast array + for (int i = 0; i < dictionary->fastArraySize; i++) { + switch (dictionary->directArray[i].entryType) { + case ENTRY_BUFFER: + parcBuffer_Release(&dictionary->directArray[i]._entry.buffer); + break; + case ENTRY_NAME: + ccnxName_Release(&dictionary->directArray[i]._entry.name); + break; + case ENTRY_IOVEC: + ccnxCodecNetworkBufferIoVec_Release(&dictionary->directArray[i]._entry.vec); + break; + case ENTRY_JSON: + parcJSON_Release(&dictionary->directArray[i]._entry.json); + break; + case ENTRY_OBJECT: + parcObject_Release(&dictionary->directArray[i]._entry.object); + break; + default: + // other types are direct storage + break; + } + } + + for (int i = 0; i < FIXED_LIST_LENGTH; i++) { + if (dictionary->fixedListHeads[i]) { + _ccnxTlvDictionaryEntry_ListRelease(&dictionary->fixedListHeads[i]); + } + } + + if (dictionary->extraListHeads) { + for (int i = FIXED_LIST_LENGTH; i < dictionary->listSize; i++) { + if (dictionary->extraListHeads[i - FIXED_LIST_LENGTH]) { + _ccnxTlvDictionaryEntry_ListRelease(&dictionary->extraListHeads[i - FIXED_LIST_LENGTH]); + } + } + parcMemory_Deallocate((void **) &(dictionary->extraListHeads)); + } + + if (dictionary->infoFreeFunction) { + dictionary->infoFreeFunction(&dictionary->info); + } + +#if DEBUG_ALLOCS + printf("finalize dictionary %p (final)\n", dictionary); +#endif +} + +parcObject_ExtendPARCObject(CCNxTlvDictionary, _ccnxTlvDictionary_FinalRelease, + NULL, NULL, ccnxTlvDictionary_Equals, NULL, NULL, NULL); + +parcObject_ImplementAcquire(ccnxTlvDictionary, CCNxTlvDictionary); + +parcObject_ImplementRelease(ccnxTlvDictionary, CCNxTlvDictionary); + +static void +_ccnxTlvDictionary_GetTimeOfDay(struct timeval *outputTime) +{ +#ifdef DEBUG + // if in debug mode, time messages + gettimeofday(outputTime, NULL); +#else + *outputTime = (struct timeval) { 0, 0 }; +#endif +} + + +CCNxTlvDictionary * +ccnxTlvDictionary_Create(size_t bufferCount, size_t listCount) +{ + CCNxTlvDictionary *dictionary = (CCNxTlvDictionary *) parcObject_CreateAndClearInstance(CCNxTlvDictionary); + + if (dictionary != NULL) { + _ccnxTlvDictionary_GetTimeOfDay(&dictionary->creationTime); + + dictionary->dictionaryType = CCNxTlvDictionaryType_Unknown; + dictionary->fastArraySize = bufferCount; + dictionary->listSize = listCount; + + dictionary->infoFreeFunction = NULL; + dictionary->info = NULL; + + dictionary->extraListHeads = NULL; + // dictionary->directArray is allocated as part of parcObject + } + +#if DEBUG_ALLOCS + printf("allocate dictionary %p\n", dictionary); +#endif + + return dictionary; +} + +CCNxTlvDictionary * +ccnxTlvDictionary_ShallowCopy(const CCNxTlvDictionary *source) +{ + size_t bufferCount = source->fastArraySize; + size_t listCount = source->listSize; + CCNxTlvDictionary *newDictionary = ccnxTlvDictionary_Create(bufferCount, listCount); + + if (newDictionary != NULL) { + newDictionary->dictionaryType = source->dictionaryType; + newDictionary->schemaVersion = source->schemaVersion; + newDictionary->generation = source->generation; + newDictionary->creationTime = source->creationTime; + newDictionary->messageInterface = source->messageInterface; + newDictionary->info = source->info; + newDictionary->infoFreeFunction = source->infoFreeFunction; + + // Update listHeads + for (uint32_t key = 0; key < source->listSize; ++key) { + size_t listSize = ccnxTlvDictionary_ListSize(source, key); + for (size_t i = 0; i < listSize; ++i) { + PARCBuffer *buffer; + uint32_t bKey; + ccnxTlvDictionary_ListGetByPosition(source, key, i, &buffer, &bKey); + parcBuffer_Acquire(buffer); + ccnxTlvDictionary_PutListBuffer(newDictionary, key, bKey, buffer); + parcBuffer_Release(&buffer); + } + } + + // Update directArray + for (uint32_t key = 0; key < source->fastArraySize; ++key) { + switch (source->directArray[key].entryType) { + case ENTRY_BUFFER: + ccnxTlvDictionary_PutBuffer(newDictionary, key, ccnxTlvDictionary_GetBuffer(source, key)); + break; + case ENTRY_NAME: + ccnxTlvDictionary_PutName(newDictionary, key, ccnxTlvDictionary_GetName(source, key)); + break; + case ENTRY_IOVEC: + ccnxTlvDictionary_PutIoVec(newDictionary, key, ccnxTlvDictionary_GetIoVec(source, key)); + break; + case ENTRY_JSON: + ccnxTlvDictionary_PutJson(newDictionary, key, ccnxTlvDictionary_GetJson(source, key)); + break; + case ENTRY_INTEGER: + ccnxTlvDictionary_PutInteger(newDictionary, key, ccnxTlvDictionary_GetInteger(source, key)); + break; + case ENTRY_OBJECT: + ccnxTlvDictionary_PutObject(newDictionary, key, ccnxTlvDictionary_GetObject(source, key)); + break; + default: + break; + } + } + } + + return newDictionary; +} + +bool +ccnxTlvDictionary_PutBuffer(CCNxTlvDictionary *dictionary, uint32_t key, const PARCBuffer *buffer) +{ + assertNotNull(dictionary, "Parameter dictionary must be non-null"); + assertNotNull(buffer, "Parameter buffer must be non-null"); + assertTrue(key < dictionary->fastArraySize, "Parameter key must be less than %zu", dictionary->fastArraySize); + + if (dictionary->directArray[key].entryType == ENTRY_UNSET) { + dictionary->directArray[key].entryType = ENTRY_BUFFER; + dictionary->directArray[key]._entry.buffer = parcBuffer_Acquire(buffer); + return true; + } + return false; +} + +bool +ccnxTlvDictionary_PutObject(CCNxTlvDictionary *dictionary, uint32_t key, const PARCObject *object) +{ + assertNotNull(dictionary, "Parameter dictionary must be non-null"); + assertNotNull(object, "Parameter object must be non-null"); + assertTrue(key < dictionary->fastArraySize, "Parameter key %ud must be less than %zu", key, dictionary->fastArraySize); + + if (dictionary->directArray[key].entryType == ENTRY_UNSET) { + dictionary->directArray[key].entryType = ENTRY_OBJECT; + dictionary->directArray[key]._entry.object = parcObject_Acquire(object); + return true; + } + return false; +} + +bool +ccnxTlvDictionary_PutName(CCNxTlvDictionary *dictionary, uint32_t key, const CCNxName *name) +{ + assertNotNull(dictionary, "Parameter dictionary must be non-null"); + assertNotNull(name, "Parameter buffer must be non-null"); + assertTrue(key < dictionary->fastArraySize, "Parameter key must be less than %zu", dictionary->fastArraySize); + + if (dictionary->directArray[key].entryType == ENTRY_UNSET) { + dictionary->directArray[key].entryType = ENTRY_NAME; + dictionary->directArray[key]._entry.name = ccnxName_Acquire(name); + return true; + } + return false; +} + +bool +ccnxTlvDictionary_PutInteger(CCNxTlvDictionary *dictionary, uint32_t key, const uint64_t value) +{ + assertNotNull(dictionary, "Parameter dictionary must be non-null"); + assertTrue(key < dictionary->fastArraySize, "Parameter key must be less than %zu", dictionary->fastArraySize); + + if (dictionary->directArray[key].entryType == ENTRY_UNSET || dictionary->directArray[key].entryType == ENTRY_INTEGER) { + dictionary->directArray[key].entryType = ENTRY_INTEGER; + dictionary->directArray[key]._entry.integer = value; + return true; + } + return false; +} + +bool +ccnxTlvDictionary_PutIoVec(CCNxTlvDictionary *dictionary, uint32_t key, const CCNxCodecNetworkBufferIoVec *vec) +{ + assertNotNull(dictionary, "Parameter dictionary must be non-null"); + assertNotNull(vec, "Parameter buffer must be non-null"); + assertTrue(key < dictionary->fastArraySize, "Parameter key must be less than %zu", dictionary->fastArraySize); + + if (dictionary->directArray[key].entryType == ENTRY_UNSET) { + dictionary->directArray[key].entryType = ENTRY_IOVEC; + dictionary->directArray[key]._entry.vec = ccnxCodecNetworkBufferIoVec_Acquire((CCNxCodecNetworkBufferIoVec *) vec); + return true; + } + return false; +} + +bool +ccnxTlvDictionary_PutJson(CCNxTlvDictionary *dictionary, uint32_t key, const PARCJSON *json) +{ + assertNotNull(dictionary, "Parameter dictionary must be non-null"); + assertNotNull(json, "Parameter json must be non-null"); + assertTrue(key < dictionary->fastArraySize, "Parameter key must be less than %zu", dictionary->fastArraySize); + + if (dictionary->directArray[key].entryType == ENTRY_UNSET) { + dictionary->directArray[key].entryType = ENTRY_JSON; + dictionary->directArray[key]._entry.json = parcJSON_Acquire(json); + return true; + } + return false; +} + +CCNxCodecNetworkBufferIoVec * +ccnxTlvDictionary_GetIoVec(const CCNxTlvDictionary *dictionary, uint32_t key) +{ + assertNotNull(dictionary, "Parameter dictionary must be non-null"); + assertTrue(key < dictionary->fastArraySize, "Parameter key must be less than %zu", dictionary->fastArraySize); + + if (dictionary->directArray[key].entryType == ENTRY_IOVEC) { + return dictionary->directArray[key]._entry.vec; + } + return NULL; +} + +// If you need to change the list head, use this +static _CCNxTlvDictionaryListEntry ** +_getListHeadReference(CCNxTlvDictionary *dictionary, uint32_t listKey) +{ + if (listKey < FIXED_LIST_LENGTH) { + return &dictionary->fixedListHeads[listKey]; + } else { + if (dictionary->extraListHeads == NULL) { + dictionary->extraListHeads = parcMemory_AllocateAndClear(sizeof(_CCNxTlvDictionaryListEntry *) * (dictionary->listSize - FIXED_LIST_LENGTH)); + } + + return &dictionary->extraListHeads[listKey - FIXED_LIST_LENGTH]; + } +} + +// If not going to modify the list, use this +static _CCNxTlvDictionaryListEntry * +_getListHead(const CCNxTlvDictionary *dictionary, uint32_t listKey) +{ + _CCNxTlvDictionaryListEntry **head = _getListHeadReference((CCNxTlvDictionary *) dictionary, listKey); + return *head; +} + +bool +ccnxTlvDictionary_PutListBuffer(CCNxTlvDictionary *dictionary, uint32_t listKey, uint32_t key, const PARCBuffer *buffer) +{ + assertNotNull(dictionary, "Parameter dictionary must be non-null"); + assertNotNull(buffer, "Parameter buffer must be non-null"); + assertTrue(listKey < dictionary->listSize, "Parameter key must be less than %zu", dictionary->listSize); + + _CCNxTlvDictionaryListEntry *entry = _ccnxTlvDictionaryListEntry_Create(key, buffer); + + _CCNxTlvDictionaryListEntry **head = _getListHeadReference(dictionary, listKey); + if (*head) { + // insert new value at list head + entry->next = *head; + *head = entry; + } else { + // new value is the list head + *head = entry; + } + return true; +} + +bool +ccnxTlvDictionary_IsValueBuffer(const CCNxTlvDictionary *dictionary, uint32_t key) +{ + assertNotNull(dictionary, "Parameter dictionary must be non-null"); + assertTrue(key < dictionary->fastArraySize, "Parameter key must be less than %zu", dictionary->fastArraySize); + return (dictionary->directArray[key].entryType == ENTRY_BUFFER); +} + +bool +ccnxTlvDictionary_IsValueObject(const CCNxTlvDictionary *dictionary, uint32_t key) +{ + assertNotNull(dictionary, "Parameter dictionary must be non-null"); + assertTrue(key < dictionary->fastArraySize, "Parameter key must be less than %zu", dictionary->fastArraySize); + return (dictionary->directArray[key].entryType == ENTRY_OBJECT); +} + +bool +ccnxTlvDictionary_IsValueInteger(const CCNxTlvDictionary *dictionary, uint32_t key) +{ + assertNotNull(dictionary, "Parameter dictionary must be non-null"); + assertTrue(key < dictionary->fastArraySize, "Parameter key must be less than %zu", dictionary->fastArraySize); + return (dictionary->directArray[key].entryType == ENTRY_INTEGER); +} + +bool +ccnxTlvDictionary_IsValueName(const CCNxTlvDictionary *dictionary, uint32_t key) +{ + assertNotNull(dictionary, "Parameter dictionary must be non-null"); + assertTrue(key < dictionary->fastArraySize, "Parameter key must be less than %zu", dictionary->fastArraySize); + return (dictionary->directArray[key].entryType == ENTRY_NAME); +} + +bool +ccnxTlvDictionary_IsValueIoVec(const CCNxTlvDictionary *dictionary, uint32_t key) +{ + assertNotNull(dictionary, "Parameter dictionary must be non-null"); + assertTrue(key < dictionary->fastArraySize, "Parameter key must be less than %zu", dictionary->fastArraySize); + return (dictionary->directArray[key].entryType == ENTRY_IOVEC); +} + +bool +ccnxTlvDictionary_IsValueJson(const CCNxTlvDictionary *dictionary, uint32_t key) +{ + assertNotNull(dictionary, "Parameter dictionary must be non-null"); + assertTrue(key < dictionary->fastArraySize, "Parameter key must be less than %zu", dictionary->fastArraySize); + return (dictionary->directArray[key].entryType == ENTRY_JSON); +} + +PARCBuffer * +ccnxTlvDictionary_GetBuffer(const CCNxTlvDictionary *dictionary, uint32_t key) +{ + assertNotNull(dictionary, "Parameter dictionary must be non-null"); + assertTrue(key < dictionary->fastArraySize, "Parameter key must be less than %zu", dictionary->fastArraySize); + + // For now return NULL for backward compatability with prior code, case 1011 + if (dictionary->directArray[key].entryType == ENTRY_BUFFER) { + return dictionary->directArray[key]._entry.buffer; + } + return NULL; +} + +CCNxName * +ccnxTlvDictionary_GetName(const CCNxTlvDictionary *dictionary, uint32_t key) +{ + assertNotNull(dictionary, "Parameter dictionary must be non-null"); + assertTrue(key < dictionary->fastArraySize, "Parameter key must be less than %zu", dictionary->fastArraySize); + + if (dictionary->directArray[key].entryType == ENTRY_NAME) { + return dictionary->directArray[key]._entry.name; + } + return NULL; +} + +uint64_t +ccnxTlvDictionary_GetInteger(const CCNxTlvDictionary *dictionary, uint32_t key) +{ + assertNotNull(dictionary, "Parameter dictionary must be non-null"); + assertTrue(key < dictionary->fastArraySize, "Parameter key must be less than %zu", dictionary->fastArraySize); + + trapIllegalValueIf(dictionary->directArray[key].entryType != ENTRY_INTEGER, + "Key %u is of type %d", + key, dictionary->directArray[key].entryType) + { + ccnxTlvDictionary_Display(dictionary, 3); + } + + return dictionary->directArray[key]._entry.integer; +} + + +PARCJSON * +ccnxTlvDictionary_GetJson(const CCNxTlvDictionary *dictionary, uint32_t key) +{ + assertNotNull(dictionary, "Parameter dictionary must be non-null"); + assertTrue(key < dictionary->fastArraySize, "Parameter key must be less than %zu", dictionary->fastArraySize); + + if (dictionary->directArray[key].entryType == ENTRY_JSON) { + return dictionary->directArray[key]._entry.json; + } + return NULL; +} + +PARCObject * +ccnxTlvDictionary_GetObject(const CCNxTlvDictionary *dictionary, uint32_t key) +{ + assertNotNull(dictionary, "Parameter dictionary must be non-null"); + assertTrue(key < dictionary->fastArraySize, "Parameter key must be less than %zu", dictionary->fastArraySize); + + if (dictionary->directArray[key].entryType == ENTRY_OBJECT) { + return dictionary->directArray[key]._entry.object; + } + + return NULL; +} + +bool +ccnxTlvDictionary_ListGetByPosition(const CCNxTlvDictionary *dictionary, uint32_t listKey, size_t listPosition, PARCBuffer **bufferPtr, uint32_t *keyPtr) +{ + assertNotNull(dictionary, "Parameter dictionary must be non-null"); + assertNotNull(bufferPtr, "Parameter bufferPtr must be non-null"); + assertNotNull(keyPtr, "Parameter keyPtr must be non-null"); + assertTrue(listKey < dictionary->listSize, "Parameter key must be less than %zu", dictionary->listSize); + + _CCNxTlvDictionaryListEntry *entry = _getListHead(dictionary, listKey); + while (entry) { + if (listPosition == 0) { + *bufferPtr = entry->buffer; + *keyPtr = entry->key; + return true; + } + entry = entry->next; + --listPosition; + } + + return false; +} + + +PARCBuffer * +ccnxTlvDictionary_ListGetByType(const CCNxTlvDictionary *dictionary, uint32_t listKey, uint32_t type) +{ + assertNotNull(dictionary, "Parameter dictionary must be non-null"); + assertTrue(listKey < dictionary->listSize, "Parameter key must be less than %zu", dictionary->listSize); + + PARCBuffer *buffer = NULL; + _CCNxTlvDictionaryListEntry *entry = _getListHead(dictionary, listKey); + while (entry) { + if (entry->key == type) { + buffer = entry->buffer; + break; + } + entry = entry->next; + } + + return buffer; +} + + +size_t +ccnxTlvDictionary_ListSize(const CCNxTlvDictionary *dictionary, uint32_t listKey) +{ + assertNotNull(dictionary, "Parameter dictionary must be non-null"); + assertTrue(listKey < dictionary->listSize, "Parameter key must be less than %zu", dictionary->listSize); + + size_t size = 0; + _CCNxTlvDictionaryListEntry *entry = _getListHead(dictionary, listKey); + while (entry) { + size++; + entry = entry->next; + } + + return size; +} + +void +ccnxTlvDictionary_SetMessageType_Interest(CCNxTlvDictionary *dictionary, CCNxTlvDictionary_SchemaVersion schemaVersion) +{ + dictionary->dictionaryType = CCNxTlvDictionaryType_Interest; + dictionary->schemaVersion = schemaVersion; +} + +void +ccnxTlvDictionary_SetMessageType_ContentObject(CCNxTlvDictionary *dictionary, CCNxTlvDictionary_SchemaVersion schemaVersion) +{ + dictionary->dictionaryType = CCNxTlvDictionaryType_ContentObject; + dictionary->schemaVersion = schemaVersion; +} + +void +ccnxTlvDictionary_SetMessageType_Control(CCNxTlvDictionary *dictionary, CCNxTlvDictionary_SchemaVersion schemaVersion) +{ + dictionary->dictionaryType = CCNxTlvDictionaryType_Control; + dictionary->schemaVersion = schemaVersion; +} + +void +ccnxTlvDictionary_SetMessageType_InterestReturn(CCNxTlvDictionary *dictionary, CCNxTlvDictionary_SchemaVersion schemaVersion) +{ + dictionary->dictionaryType = CCNxTlvDictionaryType_InterestReturn; + dictionary->schemaVersion = schemaVersion; +} + +void +ccnxTlvDictionary_SetMessageType_Manifest(CCNxTlvDictionary *dictionary, CCNxTlvDictionary_SchemaVersion schemaVersion) +{ + dictionary->dictionaryType = CCNxTlvDictionaryType_Manifest; + dictionary->schemaVersion = schemaVersion; +} + +bool +ccnxTlvDictionary_IsInterest(const CCNxTlvDictionary *dictionary) +{ + return (dictionary->dictionaryType == CCNxTlvDictionaryType_Interest); +} + +bool +ccnxTlvDictionary_IsInterestReturn(const CCNxTlvDictionary *dictionary) +{ + return (dictionary->dictionaryType == CCNxTlvDictionaryType_InterestReturn); +} + +bool +ccnxTlvDictionary_IsContentObject(const CCNxTlvDictionary *dictionary) +{ + return (dictionary->dictionaryType == CCNxTlvDictionaryType_ContentObject); +} + +bool +ccnxTlvDictionary_IsControl(const CCNxTlvDictionary *dictionary) +{ + return (dictionary->dictionaryType == CCNxTlvDictionaryType_Control); +} + +bool +ccnxTlvDictionary_IsManifest(const CCNxTlvDictionary *dictionary) +{ + return (dictionary->dictionaryType == CCNxTlvDictionaryType_Manifest); +} + +CCNxTlvDictionary_SchemaVersion +ccnxTlvDictionary_GetSchemaVersion(const CCNxTlvDictionary *dictionary) +{ + return dictionary->schemaVersion; +} + +void +ccnxTlvDictionary_SetMessageInterface(CCNxTlvDictionary *dictionary, const CCNxMessageInterface *implementation) +{ + dictionary->messageInterface = (CCNxMessageInterface *) implementation; +} + +CCNxMessageInterface * +ccnxTlvDictionary_GetMessageInterface(const CCNxTlvDictionary *dictionary) +{ + return dictionary->messageInterface; +} + +struct timeval +ccnxTlvDictionary_GetLifetime(const CCNxTlvDictionary *dictionary) +{ + struct timeval now; + _ccnxTlvDictionary_GetTimeOfDay(&now); + timersub(&now, &dictionary->creationTime, &now); + return now; +} + +static void +_ccnxTlvDictionary_DisplayBuffer(const _CCNxTlvDictionaryEntry *entry, int index) +{ + printf(" Entry %3d type %8s pointer %p\n", index, _ccnxTlvDictionaryEntryTypeToString(entry->entryType), (void *) entry->_entry.buffer); + parcBuffer_Display(entry->_entry.buffer, 6); +} + +static void +_ccnxTlvDictionary_DisplayInteger(const _CCNxTlvDictionaryEntry *entry, int index) +{ + printf(" Entry %3d type %8s value 0x%" PRIX64 " (%" PRIu64 ")\n", index, _ccnxTlvDictionaryEntryTypeToString(entry->entryType), entry->_entry.integer, entry->_entry.integer); +} + +static void +_ccnxTlvDictionary_DisplayIoVec(const _CCNxTlvDictionaryEntry *entry, int index) +{ + printf(" Entry %3d type %8s pointer %p\n", index, _ccnxTlvDictionaryEntryTypeToString(entry->entryType), (void *) entry->_entry.vec); + ccnxCodecNetworkBufferIoVec_Display(entry->_entry.vec, 6); +} + +static void +_ccnxTlvDictionary_DisplayJson(const _CCNxTlvDictionaryEntry *entry, int index) +{ + printf(" Entry %3d type %8s pointer %p\n", index, _ccnxTlvDictionaryEntryTypeToString(entry->entryType), (void *) entry->_entry.json); + char *string = parcJSON_ToString(entry->_entry.json); + printf("%s\n", string); + parcMemory_Deallocate((void **) &string); +} + +static void +_ccnxTlvDictionary_DisplayName(const _CCNxTlvDictionaryEntry *entry, int index) +{ + printf(" Entry %3d type %8s pointer %p\n", index, _ccnxTlvDictionaryEntryTypeToString(entry->entryType), (void *) entry->_entry.name); + ccnxName_Display(entry->_entry.name, 6); +} + +static void +_ccnxTlvDictionary_DisplayUnknown(const _CCNxTlvDictionaryEntry *entry, int index) +{ + printf(" Entry %3d type %8s pointer %p\n", index, _ccnxTlvDictionaryEntryTypeToString(entry->entryType), (void *) entry->_entry.buffer); +} + +static void +_ccnxTlvDictionary_DisplayListEntry(const _CCNxTlvDictionaryListEntry *entry, int listIndex, int position) +{ + printf(" List %3d Position %3d key 0x%04X pointer %p\n", listIndex, position, entry->key, (void *) entry->buffer); + parcBuffer_Display(entry->buffer, 6); +} + +void +ccnxTlvDictionary_Display(const CCNxTlvDictionary *dictionary, int indent) +{ + parcDisplayIndented_PrintLine(indent, "CCNxTlvDictionary@%p fastArraySize %zu listSize %zu dictionaryType %s schemaVersion %d refcount %" PRIu64 "\n", + (void *) dictionary, + dictionary->fastArraySize, + dictionary->listSize, + _ccnxTlvDictionaryTypeToString(dictionary->dictionaryType), + dictionary->schemaVersion, + parcObject_GetReferenceCount((PARCObject *) dictionary)); + + parcDisplayIndented_PrintLine(indent, " createTime %0.6f generation %u Info %p InfoFreeFunc %p\n", + (dictionary->creationTime.tv_sec + dictionary->creationTime.tv_usec * 1E-6), + dictionary->generation, + (void *) dictionary->info, + dictionary->infoFreeFunction); + + for (int i = 0; i < dictionary->fastArraySize; i++) { + if (dictionary->directArray[i].entryType != ENTRY_UNSET) { + switch (dictionary->directArray[i].entryType) { + case ENTRY_BUFFER: + _ccnxTlvDictionary_DisplayBuffer(&dictionary->directArray[i], i); + break; + + case ENTRY_INTEGER: + _ccnxTlvDictionary_DisplayInteger(&dictionary->directArray[i], i); + break; + + case ENTRY_IOVEC: + _ccnxTlvDictionary_DisplayIoVec(&dictionary->directArray[i], i); + break; + + case ENTRY_JSON: + _ccnxTlvDictionary_DisplayJson(&dictionary->directArray[i], i); + break; + + case ENTRY_NAME: + _ccnxTlvDictionary_DisplayName(&dictionary->directArray[i], i); + break; + + default: + _ccnxTlvDictionary_DisplayUnknown(&dictionary->directArray[i], i); + } + } + } + + for (int i = 0; i < dictionary->listSize; i++) { + _CCNxTlvDictionaryListEntry *entry = _getListHead(dictionary, i); + if (entry) { + int position = 0; + printf(" Displaying custom entry list index %3d head %p\n", i, (void *) entry); + while (entry) { + _ccnxTlvDictionary_DisplayListEntry(entry, i, position); + entry = entry->next; + } + } + } +} + +static bool +_ccnxTlvDictionaryEntry_Equals(const _CCNxTlvDictionaryEntry *a, const _CCNxTlvDictionaryEntry *b) +{ + if (a == NULL && b == NULL) { + return true; + } + + if (a == NULL || b == NULL) { + return false; + } + + bool equals = false; + if (a->entryType == b->entryType) { + switch (a->entryType) { + case ENTRY_UNSET: + equals = true; + break; + + case ENTRY_BUFFER: + equals = parcBuffer_Equals(a->_entry.buffer, b->_entry.buffer); + break; + + case ENTRY_OBJECT: + equals = parcObject_Equals(a->_entry.object, b->_entry.object); + break; + + case ENTRY_INTEGER: + equals = (a->_entry.integer == b->_entry.integer); + break; + + case ENTRY_IOVEC: + equals = ccnxCodecNetworkBufferIoVec_Equals(a->_entry.vec, b->_entry.vec); + break; + + case ENTRY_JSON: + equals = parcJSON_Equals(a->_entry.json, b->_entry.json); + break; + + case ENTRY_NAME: + equals = ccnxName_Equals(a->_entry.name, b->_entry.name); + break; + + default: + trapIllegalValue(a->entryType, "Cannot compare due to unknown entry type: %d", a->entryType); + } + } + return equals; +} + +static bool +_ccnxTlvDictionaryListEntry_Equals(const _CCNxTlvDictionaryListEntry *a, const _CCNxTlvDictionaryListEntry *b) +{ + if (a == NULL && b == NULL) { + return true; + } + + if (a == NULL || b == NULL) { + return false; + } + + if (a->key == b->key) { + if (parcBuffer_Equals(a->buffer, b->buffer)) { + return true; + } + } + return false; +} + +static bool +_ccnxTlvDictionary_ListEquals(const _CCNxTlvDictionaryListEntry *listHeadA, const _CCNxTlvDictionaryListEntry *listHeadB) +{ + if (listHeadA == NULL && listHeadB == NULL) { + return true; + } + + if (listHeadA == NULL || listHeadB == NULL) { + return false; + } + + // walk both linked lists in parallel + while (listHeadA && listHeadB) { + if (!_ccnxTlvDictionaryListEntry_Equals(listHeadA, listHeadB)) { + return false; + } + + listHeadA = listHeadA->next; + listHeadB = listHeadB->next; + } + + // they must both be NULL otherwise the lists did not end at same place + if (listHeadA == NULL && listHeadB == NULL) { + return true; + } + return false; +} + +/* + * precondition: we know they are not null and they have the same fastarray size + */ +static bool +_ccnxTlvDictionary_FastArrayEquals(const CCNxTlvDictionary *a, const CCNxTlvDictionary *b) +{ + bool equals = true; + for (int i = 0; i < a->fastArraySize && equals; i++) { + equals = _ccnxTlvDictionaryEntry_Equals(&a->directArray[i], &b->directArray[i]); + } + return equals; +} + +/* + * preconditiona: we know they are not null and they have the same list size + */ +static bool +_ccnxTlvDictionary_ListsEquals(const CCNxTlvDictionary *a, const CCNxTlvDictionary *b) +{ + bool equals = true; + for (int i = 0; i < a->listSize && equals; i++) { + _CCNxTlvDictionaryListEntry *entry_a = _getListHead(a, i); + _CCNxTlvDictionaryListEntry *entry_b = _getListHead(b, i); + equals = _ccnxTlvDictionary_ListEquals(entry_a, entry_b); + } + return equals; +} + +bool +ccnxTlvDictionary_Equals(const CCNxTlvDictionary *a, const CCNxTlvDictionary *b) +{ + if (a == NULL && b == NULL) { + return true; + } + + if (a == NULL || b == NULL) { + return false; + } + + // They are both non-null + bool equals = false; + if (a->fastArraySize == b->fastArraySize) { + if (a->listSize == b->listSize) { + if (a->dictionaryType == b->dictionaryType) { + if (a->schemaVersion == b->schemaVersion) { + if (_ccnxTlvDictionary_FastArrayEquals(a, b)) { + if (_ccnxTlvDictionary_ListsEquals(a, b)) { + equals = true; + } + } + } + } + } + } + return equals; +} diff --git a/libccnx-common/ccnx/common/internal/ccnx_TlvDictionary.h b/libccnx-common/ccnx/common/internal/ccnx_TlvDictionary.h new file mode 100755 index 00000000..bae0ec8b --- /dev/null +++ b/libccnx-common/ccnx/common/internal/ccnx_TlvDictionary.h @@ -0,0 +1,1127 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file ccnx_TlvDictionary.h + * @brief Stores pointers to PARCBuffers indexed by keys + * + * A message dictionary stores each field of a message in an array entry. The user of the dictionary needs to + * have a schema so it knows which array entry is which field. + * + * A message dictionary carries two distinguished fields that are not part of the array. The MessageType is + * Interest, ContentObject, or Control. The SchemaVersion is 0 or 1. These fields are independent of + * anything that is in the dictionary. + * + * The message dictionary creates two arrays. The first array is an array of PARCBuffer. The second array + * is a list of (type, PARCBuffer). This structure is designed such that well-known TLV keys + * are stored in the first array under well-known indicies. + * + * unknown TLV keys are stored in the second array, where each list corresponds to a TLV container. + * For example, an unknown TLV type found under the Content Object Metadata container would + * go in a list corresponding to the Metadata container and they be added to the list with the + * pair (type, PARCBuffer), where 'type' is the unknown TLV type. + * + */ + +#ifndef libccnx_ccnx_TlvDictionary_h +#define libccnx_ccnx_TlvDictionary_h + +#include <stdbool.h> +#include <sys/time.h> +#include <parc/algol/parc_Buffer.h> +#include <parc/algol/parc_JSON.h> + +#include <ccnx/common/internal/ccnx_MessageInterface.h> + +#include <ccnx/common/ccnx_Name.h> +#include <ccnx/common/codec/ccnxCodec_NetworkBuffer.h> + + +struct ccnx_tlv_dictionary; +typedef struct ccnx_tlv_dictionary CCNxTlvDictionary; + +typedef enum { + CCNxTlvDictionary_SchemaVersion_V0 = 0, + CCNxTlvDictionary_SchemaVersion_V1 = 1, +} CCNxTlvDictionary_SchemaVersion; + + +/** + * Creates a new TLV dictionary with the given size + * + * There will be 'bufferCount' array elements of type Buffer and + * 'listCount' elements of type List. Each array is indexed from 0. + * + * @param [in] bufferCount The number of Buffer elements to allocate within the dictionary. + * @param [in] listCount The number of List elements to allocate within the dictionary. + * + * @return NULL A new CCNxTlvDictionary object could not be allocated. + * @return CCNxTlvDictionary A new CCNxTlvDictionary instance with bufferCount Buffer and listCount List elements. + * + * Example: + * @code + * { + * ... + * + * CCNxTlvDictionary *dict = ccnxTlvDictionary_Create(5, 3); + * ccnxTlvDictionary_PutBuffer(dict, 3, nameBuffer); + * rtatlvDictionary_PutListBuffer(dict, 2, unknownType, unknownBuffer); + * } + * @endcode + */ +CCNxTlvDictionary *ccnxTlvDictionary_Create(size_t bufferCount, size_t listCount); + +/** + * Acquire a handle to the CCNxTlvDictionary instance. + * + * Note that new instance is not created, + * only that the given instance's reference count is incremented. + * Discard the reference by invoking `ccnxTlvDictionary_Release()`. + * + * @param [in] dictionary A pointer to the original instance. + * @return The value of the input parameter @p instance. + * + * Example: + * @code + * { + * ... + * + * CCNxTlvDictionary *dict = ccnxTlvDictionary_Acquire(instance); + * + * ccnxTlvDictionary_Release(&dict); + * } + * @endcode + */ +CCNxTlvDictionary *ccnxTlvDictionary_Acquire(const CCNxTlvDictionary *dictionary); + +/** + * Release a previously acquired reference to the specified instance, + * decrementing the reference count for the instance. + * + * The pointer to the instance is set to NULL as a side-effect of this function. + * + * If the invocation causes the last reference to the instance to be released, + * the instance is deallocated and the instance's implementation will perform + * additional cleanup and release other privately held references. + * + * @param [in] dictionaryPtr A pointer to a pointer to the instance to release. + * + * + * Example: + * @code + * { + * ... + * + * CCNxTlvDictionary *dict = ccnxTlvDictionary_Acquire(instance); + * + * ccnxTlvDictionary_Release(&dict); + * } + * @endcode + */ +void ccnxTlvDictionary_Release(CCNxTlvDictionary **dictionaryPtr); + +/** + * Adds a buffer to a dictionary entry + * + * Stores a reference count to the buffer + * + * @param [in] dictionary An CCNxTlvDictionary instance to which the Buffer entry will be added. + * @param [in] key The integer key that is to be associated with the new Buffer entry. + * @param [in] buffer The Buffer entry value to be inserted into the dictionary. + * + * @return true Key was not previously set + * @return false Key already has a buffer assigned to it + * + * Example: + * @code + * { + * ... + * + * CCNxTlvDictionary *dict = ccnxTlvDictionary_Create(5, 3); + * PARCBuffer *buffer = parcBuffer_Allocate(1); + * ccnxTlvDictionary_PutBuffer(dict, 1, buffer); + * // use the dictionary as needed + * } + * @endcode + */ +bool ccnxTlvDictionary_PutBuffer(CCNxTlvDictionary *dictionary, uint32_t key, const PARCBuffer *buffer); + +/** + * Determine if the value associated with the specified key is a Buffer. + * + * The key must be within the interval [0, bufferCount] for the dictionary. + * + * @param [in] dictionary The dictionary instance which will be examined. + * @param [in] key The key to use when indexing the dictionary. + * + * @return true The value associated with the key is of type Buffer. + * @return false The value associated with the key is -not- of type Buffer. + * + * Example: + * @code + * { + * ... + * + * CCNxTlvDictionary *dict = ccnxTlvDictionary_Create(5, 3); + * PARCBuffer *buffer = parcBuffer_Allocate(1); + * ccnxTlvDictionary_PutBuffer(dict, 1, buffer); + * bool truthy = ccnxTlvDictionary_IsValueBuffer(dict, 1); + * // truthy will be true + * } + * @endcode + */ +bool ccnxTlvDictionary_IsValueBuffer(const CCNxTlvDictionary *dictionary, uint32_t key); + +// caw TODO +bool ccnxTlvDictionary_IsValueObject(const CCNxTlvDictionary *dictionary, uint32_t key); + +/** + * Retrieves an entry from the dictionary from the specified key. + * + * @param [in] dictionary The dictionary instance which will be examined. + * @param [in] key The key to use when indexing the dictionary. + * + * @return NULL if key not found + * @return non-null The desired key + * + * Example: + * @code + * { + * ... + * + * CCNxTlvDictionary *dict = ccnxTlvDictionary_Create(5, 3); + * PARCBuffer *buffer = parcBuffer_Allocate(1); + * ccnxTlvDictionary_PutBuffer(dict, 1, buffer); + * PARCBuffer *copy = ccnxTlvDictionary_GetBuffer(dict, 1); + * // copy will be equal to the buffer instance + * } + * @endcode + */ +PARCBuffer *ccnxTlvDictionary_GetBuffer(const CCNxTlvDictionary *dictionary, uint32_t key); + +/** + * Put a new integer value in the dictionary, overwriting the old value if the key is + * already present. + * + * You can put an integer value many times, its OK to overwrite. + * They key must be UNSET or INTEGER, you cannot overwrite a different type + * The integer will be encoded as per the schema, it will not necessarily be an 8-byte field. + * + * @param [in] dictionary The dictionary instance to be modified + * @param [in] key The key used when indexing the dictionary + * @param [in] value The new value to insert into the dictionary assoicated with the above key + * + * @return true If the put/update was successful. + * @return false Otherwise (e.g., not UNSET/INTEGER type) + * + * Example: + * @code + * { + * ... + * + * CCNxTlvDictionary *dict = ccnxTlvDictionary_Create(5, 3); + * bool success = ccnxTlvDictionary_PutInteger(dict, 2, 1337); + * // success will be true since key 2 was UNSET + * } + * @endcode + */ +bool ccnxTlvDictionary_PutInteger(CCNxTlvDictionary *dictionary, uint32_t key, const uint64_t value); + +/** + * Determine if the value associated with the specified key is an Integer. + * + * The key must be within the interval [0, bufferCount] for the dictionary. + * + * @param [in] dictionary The dictionary instance which will be examined. + * @param [in] key The key to use when indexing the dictionary. + * + * @return true The value associated with the key is of type INTEGER. + * @return false The value associated with the key is -not- of type INTEGER. + * + * Example: + * @code + * { + * ... + * + * CCNxTlvDictionary *dict = ccnxTlvDictionary_Create(5, 3); + * ccnxTlvDictionary_PutInteger(dict, 2, 1337); + * bool truthy = ccnxTlvDictionary_IsValueInteger(dict, 2); + * // truthy will be true + * } + * @endcode + */ +bool ccnxTlvDictionary_IsValueInteger(const CCNxTlvDictionary *dictionary, uint32_t key); + +/** + * Returns an integer value stored in a key + * + * Will trapIllegalValue if the given key is not of type Integer. You should use + * ccnxTlvDictionary_IsValueInteger before calling. An unset key is not of type Integer. + * + * @param [in] dictionary The dictionary to check + * @param [in] key The key to retrieve + * + * @return number The value stored under the key + * + * Example: + * @code + * static uint32_t + * _fetchUint32(const CCNxTlvDictionary *interestDictionary, uint32_t key, uint32_t defaultValue) + * { + * if (ccnxTlvDictionary_IsValueInteger(interestDictionary, key)) { + * return (uint32_t) ccnxTlvDictionary_GetInteger(interestDictionary, key); + * } + * return defaultValue; + * } + * @endcode + */ +uint64_t ccnxTlvDictionary_GetInteger(const CCNxTlvDictionary *dictionary, uint32_t key); + +/** + * Put a new CCNxName into the `CCNxTlvDictionary` instance. + * + * You can only set a Name field once. The key must be UNSET. + * + * @param [in] dictionary The dictionary instance to be modified + * @param [in] key The key used when indexing the dictionary + * @param [in] name The new CCNxName value to insert into the dictionary assoicated with the above key + * + * @return true If the put/update was successful. + * @return false Otherwise (e.g., not UNSET type) + * + * Example: + * @code + * { + * ... + * + * CCNxTlvDictionary *dict = ccnxTlvDictionary_Create(5, 3); + * CCNxName *name = ccnxName_CreateFromCString("lci:/foo/bar"); + * bool success = ccnxTlvDictionary_PutName(dict, 2, name); + * // success will be true since key 2 was UNSET + * } + * @endcode + */ +bool ccnxTlvDictionary_PutName(CCNxTlvDictionary *dictionary, uint32_t key, const CCNxName *name); + +/** + * Determine if the value associated with the specified key is a CCNxName. + * + * The key must be within the interval [0, bufferCount] for the dictionary. + * + * @param [in] dictionary The dictionary instance which will be examined. + * @param [in] key The key to use when indexing the dictionary. + * + * @return true The value associated with the key is of type NAME. + * @return false The value associated with the key is -not- of type NAME. + * + * Example: + * @code + * { + * ... + * + * CCNxTlvDictionary *dict = ccnxTlvDictionary_Create(5, 3); + * CCNxName *name = ccnxName_CreateFromCString("lci:/foo/bar"); + * ccnxTlvDictionary_PutName(dict, 2, name); + * bool truthy = ccnxTlvDictionary_IsValueName(dict, 2); + * // truthy will be true since a name was inserted with key=2 + * } + * @endcode + */ +bool ccnxTlvDictionary_IsValueName(const CCNxTlvDictionary *dictionary, uint32_t key); + +/** + * Retrieve the CCNxName instance associated with the specified key. + * + * The key must be within the interval [0, bufferCount] for the dictionary. + * The entry is expected to be of type NAME, and will return NULL if not. + * + * @param [in] dictionary The dictionary instance which will be queried. + * @param [in] key The key to use when indexing the dictionary. + * + * @return NULL The entry associated with the key is not of type NAME. + * @return CCNxName A CCNxName instance associated with the specified key. + * + * Example: + * @code + * { + * ... + * + * CCNxTlvDictionary *dict = ccnxTlvDictionary_Create(5, 3); + * CCNxName *name = ccnxName_CreateFromCString("lci:/foo/bar"); + * ccnxTlvDictionary_PutName(dict, 2, name); + * CCNxName *copy = ccnxTlvDictionary_GetName(dict, 2); + * // do something with copy + * } + * @endcode + */ +CCNxName *ccnxTlvDictionary_GetName(const CCNxTlvDictionary *dictionary, uint32_t key); + +/** + * Insert a `CCNxCodecNetworkBufferIoVec` instance into the dictionary. + * + * Stores a scatter/gather network buffer. Could be either the wire format we recieve + * or the wire format we're about to send. The resulting entry type will be IOVEC. + * + * @param [in] dictionary The dictionary instance to be modified + * @param [in] key The key used when indexing the dictionary + * @param [in] vec The new CCNxCodecNetworkBufferIoVec value to insert into the dictionary assoicated with the above key + * + * @return true If the put/update was successful. + * @return false Otherwise (e.g., not UNSET type) + * + * Example: + * @code + * { + * ... + * + * CCNxTlvDictionary *dict = ccnxTlvDictionary_Create(5, 3); + * CCNxCodecNetworkBuffer *buffer = ccnxCodecNetworkBuffer_Create(&ParcMemoryMemoryBlock, NULL); + * CCNxCodecNetworkBufferIoVec *iovec = ccnxCodecNetworkBuffer_CreateIoVec(buffer); + * bool success = ccnxTlvDictionary_PutName(dict, 2, iovec); + * // success will be true since key 2 was UNSET + * } + * @endcode + */ +bool ccnxTlvDictionary_PutIoVec(CCNxTlvDictionary *dictionary, uint32_t key, const CCNxCodecNetworkBufferIoVec *vec); + +/** + * Determine if the value associated with the specified key is a CCNxCodecNetworkBufferIoVec. + * + * @param [in] dictionary The dictionary instance to be examined + * @param [in] key The key used when indexing the dictionary + * + * @return true The TLV dictionary has the given key and it is of type IOVEC + * @return false Otherwise + * + * Example: + * @code + * { + * ... + * + * CCNxTlvDictionary *dict = ccnxTlvDictionary_Create(5, 3); + * CCNxCodecNetworkBuffer *buffer = ccnxCodecNetworkBuffer_Create(&ParcMemoryMemoryBlock, NULL); + * CCNxCodecNetworkBufferIoVec *iovec = ccnxCodecNetworkBuffer_CreateIoVec(buffer); + * ccnxTlvDictionary_PutName(dict, 2, iovec); + * bool truthy = ccnxTlvDictionary_IsValueIoVec(dict, 2); + * // truthy will be true since key 2 was IOVEC and previously inserted + * } + * @endcode + */ +bool ccnxTlvDictionary_IsValueIoVec(const CCNxTlvDictionary *dictionary, uint32_t key); + +/** + * Retrieve the CCNxCodecNetworkBufferIoVec instance associated with the specified key. + * + * The key must be within the interval [0, bufferCount] for the dictionary. + * The entry is expected to be of type IOVEC, and will return NULL if not. + * + * @param [in] dictionary The dictionary instance which will be queried. + * @param [in] key The key to use when indexing the dictionary. + * + * @return NULL The entry associated with the key is not of type IOVEC. + * @return CCNxCodecNetworkBufferIoVec A CCNxCodecNetworkBufferIoVec instance associated with the specified key. + * + * Example: + * @code + * { + * ... + * + * CCNxTlvDictionary *dict = ccnxTlvDictionary_Create(5, 3); + * CCNxCodecNetworkBuffer *buffer = ccnxCodecNetworkBuffer_Create(&ParcMemoryMemoryBlock, NULL); + * CCNxCodecNetworkBufferIoVec *iovec = ccnxCodecNetworkBuffer_CreateIoVec(buffer); + * ccnxTlvDictionary_PutName(dict, 2, iovec); + * CCNxCodecNetworkBufferIoVec *copy = ccnxTlvDictionary_GetIoVec(dict, 2); + * // do something with copy + * } + * @endcode + */ +CCNxCodecNetworkBufferIoVec *ccnxTlvDictionary_GetIoVec(const CCNxTlvDictionary *dictionary, uint32_t key); + +/** + * Insert a new List item into the dictionary. + * + * The key must be within the interval [0, bufferCount] for the dictionary. + * You can only set a Buffer field once. The key must be UNSET. + * + * @param [in] dictionary The dictionary instance to be modified + * @param [in] listKey The list key used when indexing the dictionary lists + * @param [in] key The key type of the element being inserted into the list + * @param [in] buffer The new PARCBuffer value to insert into the dictionary list indexed by the listKey + * + * @return true If the put/update was successful. + * @return false Otherwise (e.g., not UNSET type) + * + * Example: + * @code + * { + * ... + * + * CCNxTlvDictionary *dict = ccnxTlvDictionary_Create(5, 3); + * PARCBuffer *buffer = parcBuffer_Allocate(1); + * bool success = ccnxTlvDictionary_PutListBuffer(dict, 1, 1, buffer); + * // success will be true since list key 1 was UNSET, and the new item will have key type 1 + * } + * @endcode + */ +bool ccnxTlvDictionary_PutListBuffer(CCNxTlvDictionary *dictionary, uint32_t listKey, uint32_t key, const PARCBuffer *buffer); + +/** + * Insert a new `PARCJSON` instance into the dictionary. + * + * The caller may destroy its reference to JSON since the type is deep-copied internally. + * The key must be within the dictionary, and the entry must be UNSET. + * + * @param [in] dictionary The dictionary instance to be modified + * @param [in] key The key used when indexing the dictionary + * @param [in] json The new PARCJSON value to insert into the dictionary associated with the above key + * + * @return true If the put/update was successful. + * @return false Otherwise (e.g., not UNSET type) + * + * Example: + * @code + * { + * ... + * + * CCNxTlvDictionary *dict = ccnxTlvDictionary_Create(5, 3); + * PARCJSON *json = ccnxJson_CreateNumber(1); + * bool success = ccnxTlvDictionary_PutJson(dict, 1, json); + * // success will be true since the key was UNSET + * } + * @endcode + */ +bool ccnxTlvDictionary_PutJson(CCNxTlvDictionary *dictionary, uint32_t key, const PARCJSON *json); + +/** + * Determine if the value associated with the specified key is a `PARCJSON` instance. + * + * @param [in] dictionary The dictionary instance to be examined + * @param [in] key The key used when indexing the dictionary + * + * @return true The TLV dictionary has the given key and it is of type JSON + * @return false Otherwise + * + * Example: + * @code + * { + * ... + * + * CCNxTlvDictionary *dict = ccnxTlvDictionary_Create(5, 3); + * PARCJSON *json = ccnxJson_CreateNumber(1); + * ccnxTlvDictionary_PutJson(dict, 1, json); + * bool truthy = ccnxTlvDictionary_IsValueJson(dict, 1); + * // truthy will be true since the JSON object was previously inserted + * } + * @endcode + */ +bool ccnxTlvDictionary_IsValueJson(const CCNxTlvDictionary *dictionary, uint32_t key); + +/** + * Retrieve the `PARCJSON` instance associated with the specified key. + * + * The key must be within the interval [0, bufferCount] for the dictionary. + * The entry is expected to be of type JSON, and will return NULL if not. + * + * @param [in] dictionary The dictionary instance which will be queried. + * @param [in] key The key to use when indexing the dictionary. + * + * @return NULL The entry associated with the key is not of type JSON. + * @return PARCJSON A PARCJSON instance associated with the specified key. + * + * Example: + * @code + * { + * ... + * + * CCNxTlvDictionary *dict = ccnxTlvDictionary_Create(5, 3); + * PARCJSON *json = ccnxJson_CreateNumber(1); + * ccnxTlvDictionary_PutJson(dict, 1, json); + * PARCJSON *copy = ccnxTlvDictionary_GetJson(dict, 1); + * // do something with copy + * } + * @endcode + */ +PARCJSON *ccnxTlvDictionary_GetJson(const CCNxTlvDictionary *dictionary, uint32_t key); + +/** + * Insert a new `PARCJSON` instance into the dictionary. + * + * The caller may destroy its reference to PARCObject since the type is deep-copied internally. + * The key must be within the dictionary, and the entry must be UNSET. + * + * @param [in] dictionary The dictionary instance to be modified + * @param [in] key The key used when indexing the dictionary + * @param [in] json The new PARCObject value to insert into the dictionary associated with the above key + * + * @return true If the put/update was successful. + * @return false Otherwise (e.g., not UNSET type) + * + * Example: + * @code + * { + * ... + * + * CCNxTlvDictionary *dict = ccnxTlvDictionary_Create(5, 3); + * PARCObject *object = ccnxName_CreateFromCString("ccnx/test"); + * bool success = ccnxTlvDictionary_PutObject(dict, 1, object); + * // success will be true since the key was UNSET + * } + * @endcode + */ +bool ccnxTlvDictionary_PutObject(CCNxTlvDictionary *dictionary, uint32_t key, const PARCObject *json); + +/** + * Determine if the value associated with the specified key is a `PARCObject` instance. + * + * @param [in] dictionary The dictionary instance to be examined + * @param [in] key The key used when indexing the dictionary + * + * @return true The TLV dictionary has the given key and it is of type PARCObject + * @return false Otherwise + * + * Example: + * @code + * { + * ... + * + * CCNxTlvDictionary *dict = ccnxTlvDictionary_Create(5, 3); + * PARCObject *object = ccnxName_CreateFromCString("ccnx/test"); + * ccnxTlvDictionary_PutObject(dict, 1, object); + * bool truthy = ccnxTlvDictionary_IsValueObject(dict, 1); + * // truthy will be true since the PARCObject was previously inserted + * } + * @endcode + */ +bool ccnxTlvDictionary_IsValueObject(const CCNxTlvDictionary *dictionary, uint32_t key); + +/** + * Retrieve the `PARCObject` instance associated with the specified key. + * + * The key must be within the interval [0, bufferCount] for the dictionary. + * The entry is expected to be of type PARCObject, and will return NULL if not. + * + * @param [in] dictionary The dictionary instance which will be queried. + * @param [in] key The key to use when indexing the dictionary. + * + * @return NULL The entry associated with the key is not of type PARCObject. + * @return PARCObject A PARCObject instance associated with the specified key. + * + * Example: + * @code + * { + * ... + * + * CCNxTlvDictionary *dict = ccnxTlvDictionary_Create(5, 3); + * PARCObject *object = ccnxName_CreateFromCString("ccnx/test"); + * ccnxTlvDictionary_PutObject(dict, 1, object); + * PARCObject *copy = ccnxTlvDictionary_GetObject(dict, 1); + * // do something with copy + * } + * @endcode + */ +PARCObject *ccnxTlvDictionary_GetObject(const CCNxTlvDictionary *dictionary, uint32_t key); + +/** + * Fetches a buffer from the ordinal position 'listItem' from the list key 'key' + * + * The entry 'key' must be type list. + * + * @param [in] dictionary The dictionary instance being examined + * @param [in] listKey The key used to identify the list to be searched + * @param [in] listPosition The index within the target list of the dictionary + * @param [out] bufferPtr If position is found, the buffer at that position + * @param [out] keyPtr If position is found, the key of the buffer + * + * @return true position found + * @return false position not found + * + * Example: + * @code + * { + * ... + * + * CCNxTlvDictionary *dict = ccnxTlvDictionary_Create(5, 3); + * PARCBuffer *buffer = parcBuffer_Allocate(1); + * ccnxTlvDictionary_PutListBuffer(dict, 1, 1, buffer); + * PARCBuffer *copy = NULL; + * uint32_t key = 0; + * ccnxTlvDictionary_ListGetByPosition(dict, 1, 0, ©, &key); + * // use the copy and key as necessary + * } + * @endcode + */ +bool ccnxTlvDictionary_ListGetByPosition(const CCNxTlvDictionary *dictionary, uint32_t listKey, size_t listPosition, PARCBuffer **bufferPtr, uint32_t *keyPtr); + +/** + * Returns the first buffer in the list identified by 'listkey' with the buffer type 'type' + * + * @param [in] dictionary The dictionary instance being examined + * @param [in] listKey The key used to index into the dictionary lists + * @param [in] type The type of element used to search within the dictionary list + * + * @return PARCBuffer* Pointer to the first PARCBuffer instance whose type matches the type argument in the target list + * @return NULL If no element in the target list has the specified type. + * + * Example: + * @code + * { + * ... + * + * CCNxTlvDictionary *dict = ccnxTlvDictionary_Create(5, 3); + * PARCBuffer *buffer = parcBuffer_Allocate(1); + * ccnxTlvDictionary_PutListBuffer(dict, 1, 1, buffer); + * PARCBuffer *copy = ccnxTlvDictionary_ListGetByType(dict, 1, 1); + * // copy and buffer will have equal PARCBuffer values + * } + * @endcode + */ +PARCBuffer *ccnxTlvDictionary_ListGetByType(const CCNxTlvDictionary *dictionary, uint32_t listKey, uint32_t type); + +/** + * Retrieve the number of elements in the list identified by 'key' + * + * The dictionary entry 'key' must be of type list. + * + * @param [in] dictionary The dictionary instance to be examined + * @param [in] listKey The key used to index into the list whose size will be checked + * + * @return The size of the list associated with the given list key + * + * Example: + * @code + * { + * ... + * + * CCNxTlvDictionary *dict = ccnxTlvDictionary_Create(5, 3); + * PARCBuffer *buffer = parcBuffer_Allocate(1); + * ccnxTlvDictionary_PutListBuffer(dict, 1, 1, buffer); + * size_t listSize = ccnxTlvDictionary_ListSize(dictionary, 1); + * // listSize will be 1 + * } + * @endcode + */ +size_t ccnxTlvDictionary_ListSize(const CCNxTlvDictionary *dictionary, uint32_t listKey); + +/** + * Set the type of message which this dictionary stores/represents to be an Interest. + * + * @param [in] dictionary The dictionary instance whose type is to be modified + * @param [in] schemaVersion The schema version which is used by the dictionary for message encoding. + * + * Example: + * @code + * { + * ... + * + * CCNxTlvDictionary *dict = ccnxTlvDictionary_Create(5, 3); + * ccnxTlvDictionary_SetMessageType_Interest(dict, CCNxTlvDictionary_SchemaVersion_V0); + * // use the dictionary + * } + * @endcode + */ +void ccnxTlvDictionary_SetMessageType_Interest(CCNxTlvDictionary *dictionary, CCNxTlvDictionary_SchemaVersion schemaVersion); + +/** + * Set the type of message which this dictionary stores/represents to be a ContentObject. + * + * @param [in] dictionary The dictionary instance whose type is to be modified + * @param [in] schemaVersion The schema version which is used by the dictionary for message encoding. + * + * Example: + * @code + * { + * ... + * + * CCNxTlvDictionary *dict = ccnxTlvDictionary_Create(5, 3); + * ccnxTlvDictionary_SetMessageType_ContentObject(dict, CCNxTlvDictionary_SchemaVersion_V1); + * // use the dictionary + * } + * @endcode + */ +void ccnxTlvDictionary_SetMessageType_ContentObject(CCNxTlvDictionary *dictionary, CCNxTlvDictionary_SchemaVersion schemaVersion); + +/** + * Set the type of message which this dictionary stores/represents to be a Control (message). + * + * @param [in] dictionary The dictionary instance whose type is to be modified + * @param [in] schemaVersion The schema version which is used by the dictionary for message encoding. + * + * Example: + * @code + * { + * ... + * + * CCNxTlvDictionary *dict = ccnxTlvDictionary_Create(5, 3); + * ccnxTlvDictionary_SetMessageType_Control(dict, CCNxTlvDictionary_SchemaVersion_V1); + * // use the dictionary + * } + * @endcode + */ +void ccnxTlvDictionary_SetMessageType_Control(CCNxTlvDictionary *dictionary, CCNxTlvDictionary_SchemaVersion schemaVersion); + +/** + * Set the type of message which this dictionary stores/represents to be a Manifest (message). + * + * @param [in] dictionary The dictionary instance whose type is to be modified. + * @param [in] schemaVersion The schema version which is used by the dictionary for message encoding. + * + * Example: + * @code + * { + * ... + * + * CCNxTlvDictionary *dict = ccnxTlvDictionary_Create(5, 3); + * ccnxTlvDictionary_SetMessageType_Manifest(dict, CCNxTlvDictionary_SchemaVersion_V1); + * // use the dictionary + * } + * @endcode + */ +void ccnxTlvDictionary_SetMessageType_Manifest(CCNxTlvDictionary *dictionary, CCNxTlvDictionary_SchemaVersion schemaVersion); + +/** + * Set the type of message which this dictionary stores/represents to be an InterestReturn. + * + * @param [in] dictionary The dictionary instance whose type is to be modified + * @param [in] schemaVersion The schema version which is used by the dictionary for message encoding. + * + * Example: + * @code + * { + * ... + * + * CCNxTlvDictionary *dict = ccnxTlvDictionary_Create(5, 3); + * ccnxTlvDictionary_SetMessageType_InterestReturn(dict, CCNxTlvDictionary_SchemaVersion_V0); + * // use the dictionary + * } + * @endcode + */ +void ccnxTlvDictionary_SetMessageType_InterestReturn(CCNxTlvDictionary *dictionary, CCNxTlvDictionary_SchemaVersion schemaVersion); + + +/** + * Retrieve the schema version `CCNxTlvDictionary_SchemaVersion` used to encode the contents of the dictionary. + * + * Currently, only two schema versions are supported: CCNxTlvDictionary_SchemaVersion_V0 and CCNxTlvDictionary_SchemaVersion_V1 + * + * @param [in] dictionary The dictionary instance being examined + * + * @return The `CCNxTlvDictionary_SchemaVersion` version used for encoding. + * + * Example: + * @code + * { + * ... + * + * CCNxTlvDictionary *dict = ccnxTlvDictionary_Create(5, 3); + * ccnxTlvDictionary_SetMessageType_Interest(dict, CCNxTlvDictionary_SchemaVersion_V0); + * CCNxTlvDictionary_SchemaVersion ver = ccnxTlvDictionary_GetSchemaVersion(dict); + * // ver will be equal to CCNxTlvDictionary_SchemaVersion_V0 + * } + * @endcode + */ +CCNxTlvDictionary_SchemaVersion ccnxTlvDictionary_GetSchemaVersion(const CCNxTlvDictionary *dictionary); + +/** + * Determine if the specified dictionary represents an Interest message. + * + * @param [in] dictionary The dictionary instance being examined. + * + * @return true The dictionary represents an Interest message. + * @return false Otherwise + * + * Example: + * @code + * { + * ... + * + * CCNxTlvDictionary *dict = ccnxTlvDictionary_Create(5, 3); + * ccnxTlvDictionary_SetMessageType_Interest(dict, CCNxTlvDictionary_SchemaVersion_V0); + * bool isInterest = ccnxTlvDictionary_IsInterest(dict); + * // isInterest will be true + * } + * @endcode + */ +bool ccnxTlvDictionary_IsInterest(const CCNxTlvDictionary *dictionary); + +/** + * Determine if the specified dictionary represents an InterestReturn message. + * + * @param [in] dictionary The dictionary instance being examined. + * + * @return true The dictionary represents an InterestReturn message. + * @return false Otherwise + * + * Example: + * @code + * { + * ... + * + * CCNxTlvDictionary *dict = ccnxTlvDictionary_Create(5, 3); + * ccnxTlvDictionary_SetMessageType_InterestReturn(dict, CCNxTlvDictionary_SchemaVersion_V1); + * bool isInterestReturn = ccnxTlvDictionary_IsInterestReturn(dict); + * // isInterest will be true + * } + * @endcode + */ +bool ccnxTlvDictionary_IsInterestReturn(const CCNxTlvDictionary *dictionary); + +/** + * Determine if the specified dictionary represents a ContentObject message. + * + * @param [in] dictionary The dictionary instance being examined. + * + * @return true The dictionary represents a ContentObject message. + * @return false Otherwise + * + * Example: + * @code + * { + * ... + * + * CCNxTlvDictionary *dict = ccnxTlvDictionary_Create(5, 3); + * ccnxTlvDictionary_SetMessageType_ContentObject(dict, CCNxTlvDictionary_SchemaVersion_V0); + * bool isContentObject = ccnxTlvDictionary_IsContentObject(dict); + * // isContentObject will be true + * } + * @endcode + */ +bool ccnxTlvDictionary_IsContentObject(const CCNxTlvDictionary *dictionary); + +/** + * Determine if the specified dictionary represents a Control message. + * + * @param [in] dictionary The dictionary instance being examined. + * + * @return true The dictionary represents a Control message. + * @return false Otherwise + * + * Example: + * @code + * { + * ... + * + * CCNxTlvDictionary *dict = ccnxTlvDictionary_Create(5, 3); + * ccnxTlvDictionary_SetMessageType_Control(dict, CCNxTlvDictionary_SchemaVersion_V0); + * bool isControl = ccnxTlvDictionary_IsControl(dict); + * // isControl will be true + * } + * @endcode + */ +bool ccnxTlvDictionary_IsControl(const CCNxTlvDictionary *dictionary); + +/** + * Determine if the specified dictionary represents a Manifest message. + * + * @param [in] dictionary The dictionary instance being examined. + * + * @return true The dictionary represents a Manifest message. + * @return false Otherwise + * + * Example: + * @code + * { + * ... + * + * CCNxTlvDictionary *dict = ccnxTlvDictionary_Create(5, 3); + * ccnxTlvDictionary_SetMessageType_Manifest(dict, CCNxTlvDictionary_SchemaVersion_V1); + * bool isManifest = ccnxTlvDictionary_IsManifest(dict); + * // isManifest will be true + * } + * @endcode + */ +bool ccnxTlvDictionary_IsManifest(const CCNxTlvDictionary *dictionary); + +/** + * If in DEBUG mode, returns how long the message has been in the system + * + * If not in DEBUG mode, will always be {.tv_sec = 0, .tv_usec = 0}. The time is based + * on gettimeofday(). + * + * Measured since the time when the dictionary was created + * + * @param [in] dictionary The dictionary instance whose lifetime is being queried + * + * @return struct timeval The lifetime of the dictionary message. + * + * Example: + * @code + * { + * ... + * CCNxTlvDictionary *dict = ccnxTlvDictionary_Create(5, 3); + * // do some things ... + * struct timeval = ccnxTlvDictionary_GetLifetime(dict); + * // use the time as needed + * } + * @endcode + */ +struct timeval ccnxTlvDictionary_GetLifetime(const CCNxTlvDictionary *dictionary); + +/** + * Display the dictionary and its contents + * + * The contents of the dictionary are printed to stdout. This is often useful + * for verbose debugging purposes. + * + * @param [in] indent The number of tabs to indent all lines printed on stdout + * @param [in] dictionary The dictionary instance whose contents will be printed to stdout + * + * Example: + * @code + * { + * ... + * CCNxTlvDictionary *dict = ccnxTlvDictionary_Create(5, 3); + * ccnxTlvDictionary_Display(dict, 0); + * } + * @endcode + */ +void ccnxTlvDictionary_Display(const CCNxTlvDictionary *dictionary, int indent); + +/** + * Determine if two CCNxTlvDictionary instances are equal. + * + * Two CCNxTlvDictionary instances are equal if, and only if, they contain the same number + * of keys, the keys are equal, and each key points to the same value. + * + * The following equivalence relations on non-null `CCNxTlvDictionary` instances are maintained: + * + * * It is reflexive: for any non-null reference value x, `ccnxTlvDictionary_Equals(x, x)` + * must return true. + * + * * It is symmetric: for any non-null reference values x and y, + * `ccnxTlvDictionary_Equals(x, y)` must return true if and only if + * `ccnxTlvDictionary_Equals(y, x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `ccnxTlvDictionary_Equals(x, y)` returns true and + * `ccnxTlvDictionary_Equals(y, z)` returns true, + * then `ccnxTlvDictionary_Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple + * invocations of `ccnxTlvDictionary_Equals(x, y)` consistently return true or + * consistently return false. + * + * * For any non-null reference value x, `ccnxTlvDictionary_Equals(x, NULL)` must + * return false. + * + * @param a A pointer to a `CCNxTlvDictionary` instance. + * @param b A pointer to a `CCNxTlvDictionary` instance. + * + * NULL == NULL, non-NULL != NULL, otherwise the dictionaries need to be the same + * type, schema, and all fields must compare for equality. + * + * Equals does not include the CreationTime (lifetime) or the SetInfo() value. + * + * @return true Dictionaries are equal + * @return false Dictionaries differ in some way + * + * Example: + * @code + * { + * ... + * CCNxTlvDictionary *dict1 = ccnxTlvDictionary_Create(5, 3); + * CCNxTlvDictionary *dict2 = ccnxTlvDictionary_Create(5, 3); + * if (ccnxTlvDictionary_Equals(dict1, dict2)) { + * // true + * } else { + * // false + * } + * } + * @endcode + */ +bool ccnxTlvDictionary_Equals(const CCNxTlvDictionary *a, const CCNxTlvDictionary *b); + +/** + * Allocates a new instance of the specified CCNxTlvDictionary that is + * a "Shallow" copy of the original. The new instance contains the + * same contents as the original CCNxTlvDictionary but an Acquire() + * operation is used where possible to form a new link to the original + * contained content (a Copy() operation is used otherwise). Note that + * this means that modifying the content of the copy will, in most + * cases, modify the content of the original. In any case, the + * contents are protected from premature deallocation. + * + * @param [in] source The dictionary to copy + * + * @return CCNxTlvDictionary A pointer to a copy of the source dictionary + * + * Example: + * @code + * { + * ... + * CCNxTlvDictionary *source = ccnxTlvDictionary_Create(5, 3); + * PARCBuffer *buffer = parcBuffer_Allocate(1); + * ccnxTlvDictionary_PutListBuffer(source, 1, 1, buffer); + * ... + * CCNxTlvDictionary *copy = ccnxTlvDictionary_ShallowCopy(source); + * PARCBuffer *buffcopy; + * uint32_t key = 0; + * ccnxTlvDictionary_ListGetByPosition(copy, 1, 0, &buffcopy, &key); + * ... + * assertTrue(ccnxTlvDictionary_Equals(source, copy), "Error: not a copy"); + * assertTrue(buffcopy == buffer, "Error: not a shallow copy"); + * } + * @endcode + */ +CCNxTlvDictionary *ccnxTlvDictionary_ShallowCopy(const CCNxTlvDictionary *source); + +/** + * Set the pointer to the implementation used to create the message type represented by this + * CCNxTlvDictionary. For example, if the CCNxTlvDictionary represents a V1 ContentObject, + * the implementation pointer should be set to &CCNxContentObjectFacadeV1_Implementation. + * The type can be inferred from the dictionary's schemaVersion and messageType. + * + * Consumers of this CCNxTlvDictionary should use the implementation pointer to access + * fields in the dictionary. Examples of implementations would be those that define + * {@link CCNxContentObjectInterface} or {@link CCNxInterestInterface}, such as + * {@link CCNxContentObjectFacadeV1_Implementation} or {CCNxInterestFacadeV1_Implementation}. + * + * @param [in] dictionary The dictionary instance on which to set the implementation pointer. + * @param [in] implementation The address of the implementation to be used to access this dictionary. + * + * Example: + * @code + * { + * ... + * CCNxTlvDictionary *dict = ccnxTlvDictionary_Create(5, 3); + * ccnxTlvDictionary_SetMessageInterface(&CCNxContentObjectFacadeV1_Implementation); + * } + * @endcode + * @see `ccnxTlvDictionary_GetMessageTypeInterface` + */ +void ccnxTlvDictionary_SetMessageInterface(CCNxTlvDictionary *dictionary, const CCNxMessageInterface *implementation); + +/** + * Return the address of the implementation used to access fields in the the message represented by this + * CCNxTlvDictionary instance. The implementation pointer would typically be + * {@link CCNxContentObjectFacadeV1_Implementation} or {@link CCNxContentObjectFacadeV1_Implementation}. + * If it is not set, it can be inferred from the messageType and the schemaVersion. + * + * @param [in] dictionary The dictionary instance from which to retrieve the implementation pointer. + * + * Example: + * @code + * { + * ... + * CCNxTlvDictionary *dict = ccnxTlvDictionary_Create(5, 3); + * ccnxTlvDictionary_SetMessageTypeInterface(&CCNxContentObjectFacadeV1_Implementation); + * CCNxContentObjectInterface *impl = ccnxTlvDictionary_GetMessageTypeInterface(dict); + * } + * @endcode + * @see `ccnxTlvDictionary_SetMessageTypeInterface` + * @see `ccnxContentObjectInterface_GetInterface` + * @see `ccnxInterestInterface_GetInterface` + */ +CCNxMessageInterface *ccnxTlvDictionary_GetMessageInterface(const CCNxTlvDictionary *dictionary); +#endif // libccnx_ccnx_TlvDictionary_h diff --git a/libccnx-common/ccnx/common/internal/ccnx_TlvError.c b/libccnx-common/ccnx/common/internal/ccnx_TlvError.c new file mode 100755 index 00000000..0f07ab9e --- /dev/null +++ b/libccnx-common/ccnx/common/internal/ccnx_TlvError.c @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <limits.h> + +#include <parc/algol/parc_Memory.h> +#include <LongBow/runtime.h> + +#include <ccnx/common/internal/ccnx_TlvError.h> + +struct error_messages { + CCNxTlvErrorCodes code; + const char *message; +} TlvErrorMessages[] = { + { .code = TLV_ERR_NO_ERROR, .message = "No error" }, + { .code = TLV_ERR_VERSION, .message = "Unsupported version" }, + { .code = TLV_ERR_PACKETTYPE, .message = "Unsupported packet type" }, + { .code = TLV_ERR_BEYOND_PACKET_END, .message = "Field goes beyond end of packet" }, + { .code = TLV_ERR_TOO_LONG, .message = "Length too long for parent container" }, + { .code = TLV_ERR_NOT_FIXED_SIZE, .message = "Fixed size Type wrong Length" }, + { .code = TLV_ERR_DUPLICATE_FIELD, .message = "Duplicate field" }, + { .code = TLV_ERR_EMPTY_SPACE, .message = "The sum of child TLVs did not add up to parent container length" }, + + // missing mandatory field errors + { .code = TLV_MISSING_MANDATORY, .message = "Missing mandatory field" }, + + { .code = TLV_ERR_DECODE, .message = "Decoding error" }, + + // end of list sentinel, the NULL determines the end of list + { .code = UINT16_MAX, .message = NULL } +}; + + +const char * +ccnxTlvErrors_ErrorMessage(CCNxTlvErrorCodes code) +{ + for (int i = 0; TlvErrorMessages[i].message != NULL; i++) { + if (TlvErrorMessages[i].code == code) { + return TlvErrorMessages[i].message; + } + } + return "No error message found"; +} + +// ========================================================================== + +struct ccnx_tlv_error { + CCNxTlvErrorCodes code; + const char *functionName; + int line; + size_t byteOffset; + unsigned refcount; + char *toString; +}; + +CCNxTlvError * +ccnxTlvError_Create(CCNxTlvErrorCodes code, const char *func, int line, size_t byteOffset) +{ + CCNxTlvError *error = parcMemory_AllocateAndClear(sizeof(CCNxTlvError)); + assertNotNull(error, "parcMemory_AllocateAndClear(%u) returned NULL", sizeof(CCNxTlvError)); + error->code = code; + error->functionName = func; + error->line = line; + error->byteOffset = byteOffset; + error->toString = NULL; // computed on the fly + error->refcount = 1; + return error; +} + +CCNxTlvError * +ccnxTlvError_Acquire(CCNxTlvError *error) +{ + assertNotNull(error, "Parameter error must be non-null"); + assertTrue(error->refcount > 0, "Parameter has 0 refcount, not valid"); + + error->refcount++; + return error; +} + +void +ccnxTlvError_Release(CCNxTlvError **errorPtr) +{ + assertNotNull(errorPtr, "Parameter must be non-null double pointer"); + assertNotNull(*errorPtr, "Parameter must derefernece to non-null pointer"); + CCNxTlvError *error = *errorPtr; + + assertTrue(error->refcount > 0, "Parameter has 0 refcount, not valid"); + error->refcount--; + if (error->refcount == 0) { + if (error->toString) { + // this is asprintf generated + free(error->toString); + } + parcMemory_Deallocate((void **) &error); + } + *errorPtr = NULL; +} + +size_t +ccnxTlvError_GetByteOffset(const CCNxTlvError *error) +{ + assertNotNull(error, "Parameter must be non-null"); + return error->byteOffset; +} + + +CCNxTlvErrorCodes +ccnxTlvError_GetErrorCode(const CCNxTlvError *error) +{ + assertNotNull(error, "Parameter must be non-null"); + return error->code; +} + +const char * +ccnxTlvError_GetFunction(const CCNxTlvError *error) +{ + assertNotNull(error, "Parameter must be non-null"); + return error->functionName; +} + +int +ccnxTlvError_GetLine(const CCNxTlvError *error) +{ + assertNotNull(error, "Parameter must be non-null"); + return error->line; +} + +const char * +ccnxTlvError_GetErrorMessage(const CCNxTlvError *error) +{ + assertNotNull(error, "Parameter must be non-null"); + return ccnxTlvErrors_ErrorMessage(error->code); +} + +const char * +ccnxTlvError_ToString(CCNxTlvError *error) +{ + assertNotNull(error, "Parameter must be non-null"); + if (error->toString) { + return error->toString; + } + + asprintf(&error->toString, "TLV error: %s:%d offset %zu: %s", + error->functionName, + error->line, + error->byteOffset, + ccnxTlvError_GetErrorMessage(error)); + + return error->toString; +} diff --git a/libccnx-common/ccnx/common/internal/ccnx_ValidationFacadeV1.c b/libccnx-common/ccnx/common/internal/ccnx_ValidationFacadeV1.c new file mode 100755 index 00000000..d9f86895 --- /dev/null +++ b/libccnx-common/ccnx/common/internal/ccnx_ValidationFacadeV1.c @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +#include <config.h> +#include <stdio.h> +#include <LongBow/runtime.h> + +#include <ccnx/common/internal/ccnx_ValidationFacadeV1.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_TlvDictionary.h> + +static void +_assertInvariants(const CCNxTlvDictionary *message) +{ + assertNotNull(message, "Parameter message must be non-null"); +} + +PARCBuffer * +ccnxValidationFacadeV1_GetKeyId(const CCNxTlvDictionary *message) +{ + _assertInvariants(message); + return ccnxTlvDictionary_GetBuffer(message, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_KEYID); +} + +CCNxLink * +ccnxValidationFacadeV1_GetKeyName(const CCNxTlvDictionary *message) +{ + _assertInvariants(message); + CCNxLink *link = NULL; + CCNxName *name = ccnxTlvDictionary_GetName(message, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_KEYNAME_NAME); + if (name) { + PARCBuffer *keyid = ccnxTlvDictionary_GetBuffer(message, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_KEYNAME_KEYID); + PARCBuffer *hash = ccnxTlvDictionary_GetBuffer(message, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_KEYNAME_OBJHASH); + + link = ccnxLink_Create(name, keyid, hash); + } + return link; +} + +PARCBuffer * +ccnxValidationFacadeV1_GetPublicKey(const CCNxTlvDictionary *message) +{ + _assertInvariants(message); + return ccnxTlvDictionary_GetBuffer(message, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_KEY); +} + +PARCBuffer * +ccnxValidationFacadeV1_GetCertificate(const CCNxTlvDictionary *message) +{ + _assertInvariants(message); + return ccnxTlvDictionary_GetBuffer(message, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_CERT); +} + +PARCBuffer * +ccnxValidationFacadeV1_GetPayload(const CCNxTlvDictionary *message) +{ + _assertInvariants(message); + return ccnxTlvDictionary_GetBuffer(message, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_PAYLOAD); +} + +bool +ccnxValidationFacadeV1_HasCryptoSuite(const CCNxTlvDictionary *message) +{ + bool hasCryptoSuite = false; + _assertInvariants(message); + if (ccnxTlvDictionary_IsValueInteger(message, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_CRYPTO_SUITE)) { + hasCryptoSuite = true; + } + return hasCryptoSuite; +} + +PARCCryptoSuite +ccnxValidationFacadeV1_GetCryptoSuite(const CCNxTlvDictionary *message) +{ + if (ccnxValidationFacadeV1_HasCryptoSuite(message)) { + return (PARCCryptoSuite) ccnxTlvDictionary_GetInteger(message, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_CRYPTO_SUITE); + } + + trapUnexpectedState("Dictionary does not have a CryptoSuite set"); +} + +bool +ccnxValidationFacadeV1_HasSigningTime(const CCNxTlvDictionary *message) +{ + bool hasSigningTime = false; + _assertInvariants(message); + if (ccnxTlvDictionary_IsValueInteger(message, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_SIGNTIME)) { + hasSigningTime = true; + } + return hasSigningTime; +} + +uint64_t +ccnxValidationFacadeV1_GetSigningTime(const CCNxTlvDictionary *message) +{ + if (ccnxValidationFacadeV1_HasSigningTime(message)) { + return ccnxTlvDictionary_GetInteger(message, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_SIGNTIME); + } + + trapUnexpectedState("Dictionary does not have a CryptoSuite set"); +} + +// =========================================================== +// Setters + +bool +ccnxValidationFacadeV1_SetKeyId(CCNxTlvDictionary *message, const PARCBuffer *keyid) +{ + _assertInvariants(message); + return ccnxTlvDictionary_PutBuffer(message, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_KEYID, keyid); +} + +bool +ccnxValidationFacadeV1_SetKeyName(CCNxTlvDictionary *message, const CCNxLink *keyNameLink) +{ + _assertInvariants(message); + const CCNxName *name = ccnxLink_GetName(keyNameLink); + bool success = ccnxTlvDictionary_PutName(message, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_KEYNAME_NAME, name); + if (success) { + PARCBuffer *keyid = ccnxLink_GetKeyID(keyNameLink); + if (keyid) { + success = ccnxTlvDictionary_PutBuffer(message, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_KEYNAME_KEYID, keyid); + } + + if (success) { + PARCBuffer *hash = ccnxLink_GetContentObjectHash(keyNameLink); + success = ccnxTlvDictionary_PutBuffer(message, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_KEYNAME_OBJHASH, hash); + } + } + return success; +} + +bool +ccnxValidationFacadeV1_SetKeyLocator(CCNxTlvDictionary *message, CCNxKeyLocator *keyLocator) +{ + bool success = true; + if (keyLocator) { + if (ccnxKeyLocator_IsKey(keyLocator)) { + PARCKey *key = ccnxKeyLocator_GetKey(keyLocator); + PARCBuffer *keybuffer = parcKey_GetKey(key); + success &= ccnxTlvDictionary_PutBuffer(message, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_KEY, keybuffer); + } else if (ccnxKeyLocator_IsKeyLink(keyLocator)) { + CCNxLink *link = ccnxKeyLocator_GetKeyLink(keyLocator); + const CCNxName *name = ccnxLink_GetName(link); + + // Support KeyId and COH as part of the keyname, case 1012 + success &= ccnxTlvDictionary_PutName(message, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_KEYNAME_NAME, name); + } else { + trapUnrecoverableState("KeyLocator is not a known type"); + } + } + return success; +} + +bool +ccnxValidationFacadeV1_SetPublicKey(CCNxTlvDictionary *message, const PARCBuffer *derEncodedKey) +{ + _assertInvariants(message); + return ccnxTlvDictionary_PutBuffer(message, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_KEY, derEncodedKey); +} + +bool +ccnxValidationFacadeV1_SetCertificate(CCNxTlvDictionary *message, const PARCBuffer *derEncodedCertificate) +{ + _assertInvariants(message); + return ccnxTlvDictionary_PutBuffer(message, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_CERT, derEncodedCertificate); +} + +bool +ccnxValidationFacadeV1_SetCryptoSuite(CCNxTlvDictionary *message, PARCCryptoSuite suite) +{ + _assertInvariants(message); + return ccnxTlvDictionary_PutInteger(message, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_CRYPTO_SUITE, suite); +} + +bool +ccnxValidationFacadeV1_SetSigningTime(CCNxTlvDictionary *message, uint64_t signingTime) +{ + _assertInvariants(message); + return ccnxTlvDictionary_PutInteger(message, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_SIGNTIME, signingTime); +} + +bool +ccnxValidationFacadeV1_SetPayload(CCNxTlvDictionary *message, const PARCBuffer *validationPayload) +{ + _assertInvariants(message); + return ccnxTlvDictionary_PutBuffer(message, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_PAYLOAD, validationPayload); +} + diff --git a/libccnx-common/ccnx/common/internal/ccnx_ValidationFacadeV1.h b/libccnx-common/ccnx/common/internal/ccnx_ValidationFacadeV1.h new file mode 100644 index 00000000..524efe7e --- /dev/null +++ b/libccnx-common/ccnx/common/internal/ccnx_ValidationFacadeV1.h @@ -0,0 +1,474 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file ccnx_ValidationFacadeV1.h + * @brief Generic functions to fetch/set the KeyId, PublicKey, Certificate, or validation payload + * + * The Validation Facade may be used directly on CCNxInterest or CCNxContentObject structures, for example: + * + * @code + * { + * CCNxName *name = ccnxName_CreateFromCString("lci:/foo"); + * CCNxContentObject *object = ccnxContentObject_CreateWithNameAndPayload(name, NULL); + * ccnxName_Release(&name); + * // generate the KeyId + * ccnxValidationFacadeV1_SetKeyId(object, keyId); + * } + * @endcode + * + */ + +#ifndef libccnx_ccnx_ValidationFacadeV1_h +#define libccnx_ccnx_ValidationFacadeV1_h + +#include <parc/algol/parc_Buffer.h> + +#include <ccnx/common/ccnx_KeyLocator.h> + +#include <ccnx/common/ccnx_Link.h> +#include <ccnx/common/internal/ccnx_TlvDictionary.h> +#include <parc/security/parc_CryptoSuite.h> + + +// =========================================================== +// Validation Algorithm + +#include <ccnx/common/validation/ccnxValidation_CRC32C.h> +#include <ccnx/common/validation/ccnxValidation_EcSecp256K1.h> +#include <ccnx/common/validation/ccnxValidation_HmacSha256.h> +#include <ccnx/common/validation/ccnxValidation_RsaSha256.h> + +// =========================================================== +// Getters + +/** + * If the Validation Algorithm has a KeyId field, return it if it exists + * + * Not all validation algorithms have a KeyId field. Only true signature algoritms, such as + * RSA or ECC should always have one. HMAC or other MACs often use the KeyId to identify a + * key agreed to via a key exchange protocol, so the meaning is only applicable to those parties. + * Integrity checks, such as CRC-32C, do not have a KeyId. + * + * @param [in] message An allocated dictionary + * + * @retval non-null The KeyId field of the Validation Algorithm + * @retval null KeyId is missing + * + * Example: + * @code + * { + * PARCBuffer *wireFormat = // packet received from the network + * CCNxTlvDictionary *dictionary = ccnxCodecTlvPacket_Decode(wireFormat); + * PARCBuffer *keyid = ccnxValidationFacadeV1_GetKeyId(dictionary); + * } + * @endcode + */ +PARCBuffer *ccnxValidationFacadeV1_GetKeyId(const CCNxTlvDictionary *message); + +/** + * If the Validation Algorithm has a KeyName, return the embedded Link + * + * The returned CCNxLink is allocated on function call, so caller must + * release it when done. + * + * @param [in] message An allocated dictionary + * + * @retval non-null The KeyName link of the Validation Algorithm (must be released) + * @retval null KeyName is missing + * + * Example: + * @code + * { + * PARCBuffer *wireFormat = // packet received from the network + * CCNxTlvDictionary *dictionary = ccnxCodecTlvPacket_Decode(wireFormat); + * CCNxLink *link = ccnxValidationFacadeV1_GetKeyName(dictionary); + * } + * @endcode + */ +CCNxLink *ccnxValidationFacadeV1_GetKeyName(const CCNxTlvDictionary *mesage); + +/** + * If the Validation Algorithm has a Public Key embedded, return it + * + * @param [in] message An allocated dictionary + * + * @retval non-null The KeyId field of the Validation Algorithm + * @retval null KeyId is missing + * + * Example: + * @code + * { + * PARCBuffer *wireFormat = // packet received from the network + * CCNxTlvDictionary *dictionary = ccnxCodecTlvPacket_Decode(wireFormat); + * PARCBuffer *publicKeyDEREncoding = ccnxValidationFacadeV1_GetPublicKey(dictionary); + * } + * @endcode + */ +PARCBuffer *ccnxValidationFacadeV1_GetPublicKey(const CCNxTlvDictionary *message); + +/** + * If the Validation Algorithm has a Certificate embedded, return it + * + * @param [in] message An allocated dictionary + * + * @retval non-null The Certificate field of the Validation Algorithm + * @retval null KeyId is missing + * + * Example: + * @code + * { + * PARCBuffer *wireFormat = // packet received from the network + * CCNxTlvDictionary *dictionary = ccnxCodecTlvPacket_Decode(wireFormat); + * PARCBuffer *x509certDEREncoding = ccnxValidationFacadeV1_GetCertificate(dictionary); + * } + * @endcode + */ +PARCBuffer *ccnxValidationFacadeV1_GetCertificate(const CCNxTlvDictionary *message); + +/** + * Returns the Validation Payload, if present. + * + * The validation payload is the actual bytes of the signature or authentication code or + * integrity check. it's format will be specific to the ValidationAlgorithm. + * + * @param [in] message An allocated dictionary + * + * @retval non-null The validation payload + * @retval null Validation payload does not exist + * + * Example: + * @code + * { + * PARCBuffer *wireFormat = // packet received from the network + * CCNxTlvDictionary *dictionary = ccnxCodecTlvPacket_Decode(wireFormat); + * PARCBuffer *validationPayload = ccnxValidationFacadeV1_GetPayload(dictionary); + * } + * @endcode + */ +PARCBuffer *ccnxValidationFacadeV1_GetPayload(const CCNxTlvDictionary *message); + +/** + * Determines if the packet specified a supported crypto suite + * + * @param [in] message An allocated dictionary + * + * @retval true There is a valid crypto suite specified + * @retval false There is not a valid crypto suite specified + * + * Example: + * @code + * { + * PARCBuffer *wireFormat = // packet received from the network + * CCNxTlvDictionary *dictionary = ccnxCodecTlvPacket_Decode(wireFormat); + * if (ccnxValidationFacadeV1_HasCryptoSuite(dictionary)) { + * PARCCryptoSuite suite = ccnxValidationFacadeV1_GetCryptoSuite(dictionary); + * // process the validation alg + * } + * } + * @endcode + */ +bool ccnxValidationFacadeV1_HasCryptoSuite(const CCNxTlvDictionary *message); + +/** + * Returns the Validation Algorithm specified in the packet + * + * If the packet specified a supported crypto suite, return the suite. If + * it is not a supported algorithm, function all assert an error. You must use + * ccnxValidationFacadeV1_HasCryptoSuite() to determine if there is a supported crypto suite. + * + * An unsupported crypto suite's entire TLV container is put in the CustomField section of + * the dictionary and you can retrieve it from there if you know the type or iterate the list. + * + * @param [in] message An allocated dictionary + * + * @return value The crypto suite + * + * Example: + * @code + * { + * PARCBuffer *wireFormat = // packet received from the network + * CCNxTlvDictionary *dictionary = ccnxCodecTlvPacket_Decode(wireFormat); + * if (ccnxValidationFacadeV1_HasCryptoSuite(dictionary)) { + * PARCCryptoSuite suite = ccnxValidationFacadeV1_GetCryptoSuite(dictionary); + * // process the validation alg + * } + * } + * @endcode + */ +PARCCryptoSuite ccnxValidationFacadeV1_GetCryptoSuite(const CCNxTlvDictionary *message); + +/** + * Determines if the packet specified a signing time + * + * @param [in] message An allocated dictionary + * + * @retval true There is a signing time in the validation algorithm + * @retval false There is not a signing time + * + * Example: + * @code + * { + * PARCBuffer *wireFormat = // packet received from the network + * CCNxTlvDictionary *dictionary = ccnxCodecTlvPacket_Decode(wireFormat); + * if (ccnxValidationFacadeV1_HasSigningTime(dictionary)) { + * uint64_t signingTime = ccnxValidationFacadeV1_GetSigningTime(dictionary); + * // process the signing time + * } + * } + * @endcode + */ +bool ccnxValidationFacadeV1_HasSigningTime(const CCNxTlvDictionary *message); + +/** + * Retruns the Validation Algorithm specified in the packet + * + * If the packet has a Signing Time in the Validation Algorithm, return that value. If + * it is not, function all assert an error. You must use + * ccnxValidationFacadeV1_HasSigningTime() to determine if there is a + * signing time. + * + * The signing time is UTC milli-seconds since the epoch. + * + * @param [in] message An allocated dictionary + * + * @return value The signing time in UTC milli-seconds since the epoch. + * + * Example: + * @code + * { + * PARCBuffer *wireFormat = // packet received from the network + * CCNxTlvDictionary *dictionary = ccnxCodecTlvPacket_Decode(wireFormat); + * if (ccnxValidationFacadeV1_HasSigningTime(dictionary)) { + * uint64_t signingTime = ccnxValidationFacadeV1_GetSigningTime(dictionary); + * // process the signing time + * } + * } + * @endcode + */ +uint64_t ccnxValidationFacadeV1_GetSigningTime(const CCNxTlvDictionary *message); + +// =========================================================== +// Setters + +/** + * Sets the KeyId attribute of the dictionary + * + * The KeyId is a mandatory field for validation algorithms that use a key, such + * as HMAC or RSA or ECC. + * + * Normally, one uses a function in the ccnxValidation algoirthm to set this value + * such as bool ccnxValidationHmacSha256_Set(CCNxTlvDictionary *message, const PARCBuffer *keyid). + * + * @param [in] message The dictionary to set the value in + * @param [in] keyid The encoded value of the keyid + * + * @retval true Value set in the dictionary + * @retval false Error, likely the value was already set + * + * Example: + * @code + * { + * CCNxName *name = ccnxName_CreateFromCString("lci:/foo"); + * CCNxContentObject *object = ccnxContentObject_CreateWithNameAndPayload(name, NULL); + * ccnxName_Release(&name); + * + * PARCBuffer *secretKey = parcBuffer_Wrap("password", 8, 0, 8); + * PARCSigner *signer = ccnxValidationHmacSha256_CreateSigner(secretKey); + * + * PARCKeyId *keyid = parcSigner_CreateKeyId(signer); + * const PARCBuffer *keyIdBytes = parcKeyId_GetKeyId(keyid); + * ccnxValidationFacadeV1_SetKeyId(object, keyIdBytes); + * parcKeyId_Release(&keyid); + * + * // continue with signer and dictionary + * } + * @endcode + */ +bool ccnxValidationFacadeV1_SetKeyId(CCNxTlvDictionary *message, const PARCBuffer *keyid); + + +/** + * Stores the KeyLocator in the standard Dictionary places for use by the standard Getters + * + * If the Validator does not need any special handing of a KeyLocator, this function + * will store the keylocator in the default dictionary entries for use by the standard getters. + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool ccnxValidationFacadeV1_SetKeyLocator(CCNxTlvDictionary *message, CCNxKeyLocator *keyLocator); + +/** + * Stores the KeyName in the standard Dictionary places for use by the standard Getters + * + * Stores the fields from the Link in the dictionary. + * + * @param [in] message The message to update + * @param [in] keyNameLink The link to put in the dictionary + * + * @retval true Values successfully added to dictionary + * @retval false An error (likely a duplicate value) + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool ccnxValidationFacadeV1_SetKeyName(CCNxTlvDictionary *message, const CCNxLink *keyNameLink); + +/** + * Embeds the DER encoded public key in the Validation Algorithm + * + * If this field is not set, the public key will not be placed in the + * validation algorithm. + * + * @param [in] message The message to update + * @param [in] derEncodedKey The DER encoded public key to put in the dictionary + * + * @retval true Value successfully added to dictionary + * @retval false An error (likely a duplicate value) + * + * Example: + * @code + * { + * CCNxName *name = ccnxName_CreateFromCString("lci:/foo"); + * CCNxContentObject *object = ccnxContentObject_CreateWithNameAndPayload(name, NULL); + * ccnxName_Release(&name); + * + * PARCSigner *signer = parcSigner_Create(parcPublicKeySignerPkcs12Store_Open("keystore.p12", "password", PARCCryptoHashType_SHA256)); + * + * PARCBuffer *publicKey = parcSigner_GetDEREncodedPublicKey(signer); + * + * ccnxValidationFacadeV1_SetPublicKey(object, publicKey); + * + * // continue with signer and dictionary + * } * @endcode + */ +bool ccnxValidationFacadeV1_SetPublicKey(CCNxTlvDictionary *message, const PARCBuffer *derEncodedKey); + +/** + * Embeds the DER encoded public key in the Validation Algorithm + * + * If this field is not set, the public key will not be placed in the + * validation algorithm. + * + * @param [in] message The message to update + * @param [in] derEncodedCertificate The DER encoded public key to put in the dictionary + * + * @retval true Value successfully added to dictionary + * @retval false An error (likely a duplicate value) + * + * Example: + * @code + * { + * CCNxName *name = ccnxName_CreateFromCString("lci:/foo"); + * CCNxContentObject *object = ccnxContentObject_CreateWithNameAndPayload(name, NULL); + * ccnxName_Release(&name); + * + * PARCSigner *signer = parcSigner_Create(parcPublicKeySignerPkcs12Store_Open("keystore.p12", "password", PARCCryptoHashType_SHA256)); + * + * PARCBuffer *cert = parcSigner_GetDEREncodedCertificate(signer); + * + * ccnxValidationFacadeV1_SetPublicKey(object, cert); + * + * // continue with signer and dictionary + * } * @endcode + */ +bool ccnxValidationFacadeV1_SetCertificate(CCNxTlvDictionary *message, const PARCBuffer *derEncodedCertificate); + +/** + * Sets the crypto suite in the dictionary + * + * It is not necessary to set the crypto suite if the packet is encoded with a Signer, + * as the crypto suite will be derived from the signer. + * + * Normally, one uses a function in the ccnxValidation algoirthm to set this value + * such as bool ccnxValidationHmacSha256_Set(CCNxTlvDictionary *message, const PARCBuffer *keyid). + * + * @param [in] message The message to update + * @param [in] suite The cryptosuite value to set in the packet + * + * @retval true The crypto suite was set + * @retval false The crypto suite was not set (not supported by V1 or other error) + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool ccnxValidationFacadeV1_SetCryptoSuite(CCNxTlvDictionary *message, PARCCryptoSuite suite); + +/** + * Sets the signing time in the dictionary + * + * The signing time represents when the signature is created. It is sometimes used for + * replay attack prevention. It is the UTC time in milli-seconds since the epoch (i.e. a posix time). + * + * If the signing time is not specified in the dictionary, it will be automatically created + * based on the system clock when the signature is generated (this assumes the system clock + * and timezone are correctly set). + * + * @param [in] message The message to update + * @param [in] signingTime UTC time since the epoch in milli-seconds + * + * @retval true The value was set + * @retval false The value was not set (likely a duplicate value) + * + * Example: + * @code + * @endcode + */ +bool ccnxValidationFacadeV1_SetSigningTime(CCNxTlvDictionary *message, uint64_t signingTime); + +/** + * Saves the validation payload in the dictionary + * + * The validation payload is the output of the validation algorithm, i.e. + * the 32-bit CRC32C checksum or the RSA signature. Usually the Codec is setting this + * value after it has generated the wire format. If this value is set prior to encoding, + * this value will be used regardless if it is correct or not. + * + * You can only save the payload once per dictionary. + * + * @param [in] message The message to update + * @param [in] validationPayload The payload to put in the packet + * + * @return true Payload saved + * @return false Payload was already set or other error + * + * Example: + * @code + * { + * PARCSignature *signature = ccnxCodecTlvEncoder_ComputeSignature(encoder); + * PARCBuffer *sigbits = parcSignature_GetSignature(signature); + * + * // this creates its own reference to sigbits + * ccnxValidationFacadeV1_SetPayload(packetDictionary, sigbits); + * + * parcSignature_Release(&signature); + * parcBuffer_Release(&sigbits); + * } + * @endcode + */ +bool ccnxValidationFacadeV1_SetPayload(CCNxTlvDictionary *message, const PARCBuffer *validationPayload); + +#endif // libccnx_ccnx_ValidationFacadeV1_h diff --git a/libccnx-common/ccnx/common/internal/ccnx_WireFormatFacadeV1.c b/libccnx-common/ccnx/common/internal/ccnx_WireFormatFacadeV1.c new file mode 100644 index 00000000..daf851c7 --- /dev/null +++ b/libccnx-common/ccnx/common/internal/ccnx_WireFormatFacadeV1.c @@ -0,0 +1,484 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +#include <config.h> +#include <stdio.h> +#include <fcntl.h> +#include <sys/stat.h> + +#include <LongBow/runtime.h> + +#include <parc/algol/parc_FileOutputStream.h> + +#include <ccnx/common/internal/ccnx_WireFormatMessageInterface.h> +#include <ccnx/common/internal/ccnx_WireFormatFacadeV1.h> +#include <ccnx/common/ccnx_WireFormatMessage.h> + +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_TlvDictionary.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_Types.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_FixedHeader.h> + + +static CCNxTlvDictionary * +_ccnxWireFormatFacadeV1_FromInterestPacketType(const PARCBuffer *wireFormat) +{ + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + ccnxTlvDictionary_PutBuffer(dictionary, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_WireFormat, wireFormat); + return dictionary; +} + +static CCNxTlvDictionary * +_ccnxWireFormatFacadeV1_FromInterestPacketTypeIoVec(const CCNxCodecNetworkBufferIoVec *vec) +{ + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + ccnxTlvDictionary_PutIoVec(dictionary, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_WireFormat, vec); + return dictionary; +} + +static CCNxTlvDictionary * +_ccnxWireFormatFacadeV1_FromInterestReturnPacketType(const PARCBuffer *wireFormat) +{ + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateInterestReturn(); + ccnxTlvDictionary_PutBuffer(dictionary, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_WireFormat, wireFormat); + return dictionary; +} + +static CCNxTlvDictionary * +_ccnxWireFormatFacadeV1_FromContentObjectPacketType(const PARCBuffer *wireFormat) +{ + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateContentObject(); + ccnxTlvDictionary_PutBuffer(dictionary, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_WireFormat, wireFormat); + return dictionary; +} + +static CCNxTlvDictionary * +_ccnxWireFormatFacadeV1_FromControlPacketType(const PARCBuffer *wireFormat) +{ + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateControl(); + ccnxTlvDictionary_PutBuffer(dictionary, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_WireFormat, wireFormat); + return dictionary; +} + +static CCNxCodecSchemaV1InterestHeader * +_getWireFormatFixedHeader(CCNxTlvDictionary *dictionary) +{ + // Currently there is only one of either a PARCBuffer or an IoVec ... + + CCNxCodecSchemaV1InterestHeader *header = NULL; + + // Update attached iovec + CCNxCodecNetworkBufferIoVec *iovec = ccnxWireFormatMessage_GetIoVec(dictionary); + if (iovec) { + assertTrue(ccnxCodecNetworkBufferIoVec_Length(iovec) >= sizeof(CCNxCodecSchemaV1InterestHeader), + "IoVector smaller than required for message header"); + const struct iovec *iov = ccnxCodecNetworkBufferIoVec_GetArray(iovec); + assertTrue(iov[0].iov_len >= sizeof(CCNxCodecSchemaV1InterestHeader), + "Header not contained in first element of io vector"); + header = iov[0].iov_base; + } else { + // Update attached buffer + PARCBuffer *wireFormatBuffer = ccnxWireFormatMessage_GetWireFormatBuffer(dictionary); + if (wireFormatBuffer) { + header = parcBuffer_Overlay(wireFormatBuffer, 0); + } + } + + return header; +} + +static bool +_ccnxWireFormatFacadeV1_SetHopLimit(CCNxTlvDictionary *dictionary, uint32_t hopLimit) +{ + bool result = false; + + CCNxCodecSchemaV1InterestHeader *header = _getWireFormatFixedHeader(dictionary); + if (header != NULL) { + header->hopLimit = hopLimit; + result = true; + } + return result; +} + +static bool +_ccnxWireFormatFacadeV1_ConvertInterestToInterestReturn(CCNxTlvDictionary *dictionary, uint8_t code) +{ + bool result = false; + + CCNxCodecSchemaV1InterestHeader *header = _getWireFormatFixedHeader(dictionary); + if (header != NULL) { + header->returnCode = code; + header->packetType = CCNxCodecSchemaV1Types_PacketType_InterestReturn; + result = true; + } + return result; +} + +static CCNxTlvDictionary * +_ccnxWireFormatFacadeV1_CreateFromV1(const PARCBuffer *wireFormat) +{ + CCNxTlvDictionary *dictionary = NULL; + uint8_t packetType = parcBuffer_GetAtIndex(wireFormat, 1); + switch (packetType) { + case CCNxCodecSchemaV1Types_PacketType_ContentObject: + dictionary = _ccnxWireFormatFacadeV1_FromContentObjectPacketType(wireFormat); + break; + case CCNxCodecSchemaV1Types_PacketType_Control: + dictionary = _ccnxWireFormatFacadeV1_FromControlPacketType(wireFormat); + break; + case CCNxCodecSchemaV1Types_PacketType_Interest: + dictionary = _ccnxWireFormatFacadeV1_FromInterestPacketType(wireFormat); + break; + case CCNxCodecSchemaV1Types_PacketType_InterestReturn: + dictionary = _ccnxWireFormatFacadeV1_FromInterestReturnPacketType(wireFormat); + break; + default: + // will return NULL + break; + } + return dictionary; +} + +static PARCBuffer * +_ccnxWireFormatFacadeV1_GetWireFormatBuffer(const CCNxTlvDictionary *dictionary) +{ + PARCBuffer *wireFormat = ccnxTlvDictionary_GetBuffer(dictionary, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_WireFormat); + return wireFormat; +} + +static bool +_ccnxWireFormatFacadeV1_PutWireFormatBuffer(CCNxTlvDictionary *dictionary, PARCBuffer *wireFormat) +{ + bool success = ccnxTlvDictionary_PutBuffer(dictionary, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_WireFormat, wireFormat); + return success; +} + +static CCNxCodecNetworkBufferIoVec * +_ccnxWireFormatFacadeV1_GetIoVec(const CCNxTlvDictionary *dictionary) +{ + CCNxCodecNetworkBufferIoVec *vec = ccnxTlvDictionary_GetIoVec(dictionary, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_WireFormat); + return vec; +} + +static bool +_ccnxWireFormatFacadeV1_PutIoVec(CCNxTlvDictionary *dictionary, CCNxCodecNetworkBufferIoVec *vec) +{ + bool success = ccnxTlvDictionary_PutIoVec(dictionary, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_WireFormat, vec); + return success; +} + +static void +_ccnxWireFormatFacadeV1_WriteToFile(const CCNxTlvDictionary *dictionary, const char *filename) +{ + int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRWXU); + PARCFileOutputStream *fos = parcFileOutputStream_Create(fd); + + PARCBuffer *wireFormat = _ccnxWireFormatFacadeV1_GetWireFormatBuffer(dictionary); + if (wireFormat) { + parcFileOutputStream_Write(fos, wireFormat); + parcBuffer_Rewind(wireFormat); + } + + // this will close the file descriptor + parcFileOutputStream_Release(&fos); +} + +static bool +_ccnxWireFormatFacadeV1_SetProtectedRegionStart(CCNxTlvDictionary *dictionary, size_t startPosition) +{ + assertNotNull(dictionary, "Parameter dictionary must be non-null"); + // ccnx packets work on 16-bit lengths + assertTrue(startPosition <= UINT16_MAX, "Start position beyond UINT16_MAX: %zu", startPosition); + + bool success = ccnxTlvDictionary_PutInteger(dictionary, + CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_ProtectedStart, + startPosition); + return success; +} + +static bool +_ccnxWireFormatFacadeV1_SetProtectedRegionLength(CCNxTlvDictionary *dictionary, size_t length) +{ + assertNotNull(dictionary, "Parameter dictionary must be non-null"); + // ccnx packets work on 16-bit lengths + assertTrue(length <= UINT16_MAX, "Length position beyond UINT16_MAX: %zu", length); + + bool success = ccnxTlvDictionary_PutInteger(dictionary, + CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_ProtectedLength, + length); + return success; +} + +static bool +_ccnxWireFormatFacadeV1_SetContentObjectHashRegionStart(CCNxTlvDictionary *dictionary, size_t startPosition) +{ + assertNotNull(dictionary, "Parameter dictionary must be non-null"); + // ccnx packets work on 16-bit lengths + assertTrue(startPosition <= UINT16_MAX, "Start position beyond UINT16_MAX: %zu", startPosition); + + bool success = ccnxTlvDictionary_PutInteger(dictionary, + CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_ContentObjectHashRegionStart, + startPosition); + return success; +} + +static bool +_ccnxWireFormatFacadeV1_SetContentObjectHashRegionLength(CCNxTlvDictionary *dictionary, size_t length) +{ + assertNotNull(dictionary, "Parameter dictionary must be non-null"); + // ccnx packets work on 16-bit lengths + assertTrue(length <= UINT16_MAX, "Length position beyond UINT16_MAX: %zu", length); + + bool success = ccnxTlvDictionary_PutInteger(dictionary, + CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_ContentObjectHashRegionLength, + length); + return success; +} + + +/* + * Compute the hash over the specified region. This is a static function, so we know the caller + * has verified all the parameters before giving them to us. + * The caller must also verify that (start + length <= totalLength). + * + * As we walk through the iovecs, the variables will look like this + * +-----------+-----------+-----------+-----------+-----------+ + * iov[0] iov[1] iov[2] iov[3] + * +-----------+-----------+-----------+-----------+-----------+ + * ^ ^ ^ ^ + * | | | | + * | start | end + * iovStart iovEnd + * |-------|---| + * offset + * remaining + * + * +-----------+-----------+-----------+-----------+-----------+ + * iov[0] iov[1] iov[2] iov[3] + * +-----------+-----------+-----------+-----------+-----------+ + * ^ ^ ^ + * | | | + * start | end + * iovStart iovEnd + * |-----------| + * offset (0) + * remaining (iov_len) + * + * +-----------+-----------+-----------+-----------+-----------+ + * iov[0] iov[1] iov[2] iov[3] + * +-----------+-----------+-----------+-----------+-----------+ + * ^ ^ + * | | + * start end + * iovStart iovEnd + * |------| + * offset (0) + * remaining (end-start) + * + */ +static PARCCryptoHash * +_hashProtectedRegionIoVec(CCNxCodecNetworkBufferIoVec *vec, PARCCryptoHasher *hasher, size_t start, size_t length) +{ + int failure = parcCryptoHasher_Init(hasher); + assertFalse(failure, "Error initializing the hasher"); + + const struct iovec *iov = ccnxCodecNetworkBufferIoVec_GetArray(vec); + int iovcnt = ccnxCodecNetworkBufferIoVec_GetCount(vec); + + // iovStart is the beginning position of the current iovec + size_t iovStart = 0; + size_t end = start + length; + + // Once iovStart is past end, we no longer need to loop, we're done + for (int i = 0; i < iovcnt && iovStart < end; i++) { + size_t iovEnd = iovStart + iov[i].iov_len; + iovEnd = (iovEnd > end) ? end : iovEnd; + + // is "start" contained in this iovec? + if (iovStart <= start && start < iovEnd) { + size_t offset = start - iovStart; + size_t remaining = iovEnd - start; + + failure = parcCryptoHasher_UpdateBytes(hasher, (const void *) ((uint8_t *) iov[i].iov_base + offset), remaining); + assertFalse(failure, "Error updating hasher iovec %d offset %zu remaining %zu", i, offset, remaining) + { + ccnxCodecNetworkBufferIoVec_Display(vec, 3); + } + + // keep moving start to the next iovec + start += remaining; + } + + iovStart += iov[i].iov_len; + } + + PARCCryptoHash *hash = parcCryptoHasher_Finalize(hasher); + + return hash; +} + + +/* + * Compute the hash over the specified region. This is a static function, so we know the caller + * has verified all the parameters before giving them to us. + * The caller must also verify that (start + length <= parcBuffer_Limit). + * + * +-----------+-----------+-----------+-----------+-----------+ + * PARCBuffer + * +-----------+-----------+-----------+-----------+-----------+ + * ^ ^ ^ ^ + * | | | | + * 0 start end Limit + */ +static PARCCryptoHash * +_ccnxWireFormatFacadeV1_ComputeBufferHash(PARCBuffer *wireFormat, PARCCryptoHasher *hasher, size_t start, size_t length) +{ + int failure = parcCryptoHasher_Init(hasher); + assertTrue(failure == 0, "Error initializing the hasher"); + + parcBuffer_SetPosition(wireFormat, start); + uint8_t *overlay = parcBuffer_Overlay(wireFormat, 0); + + failure = parcCryptoHasher_UpdateBytes(hasher, overlay, length); + assertTrue(failure == 0, "Error updating hasher start %zu length %zu", start, length) + { + parcBuffer_Display(wireFormat, 3); + } + + parcBuffer_Rewind(wireFormat); + + PARCCryptoHash *hash = parcCryptoHasher_Finalize(hasher); + return hash; +} + +static PARCCryptoHash * +_ccnxWireFormatFacadeV1_HashProtectedRegion(const CCNxTlvDictionary *dictionary, PARCCryptoHasher *hasher) +{ + assertNotNull(dictionary, "Parameter dictionary must be non-null"); + assertNotNull(hasher, "Parameter hasher must be non-null"); + + PARCCryptoHash *hash = NULL; + + if (ccnxTlvDictionary_IsValueInteger(dictionary, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_ProtectedStart)) { + if (ccnxTlvDictionary_IsValueInteger(dictionary, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_ProtectedLength)) { + size_t startPosition = ccnxTlvDictionary_GetInteger(dictionary, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_ProtectedStart); + size_t length = ccnxTlvDictionary_GetInteger(dictionary, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_ProtectedLength); + + CCNxCodecNetworkBufferIoVec *vec = _ccnxWireFormatFacadeV1_GetIoVec(dictionary); + if (vec) { + size_t totalLength = ccnxCodecNetworkBufferIoVec_Length(vec); + if (startPosition + length <= totalLength) { + hash = _hashProtectedRegionIoVec(vec, hasher, startPosition, length); + } + } else { + PARCBuffer *wireFormat = _ccnxWireFormatFacadeV1_GetWireFormatBuffer(dictionary); + if (wireFormat) { + size_t totalLength = parcBuffer_Remaining(wireFormat); + if (startPosition + length <= totalLength) { + hash = _ccnxWireFormatFacadeV1_ComputeBufferHash(wireFormat, hasher, startPosition, length); + } + } + } + } + } + + return hash; +} + + +static PARCCryptoHash * +_ccnxWireFormatFacadeV1_ComputeContentObjectHash(CCNxTlvDictionary *dictionary) +{ + // This assumes the dictionary has been passed through something like the V1 packet decoder, + // (e.g. ccnxCodecSchemaV1PacketDecoder_Decode) and has had the protected region extents set. + // This will be the case for Athena. Metis has its own TLV parsing. + + assertTrue(ccnxTlvDictionary_IsContentObject(dictionary) || ccnxTlvDictionary_IsManifest(dictionary), "Message must be a ContentObject or Manifest"); + + PARCCryptoHash *result = NULL; + + if (ccnxTlvDictionary_IsValueInteger(dictionary, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_ContentObjectHashRegionStart) + && ccnxTlvDictionary_IsValueInteger(dictionary, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_ContentObjectHashRegionLength)) { + PARCCryptoHasher *hasher = parcCryptoHasher_Create(PARCCryptoHashType_SHA256); + size_t startPosition = ccnxTlvDictionary_GetInteger(dictionary, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_ContentObjectHashRegionStart); + size_t length = ccnxTlvDictionary_GetInteger(dictionary, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_ContentObjectHashRegionLength); + + CCNxCodecNetworkBufferIoVec *vec = _ccnxWireFormatFacadeV1_GetIoVec(dictionary); + if (vec) { + size_t totalLength = ccnxCodecNetworkBufferIoVec_Length(vec); + if (startPosition + length <= totalLength) { + result = _hashProtectedRegionIoVec(vec, hasher, startPosition, length); + } + } else { + PARCBuffer *wireFormat = _ccnxWireFormatFacadeV1_GetWireFormatBuffer(dictionary); + if (wireFormat) { + size_t totalLength = parcBuffer_Remaining(wireFormat); + if (startPosition + length <= totalLength) { + result = _ccnxWireFormatFacadeV1_ComputeBufferHash(wireFormat, hasher, startPosition, length); + } + } + } + parcCryptoHasher_Release(&hasher); + } + + return result; // Could be NULL +} + + +/** + * `CCNxWireFormatFacadeV1_Implementation` is the structure containing the pointers to the + * V1 schema WireFormatMessage implementation. + */ +CCNxWireFormatMessageInterface CCNxWireFormatFacadeV1_Implementation = { + .description = "CCNxWireFormatFacadeV1_Implementation", + + .create = &_ccnxWireFormatFacadeV1_CreateFromV1, + + .fromInterestPacketType = &_ccnxWireFormatFacadeV1_FromInterestPacketType, + + .fromInterestPacketTypeIoVec = &_ccnxWireFormatFacadeV1_FromInterestPacketTypeIoVec, + + .fromContentObjectPacketType = &_ccnxWireFormatFacadeV1_FromContentObjectPacketType, + + .fromControlPacketType = &_ccnxWireFormatFacadeV1_FromControlPacketType, + + .getWireFormatBuffer = &_ccnxWireFormatFacadeV1_GetWireFormatBuffer, + + .getIoVec = &_ccnxWireFormatFacadeV1_GetIoVec, + + .putWireFormatBuffer = &_ccnxWireFormatFacadeV1_PutWireFormatBuffer, + + .putIoVec = &_ccnxWireFormatFacadeV1_PutIoVec, + + .writeToFile = &_ccnxWireFormatFacadeV1_WriteToFile, + + .setProtectedRegionStart = &_ccnxWireFormatFacadeV1_SetProtectedRegionStart, + + .setProtectedRegionLength = &_ccnxWireFormatFacadeV1_SetProtectedRegionLength, + + .hashProtectedRegion = &_ccnxWireFormatFacadeV1_HashProtectedRegion, + + .setContentObjectHashRegionStart = &_ccnxWireFormatFacadeV1_SetContentObjectHashRegionStart, + + .setContentObjectHashRegionLength = &_ccnxWireFormatFacadeV1_SetContentObjectHashRegionLength, + + .computeContentObjectHash = &_ccnxWireFormatFacadeV1_ComputeContentObjectHash, + + .setHopLimit = &_ccnxWireFormatFacadeV1_SetHopLimit, + + .convertInterestToInterestReturn = &_ccnxWireFormatFacadeV1_ConvertInterestToInterestReturn, +}; diff --git a/libccnx-common/ccnx/common/internal/ccnx_WireFormatFacadeV1.h b/libccnx-common/ccnx/common/internal/ccnx_WireFormatFacadeV1.h new file mode 100755 index 00000000..cd888062 --- /dev/null +++ b/libccnx-common/ccnx/common/internal/ccnx_WireFormatFacadeV1.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +/** + * @file ccnx_WireFormatFacadeV1.h + * @ingroup Utility + * + * A WireFormat facade will set/get the wire format representation of a message from the + * dictionary. + * + * One may also create a message dictionary only with a wire format, not specifying the actual message type. + * This occurs mostly at the lowest layer that receives a network buffer and does not yet know what sort of message it holds. + * + * This facade is used by the Forwarder Connector to create the original dictionary + * at the bottom of the stack on receive. It is also used by the Codec component to set + * the wireformat to encode a packet. + * + * If an application has a pre-encoded packet, it can create an empty dictionary and set the wire format + * then send that down the stack. + * + */ +#ifndef libccnx_ccnx_WireFormatFacadeV1_h +#define libccnx_ccnx_WireFormatFacadeV1_h + + +#include <ccnx/common/internal/ccnx_WireFormatMessageInterface.h> + +extern CCNxWireFormatMessageInterface CCNxWireFormatFacadeV1_Implementation; +#endif // libccnx_ccnx_WireFormatFacadeV1_h diff --git a/libccnx-common/ccnx/common/internal/ccnx_WireFormatMessageInterface.c b/libccnx-common/ccnx/common/internal/ccnx_WireFormatMessageInterface.c new file mode 100755 index 00000000..bb698eb4 --- /dev/null +++ b/libccnx-common/ccnx/common/internal/ccnx_WireFormatMessageInterface.c @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +#include <config.h> + +#include <LongBow/runtime.h> + +#include <ccnx/common/internal/ccnx_WireFormatMessageInterface.h> + + +CCNxWireFormatMessageInterface * +ccnxWireFormatMessageInterface_GetInterface(const CCNxTlvDictionary *dictionary) +{ + CCNxWireFormatMessageInterface *impl = NULL; + + int schemaVersion = ccnxTlvDictionary_GetSchemaVersion(dictionary); + + switch (schemaVersion) { + case CCNxTlvDictionary_SchemaVersion_V1: + impl = &CCNxWireFormatFacadeV1_Implementation; + break; + default: + trapUnexpectedState("Unknown SchemaVersion encountered in ccnxWireFormatMessageInterface_GetInterface()"); + break; + } + + // We do not set the implementation pointer in the dictionary here. That is only done when accessing the dictionary + // as a CCNxContentObject, CCNxInterest, CCNxInterestReturn, or CCNxControlMessage. + + return impl; +} diff --git a/libccnx-common/ccnx/common/internal/ccnx_WireFormatMessageInterface.h b/libccnx-common/ccnx/common/internal/ccnx_WireFormatMessageInterface.h new file mode 100755 index 00000000..2efc2194 --- /dev/null +++ b/libccnx-common/ccnx/common/internal/ccnx_WireFormatMessageInterface.h @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @brief The definition of the interface used to call into a CCNxWireFormatFacade implementation. + * + */ + +#ifndef CCNx_Common_ccnx_internal_WireFormatMessageInterface_h +#define CCNx_Common_ccnx_internal_WireFormatMessageInterface_h + +#include <ccnx/common/internal/ccnx_TlvDictionary.h> + +typedef struct ccnx_wireformatmessage_interface { + char *description; // A human-readable label for this implementation + + /** @see ccnxWireFormatMessage_Create */ + CCNxTlvDictionary *(*create)(const PARCBuffer * wireFormat); + + /** @see ccnxWireFormatMessage_FromInterestPacketType */ + CCNxTlvDictionary *(*fromInterestPacketType)(const PARCBuffer * wireFormat); + + /** @see ccnxWireFormatMessage_FromInterestPacketTypeIoVec */ + CCNxTlvDictionary *(*fromInterestPacketTypeIoVec)(const CCNxCodecNetworkBufferIoVec * vec); + + /** @see ccnxWireFormatMessage_FromContentObjectPacketType */ + CCNxTlvDictionary *(*fromContentObjectPacketType)(const PARCBuffer * wireFormat); + + /** @see ccnxWireFormatMessage_FromControlPacketType */ + CCNxTlvDictionary *(*fromControlPacketType)(const PARCBuffer * wireFormat); + + /** @see ccnxWireFormatMessage_GetWireFormatBuffer */ + PARCBuffer *(*getWireFormatBuffer)(const CCNxTlvDictionary * dictionary); + + /** @see ccnxWireFormatMessage_GetIoVec */ + CCNxCodecNetworkBufferIoVec *(*getIoVec)(const CCNxTlvDictionary * dictionary); + + /** @see ccnxWireFormatMessage_PutWireFormatBuffer */ + bool (*putWireFormatBuffer)(CCNxTlvDictionary *dictionary, PARCBuffer *buffer); + + /** @see ccnxWireFormatMessage_PutIoVec */ + bool (*putIoVec)(CCNxTlvDictionary *dictionary, CCNxCodecNetworkBufferIoVec *vec); + + /** @see ccnxWireFormatMessage_WriteToFile */ + void (*writeToFile)(const CCNxTlvDictionary *dictionary, const char *filename); + + /** @see ccnxWireFormatMessage_SetProtectedRegionStart */ + bool (*setProtectedRegionStart)(CCNxTlvDictionary *dictionary, size_t startPosition); + + /** @see ccnxWireFormatMessage_SetProtectedRegionLength */ + bool (*setProtectedRegionLength)(CCNxTlvDictionary *dictionary, size_t length); + + /** @see ccnxWireFormatMessage_SetContentObjectHashRegionStart */ + bool (*setContentObjectHashRegionStart)(CCNxTlvDictionary *dictionary, size_t startPosition); + + /** @see ccnxWireFormatMessage_SetContentObjectHashRegionLength */ + bool (*setContentObjectHashRegionLength)(CCNxTlvDictionary *dictionary, size_t length); + + /** @see ccnxWireFormatMessage_HashProtectedRegion */ + PARCCryptoHash *(*hashProtectedRegion)(const CCNxTlvDictionary * dictionary, PARCCryptoHasher * hasher); + + /** @see ccnxWireFormatMessage_SetHopLimit */ + bool (*setHopLimit)(CCNxTlvDictionary *dictionary, uint32_t hopLimit); + + /** @see ccnxWireFormatMessage_AssertValid*/ + void (*assertValid)(const CCNxTlvDictionary *dictionary); + + /** @see ccnxWireFormatMessage_CreateContentObjectHash */ + PARCCryptoHash *(*computeContentObjectHash)(CCNxTlvDictionary * dictionary); + + /** @see ccnxWireFormatMessage_ConvertInterestToInterestReturn */ + bool (*convertInterestToInterestReturn)(CCNxTlvDictionary *dictionary, uint8_t returnCode); +} CCNxWireFormatMessageInterface; + +/** + * The SchemaV0 WireFormatMessage implementaton + */ +extern CCNxWireFormatMessageInterface CCNxWireFormatFacadeV0_Implementation; + +/** + * The SchemaV1 WireFormatMessage implementaton + */ +extern CCNxWireFormatMessageInterface CCNxWireFormatFacadeV1_Implementation; + +/** + * Given a CCNxTlvDictionary representing a CCNxWireFormatMessage, return the address of the CCNxWireFormatMessageInterface + * instance that should be used to access the WireFormatMessage. + * + * @param interestDictionary - a {@link CCNxTlvDictionary} representing a CCNxInterestReturn. + * @return the address of the `CCNxWireFormatMessageInterface` instance that should be used to access the CCNxWireFormatMessage. + * + * Example: + * @code + * { + * + * CCNxWireFormatMessage *message = ccnxWireFormatMessage_FromInterestPacketType(schemaVersion, wireFormatBuffer); + * + * //V1 test + * if (ccnxWireFormatMessageInterface_GetInterface(interestReturn) == &CCNxWireFormatFacadeV1_Implementation) { + * printf("Using a V1 CCNxWireFormatFacadeV1_Implementation \n"); + * } + * + * ... + * + * ccnxWireFormatMessage_Release(&message); + * } * @endcode + */ +CCNxWireFormatMessageInterface *ccnxWireFormatMessageInterface_GetInterface(const CCNxTlvDictionary *contentDictionary); +#endif diff --git a/libccnx-common/ccnx/common/internal/test/.gitignore b/libccnx-common/ccnx/common/internal/test/.gitignore new file mode 100644 index 00000000..d8148318 --- /dev/null +++ b/libccnx-common/ccnx/common/internal/test/.gitignore @@ -0,0 +1,18 @@ +test_ccnx_ChunkingFacadeV0 +test_ccnx_ChunkingFacadeV1 +test_ccnx_ContentObjectFacadeV0 +test_ccnx_ContentObjectFacadeV1 +test_ccnx_ContentObjectInterface +test_ccnx_ControlFacade +test_ccnx_InterestFacadeV0 +test_ccnx_InterestFacadeV1 +test_ccnx_InterestInterface +test_ccnx_InterestReturnInterface +test_ccnx_Tlv +test_ccnx_TlvDictionary +test_ccnx_TlvEncodingBuffer +test_ccnx_TlvError +test_ccnx_ValidationFacadeV0 +test_ccnx_ValidationFacadeV1 +test_ccnx_WireFormatFacadeV0 +test_ccnx_WireFormatFacadeV1 diff --git a/libccnx-common/ccnx/common/internal/test/CMakeLists.txt b/libccnx-common/ccnx/common/internal/test/CMakeLists.txt new file mode 100644 index 00000000..900ba031 --- /dev/null +++ b/libccnx-common/ccnx/common/internal/test/CMakeLists.txt @@ -0,0 +1,24 @@ +# Enable gcov output for the tests +add_definitions(--coverage) +set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} " --coverage") + +set(TestsExpectedToPass + test_ccnx_ChunkingFacadeV1 + test_ccnx_ContentObjectFacadeV1 + test_ccnx_ContentObjectInterface + test_ccnx_InterestFacadeV1 + test_ccnx_InterestInterface + test_ccnx_InterestReturnFacadeV1 + test_ccnx_InterestReturnInterface + test_ccnx_ManifestFacadeV1 + test_ccnx_ManifestInterface + test_ccnx_TlvDictionary + test_ccnx_ValidationFacadeV1 + test_ccnx_WireFormatFacadeV1 +) + + +foreach(test ${TestsExpectedToPass}) + AddTest(${test}) +endforeach() + diff --git a/libccnx-common/ccnx/common/internal/test/test_ccnx_ChunkingFacade b/libccnx-common/ccnx/common/internal/test/test_ccnx_ChunkingFacade Binary files differnew file mode 100755 index 00000000..0894fcbd --- /dev/null +++ b/libccnx-common/ccnx/common/internal/test/test_ccnx_ChunkingFacade diff --git a/libccnx-common/ccnx/common/internal/test/test_ccnx_ChunkingFacadeV1.c b/libccnx-common/ccnx/common/internal/test/test_ccnx_ChunkingFacadeV1.c new file mode 100755 index 00000000..6f14cf47 --- /dev/null +++ b/libccnx-common/ccnx/common/internal/test/test_ccnx_ChunkingFacadeV1.c @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../ccnx_ChunkingFacadeV1.c" +#include <parc/algol/parc_SafeMemory.h> + +#include <LongBow/unit-test.h> +#include <inttypes.h> + +#include <ccnx/common/ccnx_ContentObject.h> +#include <ccnx/common/ccnx_Interest.h> +#include <ccnx/common/ccnx_PayloadType.h> + +#include <ccnx/common/internal/ccnx_ContentObjectInterface.h> + +typedef struct test_data { + CCNxName *name; + CCNxTlvDictionary *contentObjectV1; + CCNxTlvDictionary *contentObjectVFF; + + CCNxTlvDictionary *interest; +} TestData; + +static TestData * +_commonSetup(void) +{ + TestData *data = parcMemory_AllocateAndClear(sizeof(TestData)); + assertNotNull(data, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(TestData)); + + data->name = ccnxName_CreateFromCString("lci:/foo/bar/v1"); + + data->contentObjectV1 = + ccnxContentObject_CreateWithImplAndPayload(&CCNxContentObjectFacadeV1_Implementation, + data->name, + CCNxPayloadType_DATA, NULL); + + data->contentObjectVFF = ccnxTlvDictionary_Create(CCNxCodecSchemaV1TlvDictionary_MessageFastArray_END, CCNxCodecSchemaV1TlvDictionary_Lists_END); + + ccnxTlvDictionary_SetMessageType_ContentObject(data->contentObjectVFF, 0xFF); + + data->interest = + ccnxInterest_CreateWithImpl(&CCNxInterestFacadeV1_Implementation, + data->name, 5000, NULL, NULL, 100); + + return data; +} + +static void +_commonTeardown(TestData *data) +{ + ccnxName_Release(&data->name); + ccnxTlvDictionary_Release(&data->contentObjectV1); + ccnxTlvDictionary_Release(&data->contentObjectVFF); + ccnxTlvDictionary_Release(&data->interest); + parcMemory_Deallocate((void **) &data); +} + +LONGBOW_TEST_RUNNER(ccnx_ChunkingFacade) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(ccnx_ChunkingFacade) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(ccnx_ChunkingFacade) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, ccnxChunkingFacade_GetEndChunkNumber_NotContentObject); + + LONGBOW_RUN_TEST_CASE(Global, ccnxChunkingFacade_GetEndChunkNumber_V1_With); + LONGBOW_RUN_TEST_CASE(Global, ccnxChunkingFacade_GetEndChunkNumber_V1_Without); + LONGBOW_RUN_TEST_CASE(Global, ccnxChunkingFacade_GetEndChunkNumber_InvalidVersion); + + LONGBOW_RUN_TEST_CASE(Global, ccnxChunkingFacade_HasEndChunkNumber_NotContentObject); + LONGBOW_RUN_TEST_CASE(Global, ccnxChunkingFacade_HasEndChunkNumber_V1_With); + LONGBOW_RUN_TEST_CASE(Global, ccnxChunkingFacade_HasEndChunkNumber_V1_Without); + LONGBOW_RUN_TEST_CASE(Global, ccnxChunkingFacade_HasEndChunkNumber_InvalidVersion); + + LONGBOW_RUN_TEST_CASE(Global, ccnxChunkingFacade_SetEndChunkNumber_NotContentObject); + LONGBOW_RUN_TEST_CASE(Global, ccnxChunkingFacade_SetEndChunkNumber_InvalidVersion); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + longBowTestCase_SetClipBoardData(testCase, _commonSetup()); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + _commonTeardown(data); + + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +// ====================================================================================== + +LONGBOW_TEST_CASE_EXPECTS(Global, ccnxChunkingFacade_GetEndChunkNumber_NotContentObject, .event = &LongBowTrapIllegalValue) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + ccnxChunkingFacadeV1_GetEndChunkNumber(data->interest); +} + +LONGBOW_TEST_CASE(Global, ccnxChunkingFacade_GetEndChunkNumber_V1_With) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + uint64_t endChunkNumber = 5; + + ccnxChunkingFacadeV1_SetEndChunkNumber(data->contentObjectV1, endChunkNumber); + uint64_t test = ccnxChunkingFacadeV1_GetEndChunkNumber(data->contentObjectV1); + assertTrue(test == endChunkNumber, "wrong value, got %" PRIu64 " expected %" PRIu64 "", test, endChunkNumber) + { + ccnxTlvDictionary_Display(data->contentObjectV1, 3); + } +} + +LONGBOW_TEST_CASE_EXPECTS(Global, ccnxChunkingFacade_GetEndChunkNumber_V1_Without, .event = &LongBowTrapIllegalValue) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + ccnxChunkingFacadeV1_GetEndChunkNumber(data->contentObjectV1); +} + +LONGBOW_TEST_CASE_EXPECTS(Global, ccnxChunkingFacade_GetEndChunkNumber_InvalidVersion, .event = &LongBowTrapIllegalValue) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + ccnxChunkingFacadeV1_GetEndChunkNumber(data->contentObjectVFF); +} + +// ====================================================================================== + + +LONGBOW_TEST_CASE(Global, ccnxChunkingFacade_HasEndChunkNumber_NotContentObject) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + bool success = ccnxChunkingFacadeV1_HasEndChunkNumber(data->interest); + assertFalse(success, "An Interest should always return FALSE for EndChunkNumber"); +} + +LONGBOW_TEST_CASE(Global, ccnxChunkingFacade_HasEndChunkNumber_V1_With) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + ccnxChunkingFacadeV1_SetEndChunkNumber(data->contentObjectV1, 5); + bool success = ccnxChunkingFacadeV1_HasEndChunkNumber(data->contentObjectV1); + assertTrue(success, "Content Object with EndChunkNumber returned false") + { + ccnxTlvDictionary_Display(data->contentObjectV1, 3); + } +} + +LONGBOW_TEST_CASE(Global, ccnxChunkingFacade_HasEndChunkNumber_V1_Without) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + bool success = ccnxChunkingFacadeV1_HasEndChunkNumber(data->contentObjectV1); + assertFalse(success, "Content Object without EndChunkNumber returned true") + { + ccnxTlvDictionary_Display(data->contentObjectV1, 3); + } +} + +LONGBOW_TEST_CASE_EXPECTS(Global, ccnxChunkingFacade_HasEndChunkNumber_InvalidVersion, .event = &LongBowTrapIllegalValue) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + ccnxChunkingFacadeV1_HasEndChunkNumber(data->contentObjectVFF); +} + +// ====================================================================================== + +LONGBOW_TEST_CASE_EXPECTS(Global, ccnxChunkingFacade_SetEndChunkNumber_NotContentObject, .event = &LongBowTrapIllegalValue) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + uint64_t endChunkNumber = 7; + ccnxChunkingFacadeV1_SetEndChunkNumber(data->interest, endChunkNumber); +} + + +LONGBOW_TEST_CASE(Global, ccnxChunkingFacade_SetEndChunkNumber_V1) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + uint64_t endChunkNumber = 7; + bool success = ccnxChunkingFacadeV1_SetEndChunkNumber(data->contentObjectV1, endChunkNumber); + + assertTrue(success, "Setting EndChunkNumber failed") + { + ccnxTlvDictionary_Display(data->contentObjectV1, 3); + } +} + +LONGBOW_TEST_CASE_EXPECTS(Global, ccnxChunkingFacade_SetEndChunkNumber_InvalidVersion, .event = &LongBowTrapIllegalValue) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + ccnxChunkingFacadeV1_SetEndChunkNumber(data->contentObjectVFF, 7); +} + +// ====================================================================================== + +LONGBOW_TEST_FIXTURE(Local) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(ccnx_ChunkingFacade); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-common/ccnx/common/internal/test/test_ccnx_ContentObjectFacade b/libccnx-common/ccnx/common/internal/test/test_ccnx_ContentObjectFacade Binary files differnew file mode 100755 index 00000000..073ccae4 --- /dev/null +++ b/libccnx-common/ccnx/common/internal/test/test_ccnx_ContentObjectFacade diff --git a/libccnx-common/ccnx/common/internal/test/test_ccnx_ContentObjectFacadeV1.c b/libccnx-common/ccnx/common/internal/test/test_ccnx_ContentObjectFacadeV1.c new file mode 100644 index 00000000..b3c6b343 --- /dev/null +++ b/libccnx-common/ccnx/common/internal/test/test_ccnx_ContentObjectFacadeV1.c @@ -0,0 +1,377 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../ccnx_ContentObjectFacadeV1.c" +#include <parc/algol/parc_SafeMemory.h> +#include <LongBow/unit-test.h> + +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_TlvDictionary.h> +#include <inttypes.h> + +typedef struct test_data { + CCNxName *name; + CCNxPayloadType payloadType; + PARCBuffer *payload; + + // a V1 dictionary but with no values set + CCNxTlvDictionary *contentObjectV1Empty; + + // a V1 nameless Content Object + CCNxTlvDictionary *contentObjectV1Nameless; + + // a valid V1 Content Object + CCNxTlvDictionary *contentObjectV1; + CCNxTlvDictionary *contentObjectVFF; +} TestData; + +static TestData * +_commonSetup(void) +{ + TestData *data = parcMemory_AllocateAndClear(sizeof(TestData)); + assertNotNull(data, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(TestData)); + + data->name = ccnxName_CreateFromCString("lci:/foo/bar"); + data->payloadType = CCNxPayloadType_DATA; + + data->payload = parcBuffer_Flip(parcBuffer_PutArray(parcBuffer_Allocate(20), 7, (uint8_t *) "payload")); + + data->contentObjectV1Empty = ccnxCodecSchemaV1TlvDictionary_CreateContentObject(); + data->contentObjectV1 = _ccnxContentObjectFacadeV1_CreateWithNameAndPayload(data->name, data->payloadType, + data->payload); + data->contentObjectV1Nameless = _ccnxContentObjectFacadeV1_CreateWithPayload(data->payloadType, data->payload); + + data->contentObjectVFF = ccnxTlvDictionary_Create(CCNxCodecSchemaV1TlvDictionary_MessageFastArray_END, CCNxCodecSchemaV1TlvDictionary_Lists_END); + ccnxTlvDictionary_SetMessageType_ContentObject(data->contentObjectVFF, 0xFF); + + return data; +} + +static void +_commonTeardown(TestData *data) +{ + ccnxName_Release(&data->name); + parcBuffer_Release(&data->payload); + + ccnxTlvDictionary_Release(&data->contentObjectV1Empty); + ccnxTlvDictionary_Release(&data->contentObjectV1); + ccnxTlvDictionary_Release(&data->contentObjectV1Nameless); + ccnxTlvDictionary_Release(&data->contentObjectVFF); + parcMemory_Deallocate((void **) &data); +} + + +LONGBOW_TEST_RUNNER(ccnx_ContentObjectFacade) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(ImplInterface); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(ccnx_ContentObjectFacade) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(ccnx_ContentObjectFacade) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// ====================================================================================== + +LONGBOW_TEST_FIXTURE(ImplInterface) +{ + LONGBOW_RUN_TEST_CASE(ImplInterface, ccnxContentObjectFacadeV1_Init); + + LONGBOW_RUN_TEST_CASE(ImplInterface, ccnxContentObjectFacadeV1_GetName); + LONGBOW_RUN_TEST_CASE(ImplInterface, ccnxContentObjectFacadeV1_GetPayload); + LONGBOW_RUN_TEST_CASE(ImplInterface, ccnxContentObjectFacadeV1_SetPayload); + LONGBOW_RUN_TEST_CASE(ImplInterface, ccnxContentObjectFacadeV1_SetPayload_Link); + LONGBOW_RUN_TEST_CASE(ImplInterface, ccnxContentObjectFacadeV1_GetPayloadType); + LONGBOW_RUN_TEST_CASE(ImplInterface, ccnxContentObjectFacadeV1_GetPayloadType_Unset); + LONGBOW_RUN_TEST_CASE(ImplInterface, ccnxContentObjectFacadeV1_GetExpiryTime); + LONGBOW_RUN_TEST_CASE(ImplInterface, ccnxContentObjectFacadeV1_SetExpiryTime); + + LONGBOW_RUN_TEST_CASE(ImplInterface, ccnxContentObjectFacadeV1_SetSignature); + LONGBOW_RUN_TEST_CASE(ImplInterface, ccnxContentObjectFacadeV1_GetKeyId); + + LONGBOW_RUN_TEST_CASE(ImplInterface, ccnxContentObjectFacadeV1_ToString); + LONGBOW_RUN_TEST_CASE(ImplInterface, ccnxContentObjectFacadeV1_Display); + LONGBOW_RUN_TEST_CASE(ImplInterface, ccnxContentObjectFacadeV1_Equals); +} + +LONGBOW_TEST_FIXTURE_SETUP(ImplInterface) +{ + longBowTestCase_SetClipBoardData(testCase, _commonSetup()); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(ImplInterface) +{ + _commonTeardown(longBowTestCase_GetClipBoardData(testCase)); + + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +// ====================================================================================== + +LONGBOW_TEST_CASE(ImplInterface, ccnxContentObjectFacadeV1_Init) +{ + CCNxContentObjectInterface *impl = &CCNxContentObjectFacadeV1_Implementation; + assertNotNull(impl->createWithNameAndPayload, "Expected CreateWithNameAndPayload to be set"); + assertNotNull(impl->createWithPayload, "Expected CreateWithPayload to be set"); + + assertNotNull(impl->getName, "Expected GetName to be set"); + assertNotNull(impl->setSignature, "Expected SetSignature to be set"); + assertNotNull(impl->getKeyId, "Expected GetKeyID to be set"); + assertNotNull(impl->getPayload, "Expected GetPayload to be set"); + assertNotNull(impl->getPayloadType, "Expected GetPayloadType to be set"); + + assertNotNull(impl->hasFinalChunkNumber, "Expected HasFinalChunkNumber to be set"); + assertNotNull(impl->getFinalChunkNumber, "Expected GetFinalChunkNumber to be set"); + assertNotNull(impl->setFinalChunkNumber, "Expected SetFinalChunkNumber to be set"); + + assertNotNull(impl->hasExpiryTime, "Expected HasExpiryTime to be set"); + assertNotNull(impl->getExpiryTime, "Expected GetExpiryTime to be set"); + assertNotNull(impl->setExpiryTime, "Expected SetExpiryTime to be set"); + + assertNotNull(impl->toString, "Expected ToString to be set"); + assertNotNull(impl->display, "Expected Display to be set"); +} + +LONGBOW_TEST_CASE(ImplInterface, ccnxContentObjectFacadeV1_GetName) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + CCNxName *test = _ccnxContentObjectFacadeV1_GetName(data->contentObjectV1); + assertTrue(ccnxName_Equals(test, data->name), "Names do not match") + { + ccnxName_Display(test, 0); + ccnxName_Display(data->name, 0); + } + test = _ccnxContentObjectFacadeV1_GetName(data->contentObjectV1Nameless); + assertNull(test, "A nameless Content Object has no name."); +} + +LONGBOW_TEST_CASE(ImplInterface, ccnxContentObjectFacadeV1_SetPayload) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCBuffer *newPayload = parcBuffer_WrapCString("this is a new payload"); + + // Shouldn't be able to set it on an already initialized ContentObject. + // (Though this may change...) + assertFalse(_ccnxContentObjectFacadeV1_SetPayload(data->contentObjectV1, CCNxPayloadType_DATA, newPayload), + "Expected to not be able to re-assign a payload on an already initialized ContentObject"); + + CCNxTlvDictionary *contentObject = + _ccnxContentObjectFacadeV1_CreateWithNameAndPayload(data->name, + CCNxPayloadType_DATA, + NULL); + + + bool status = _ccnxContentObjectFacadeV1_SetPayload(contentObject, CCNxPayloadType_KEY, newPayload); + assertTrue(status, "Expected to be able to set the buffer"); + + PARCBuffer *testPayload = _ccnxContentObjectFacadeV1_GetPayload(contentObject); + assertTrue(parcBuffer_Equals(newPayload, testPayload), "payloads do not match") + { + parcBuffer_Display(newPayload, 0); + parcBuffer_Display(data->payload, 0); + } + + CCNxPayloadType testType = _ccnxContentObjectFacadeV1_GetPayloadType(contentObject); + assertTrue(testType == CCNxPayloadType_KEY, "Expected type KEY"); + + parcBuffer_Release(&newPayload); + ccnxTlvDictionary_Release(&contentObject); +} + +LONGBOW_TEST_CASE(ImplInterface, ccnxContentObjectFacadeV1_SetPayload_Link) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCBuffer *payload = parcBuffer_WrapCString("this is a payload"); + + CCNxTlvDictionary *contentObject = + _ccnxContentObjectFacadeV1_CreateWithNameAndPayload(data->name, + CCNxPayloadType_LINK, + payload); + + PARCBuffer *testPayload = _ccnxContentObjectFacadeV1_GetPayload(contentObject); + assertTrue(parcBuffer_Equals(payload, testPayload), "payloads do not match") + { + parcBuffer_Display(payload, 0); + parcBuffer_Display(testPayload, 0); + } + + CCNxPayloadType testType = _ccnxContentObjectFacadeV1_GetPayloadType(contentObject); + assertTrue(testType == CCNxPayloadType_LINK, "Expected type LINK"); + + parcBuffer_Release(&payload); + ccnxTlvDictionary_Release(&contentObject); +} + +LONGBOW_TEST_CASE(ImplInterface, ccnxContentObjectFacadeV1_GetPayload) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCBuffer *test = _ccnxContentObjectFacadeV1_GetPayload(data->contentObjectV1); + assertTrue(parcBuffer_Equals(test, data->payload), "payloads do not match") + { + parcBuffer_Display(test, 0); + parcBuffer_Display(data->payload, 0); + } +} + +LONGBOW_TEST_CASE(ImplInterface, ccnxContentObjectFacadeV1_GetPayloadType) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + CCNxPayloadType test = _ccnxContentObjectFacadeV1_GetPayloadType(data->contentObjectV1); + assertTrue(test == data->payloadType, "payloads do not match, got %d expected %d", test, data->payloadType) + { + ccnxTlvDictionary_Display(data->contentObjectV1, 0); + } +} + +LONGBOW_TEST_CASE(ImplInterface, ccnxContentObjectFacadeV1_GetPayloadType_Unset) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + CCNxPayloadType test = _ccnxContentObjectFacadeV1_GetPayloadType(data->contentObjectV1Empty); + assertTrue(test == CCNxPayloadType_DATA, "payloads do not match, got %d expected %d", test, CCNxPayloadType_DATA) + { + ccnxTlvDictionary_Display(data->contentObjectV1, 0); + } +} + +LONGBOW_TEST_CASE_EXPECTS(ImplInterface, ccnxContentObjectFacadeV1_ToString, .event = &LongBowTrapNotImplemented) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + _ccnxContentObjectFacadeV1_ToString(data->contentObjectV1); +} + +LONGBOW_TEST_CASE(ImplInterface, ccnxContentObjectFacadeV1_Display) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + _ccnxContentObjectFacadeV1_Display(data->contentObjectV1, 0); +} + +LONGBOW_TEST_CASE(ImplInterface, ccnxContentObjectFacadeV1_Equals) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + assertTrue(_ccnxContentObjectFacadeV1_Equals(data->contentObjectV1, data->contentObjectV1), + "Expected equals to be true"); + assertFalse(_ccnxContentObjectFacadeV1_Equals(data->contentObjectV1, data->contentObjectV1Empty), + "Expected equals to be false"); +} + +LONGBOW_TEST_CASE(ImplInterface, ccnxContentObjectFacadeV1_GetExpiryTime) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + uint64_t truth = 12345; + ccnxTlvDictionary_PutInteger(data->contentObjectV1, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_EXPIRY_TIME, truth); + + assertTrue(_ccnxContentObjectFacadeV1_HasExpiryTime(data->contentObjectV1), "Expected HasExpiryTime() to return true"); + uint64_t expiryTime = _ccnxContentObjectFacadeV1_GetExpiryTime(data->contentObjectV1); + assertTrue(truth == expiryTime, "times do not match, expected %" PRIu64 ", got %" PRIu64, truth, expiryTime); +} + +LONGBOW_TEST_CASE(ImplInterface, ccnxContentObjectFacadeV1_SetExpiryTime) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + uint64_t truth = 12345; + _ccnxContentObjectFacadeV1_SetExpiryTime(data->contentObjectV1, truth); + uint64_t expiryTime = _ccnxContentObjectFacadeV1_GetExpiryTime(data->contentObjectV1); + assertTrue(truth == expiryTime, "times do not match, expected %" PRIu64 ", got %" PRIu64, truth, expiryTime); +} + +LONGBOW_TEST_CASE(ImplInterface, ccnxContentObjectFacadeV1_SetSignature) +{ + CCNxName *name = ccnxName_CreateFromCString("lci:/foo/bar/baz"); + PARCBuffer *payload = parcBuffer_Allocate(100); + + CCNxTlvDictionary *contentObject = _ccnxContentObjectFacadeV1_CreateWithNameAndPayload(name, CCNxPayloadType_DATA, + payload); + + PARCBuffer *keyId = parcBuffer_WrapCString("keyhash"); + char *rawsig = "siggybits"; + PARCBuffer *sigbits = parcBuffer_CreateFromArray((void *) rawsig, strlen(rawsig)); + + PARCSignature *signature = parcSignature_Create(PARCSigningAlgorithm_RSA, PARCCryptoHashType_SHA256, parcBuffer_Flip(sigbits)); + parcBuffer_Release(&sigbits); + + assertTrue(_ccnxContentObjectFacadeV1_SetSignature(contentObject, keyId, signature, NULL), + "Expected to be able to set the signature"); + + ccnxName_Release(&name); + parcBuffer_Release(&payload); + parcSignature_Release(&signature); + parcBuffer_Release(&keyId); + ccnxTlvDictionary_Release(&contentObject); +} + +LONGBOW_TEST_CASE(ImplInterface, ccnxContentObjectFacadeV1_GetKeyId) +{ + CCNxName *name = ccnxName_CreateFromCString("lci:/foo/bar/baz"); + PARCBuffer *payload = parcBuffer_Allocate(100); + + CCNxTlvDictionary *contentObject = _ccnxContentObjectFacadeV1_CreateWithNameAndPayload(name, CCNxPayloadType_DATA, + payload); + + assertNull(_ccnxContentObjectFacadeV1_GetKeyId(contentObject), "Expect key ID to be NULL"); + + PARCBuffer *testKeyId = parcBuffer_WrapCString("keyhash"); + char *rawsig = "siggybits"; + PARCBuffer *sigbits = parcBuffer_CreateFromArray((void *) rawsig, strlen(rawsig)); + + PARCSignature *signature = parcSignature_Create(PARCSigningAlgorithm_RSA, PARCCryptoHashType_SHA256, parcBuffer_Flip(sigbits)); + parcBuffer_Release(&sigbits); + + assertTrue(_ccnxContentObjectFacadeV1_SetSignature(contentObject, testKeyId, signature, NULL), + "Expected to be able to set the signature"); + + PARCBuffer *keyId = _ccnxContentObjectFacadeV1_GetKeyId(contentObject); + + assertTrue(parcBuffer_Equals(keyId, testKeyId), "Expect key ID's to be the same"); + + ccnxName_Release(&name); + parcBuffer_Release(&payload); + parcSignature_Release(&signature); + parcBuffer_Release(&keyId); + ccnxTlvDictionary_Release(&contentObject); +} + +// ====================================================================================== + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(ccnx_ContentObjectFacade); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-common/ccnx/common/internal/test/test_ccnx_ContentObjectInterface.c b/libccnx-common/ccnx/common/internal/test/test_ccnx_ContentObjectInterface.c new file mode 100755 index 00000000..65e78bed --- /dev/null +++ b/libccnx-common/ccnx/common/internal/test/test_ccnx_ContentObjectInterface.c @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../ccnx_ContentObjectInterface.c" + +#include <stdio.h> + +#include <ccnx/common/ccnx_ContentObject.h> + +#include <parc/algol/parc_SafeMemory.h> +#include <LongBow/unit-test.h> + +LONGBOW_TEST_RUNNER(ccnx_ContentObjectInterface) +{ + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(ccnx_ContentObjectInterface) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(ccnx_ContentObjectInterface) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// ====================================================================================== + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, ccnxContentObjectInterface_GetInterface); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +// ====================================================================================== + + +LONGBOW_TEST_CASE(Global, ccnxContentObjectInterface_GetInterface) +{ + CCNxName *name = ccnxName_CreateFromCString("lci:/boose/roo/pie"); + + CCNxContentObject *contentObjectV1 = + ccnxContentObject_CreateWithImplAndPayload(&CCNxContentObjectFacadeV1_Implementation, + name, + CCNxPayloadType_DATA, + NULL); + + assertTrue(ccnxContentObjectInterface_GetInterface(contentObjectV1) == &CCNxContentObjectFacadeV1_Implementation, + "Expected V1 Implementation"); + + // Now unset the pointer and see if it gets derived properly. + ccnxTlvDictionary_SetMessageInterface(contentObjectV1, NULL); + assertTrue(ccnxContentObjectInterface_GetInterface(contentObjectV1) == &CCNxContentObjectFacadeV1_Implementation, + "Expected V1 Implementation"); + + + ccnxName_Release(&name); + ccnxContentObject_Release(&contentObjectV1); +} + + +// ====================================================================================== + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(ccnx_ContentObjectInterface); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-common/ccnx/common/internal/test/test_ccnx_InterestFacadeV1.c b/libccnx-common/ccnx/common/internal/test/test_ccnx_InterestFacadeV1.c new file mode 100755 index 00000000..477b963d --- /dev/null +++ b/libccnx-common/ccnx/common/internal/test/test_ccnx_InterestFacadeV1.c @@ -0,0 +1,334 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../ccnx_InterestFacadeV1.c" +#include <parc/algol/parc_SafeMemory.h> +#include <LongBow/unit-test.h> + +#include <ccnx/common/ccnx_Interest.h> + +typedef struct test_data { + CCNxTlvDictionary *interest; + + CCNxName *name; + PARCBuffer *keyid; + PARCBuffer *contentObjectHash; + PARCBuffer *payload; + + // allocated data + uint8_t keyidArray[32]; + uint8_t contentObjectHashArray[32]; + uint8_t payloadArray[128]; + + uint32_t lifetime; + uint32_t hoplimit; +} TestData; + +static TestData * +_commonSetup(void) +{ + TestData *data = parcMemory_AllocateAndClear(sizeof(TestData)); + assertNotNull(data, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(TestData)); + data->name = ccnxName_CreateFromCString("lci:/once/upon/a/time"); + + for (int i = 0; i < 32; i++) { + data->keyidArray[i] = i * 7; + data->contentObjectHashArray[i] = i * 11; + } + + for (int i = 0; i < 128; i++) { + data->payloadArray[i] = i * 13; + } + + data->keyid = parcBuffer_Wrap(data->keyidArray, 32, 0, 32); + data->contentObjectHash = parcBuffer_Wrap(data->contentObjectHashArray, 32, 0, 32); + data->payload = parcBuffer_Wrap(data->payloadArray, 128, 0, 128); + + data->lifetime = 900; + data->hoplimit = 77; + + data->interest = _ccnxInterestFacadeV1_Create(data->name, + data->lifetime, + data->keyid, + data->contentObjectHash, + data->hoplimit); + + _ccnxInterestFacadeV1_SetPayload(data->interest, data->payload); + + return data; +} + +static void +_commonTeardown(TestData *data) +{ + ccnxName_Release(&data->name); + parcBuffer_Release(&data->keyid); + parcBuffer_Release(&data->contentObjectHash); + parcBuffer_Release(&data->payload); + ccnxTlvDictionary_Release(&data->interest); + + parcMemory_Deallocate((void **) &data); +} + +LONGBOW_TEST_RUNNER(ccnx_InterestFacadeV1) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Performance); + + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(ccnx_InterestFacadeV1) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(ccnx_InterestFacadeV1) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// ======================================================================================== + +LONGBOW_TEST_FIXTURE(Performance) +{ + LONGBOW_RUN_TEST_CASE(Performance, newfangled); +// LONGBOW_RUN_TEST_CASE(Performance, oldtimey); +} + +LONGBOW_TEST_FIXTURE_SETUP(Performance) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Performance) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Performance, newfangled) +{ + uint8_t keyidArray[32]; + for (int i = 0; i < 32; i++) { + keyidArray[i] = i; + } + + PARCBuffer *keyid = parcBuffer_Wrap(keyidArray, 32, 0, 32); + CCNxName *name = ccnxName_CreateFromCString("lci:/dark/and/stormy/bits"); + + struct timeval t0, t1; + unsigned trials = 10000; + gettimeofday(&t0, NULL); + for (int i = 0; i < trials; i++) { + CCNxTlvDictionary *interest = _ccnxInterestFacadeV1_Create(name, + CCNxInterestDefault_LifetimeMilliseconds, + keyid, + NULL, + 0x45); + + ccnxTlvDictionary_Release(&interest); + } + gettimeofday(&t1, NULL); + timersub(&t1, &t0, &t1); + double seconds = t1.tv_sec + t1.tv_usec * 1E-6; + printf("\nNewFangled iterations %u seconds %.3f msg/sec %.3f\n", trials, seconds, trials / seconds); + + ccnxName_Release(&name); + parcBuffer_Release(&keyid); +} + + +// ======================================================================================== + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, ccnxInterestFacadeV1_CreateSimple); + LONGBOW_RUN_TEST_CASE(Global, ccnxInterestFacadeV1_GetContentObjectHash); + LONGBOW_RUN_TEST_CASE(Global, ccnxInterestFacadeV1_GetHopLimit); + LONGBOW_RUN_TEST_CASE(Global, ccnxInterestFacadeV1_GetInterestLifetime); + LONGBOW_RUN_TEST_CASE(Global, ccnxInterestFacadeV1_GetName); + LONGBOW_RUN_TEST_CASE(Global, ccnxInterestFacadeV1_GetPayload); + LONGBOW_RUN_TEST_CASE(Global, ccnxInterestFacadeV1_GetPublisherPublicKeyDigest); + LONGBOW_RUN_TEST_CASE(Global, ccnxInterestFacadeV1_AssertValid); + LONGBOW_RUN_TEST_CASE(Global, ccnxInterestFacadeV1_Display); + + LONGBOW_RUN_TEST_CASE(Global, ccnxInterestFacadeV1_Equals); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + longBowTestCase_SetClipBoardData(testCase, _commonSetup()); + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + _commonTeardown(longBowTestCase_GetClipBoardData(testCase)); + + if (parcSafeMemory_ReportAllocation(STDOUT_FILENO) != 0) { + printf("('%s' leaks memory by %d (allocs - frees)) ", longBowTestCase_GetName(testCase), parcMemory_Outstanding()); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, ccnxInterestFacadeV1_CreateSimple) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + CCNxTlvDictionary *interest = _ccnxInterestFacadeV1_CreateSimple(data->name); + + CCNxName *test = _ccnxInterestFacadeV1_GetName(interest); + assertTrue(ccnxName_Equals(test, data->name), "Names do not match"); + ccnxTlvDictionary_Release(&interest); +} + +LONGBOW_TEST_CASE(Global, ccnxInterestFacadeV1_GetContentObjectHash) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCBuffer *test = _ccnxInterestFacadeV1_GetContentObjectHashRestriction(data->interest); + assertTrue(parcBuffer_Equals(test, data->contentObjectHash), "ContentObjectHashes do not match") + { + printf("\ngot : \n"); parcBuffer_Display(test, 3); + printf("\nexpected: \n"); parcBuffer_Display(data->contentObjectHash, 3); + } +} + +LONGBOW_TEST_CASE(Global, ccnxInterestFacadeV1_GetHopLimit) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + unsigned test = (unsigned) _ccnxInterestFacadeV1_GetHopLimit(data->interest); + assertTrue(test == data->hoplimit, "Wrong hoplimit: got %u expected %u", test, data->hoplimit); +} + +LONGBOW_TEST_CASE(Global, ccnxInterestFacadeV1_GetInterestLifetime) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + unsigned test = (unsigned) _ccnxInterestFacadeV1_GetLifetime(data->interest); + assertTrue(test == data->lifetime, "Wrong lifetime: got %u expected %u", test, data->lifetime); +} + +LONGBOW_TEST_CASE(Global, ccnxInterestFacadeV1_AssertValid) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + _ccnxInterestFacadeV1_Display(data->interest, 4); +} + +LONGBOW_TEST_CASE(Global, ccnxInterestFacadeV1_Display) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + _ccnxInterestFacadeV1_AssertValid(data->interest); +} + +LONGBOW_TEST_CASE(Global, ccnxInterestFacadeV1_GetName) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + CCNxName *test = _ccnxInterestFacadeV1_GetName(data->interest); + assertTrue(ccnxName_Equals(test, data->name), "Names do not match") + { + printf("\ngot : \n"); ccnxName_Display(test, 3); + printf("\nexpected: \n"); ccnxName_Display(data->name, 3); + } +} + +LONGBOW_TEST_CASE(Global, ccnxInterestFacadeV1_GetPublisherPublicKeyDigest) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCBuffer *test = _ccnxInterestFacadeV1_GetKeyIdRestriction(data->interest); + assertTrue(parcBuffer_Equals(test, data->keyid), "KeyIDs do not match") + { + printf("\ngot : \n"); parcBuffer_Display(test, 3); + printf("\nexpected: \n"); parcBuffer_Display(data->keyid, 3); + } +} + +LONGBOW_TEST_CASE(Global, ccnxInterestFacadeV1_GetPayload) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCBuffer *test = _ccnxInterestFacadeV1_GetPayload(data->interest); + assertTrue(parcBuffer_Equals(test, data->payload), "Payloads do not match") + { + printf("\ngot : \n"); parcBuffer_Display(test, 3); + printf("\nexpected: \n"); parcBuffer_Display(data->payload, 3); + } +} + +LONGBOW_TEST_CASE(Global, ccnxInterestFacadeV1_Equals) +{ + CCNxName *name1 = ccnxName_CreateFromCString("lci:/name/1"); + CCNxName *name2 = ccnxName_CreateFromCString("lci:/name/2"); + + CCNxTlvDictionary *x = _ccnxInterestFacadeV1_CreateSimple(name1); // same + CCNxTlvDictionary *y = _ccnxInterestFacadeV1_CreateSimple(name1); // same + CCNxTlvDictionary *z = _ccnxInterestFacadeV1_CreateSimple(name1); // same + + CCNxTlvDictionary *diff = _ccnxInterestFacadeV1_CreateSimple(name2); // different + + assertEqualsContract(_ccnxInterestFacadeV1_Equals, x, y, z, diff); + + ccnxTlvDictionary_Release(&x); + ccnxTlvDictionary_Release(&y); + ccnxTlvDictionary_Release(&z); + ccnxTlvDictionary_Release(&diff); + + ccnxName_Release(&name1); + ccnxName_Release(&name2); +} + + +// ======================================================================================== + +LONGBOW_TEST_FIXTURE(Local) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(ccnx_InterestFacadeV1); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-common/ccnx/common/internal/test/test_ccnx_InterestInterface.c b/libccnx-common/ccnx/common/internal/test/test_ccnx_InterestInterface.c new file mode 100755 index 00000000..67bde1f5 --- /dev/null +++ b/libccnx-common/ccnx/common/internal/test/test_ccnx_InterestInterface.c @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. + +#include <config.h> +#include <LongBow/unit-test.h> + +#include "../ccnx_InterestInterface.c" + +#include <stdio.h> + +#include <ccnx/common/ccnx_Interest.h> +#include <ccnx/common/internal/ccnx_InterestDefault.h> + +#include <parc/algol/parc_SafeMemory.h> + + +LONGBOW_TEST_RUNNER(ccnx_InterestInterface) +{ + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(ccnx_InterestInterface) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(ccnx_InterestInterface) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// ====================================================================================== + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, ccnxInterestInterface_GetInterface); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +// ====================================================================================== + + +LONGBOW_TEST_CASE(Global, ccnxInterestInterface_GetInterface) +{ + CCNxName *name = ccnxName_CreateFromCString("lci:/boose/roo/pie"); + + CCNxInterest *interestV1 = + ccnxInterest_CreateWithImpl(&CCNxInterestFacadeV1_Implementation, + name, + CCNxInterestDefault_LifetimeMilliseconds, + NULL, + NULL, + CCNxInterestDefault_HopLimit); + + CCNxInterestInterface *interface = ccnxInterestInterface_GetInterface(interestV1); + assertTrue(interface == &CCNxInterestFacadeV1_Implementation, "Expected V1 Implementation"); + + ccnxName_Release(&name); + ccnxInterest_Release(&interestV1); +} + + +// ====================================================================================== + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(ccnx_InterestInterface); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-common/ccnx/common/internal/test/test_ccnx_InterestReturnFacadeV1.c b/libccnx-common/ccnx/common/internal/test/test_ccnx_InterestReturnFacadeV1.c new file mode 100755 index 00000000..1372a750 --- /dev/null +++ b/libccnx-common/ccnx/common/internal/test/test_ccnx_InterestReturnFacadeV1.c @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../ccnx_InterestReturnFacadeV1.c" +#include "../ccnx_InterestFacadeV1.h" +#include <parc/algol/parc_SafeMemory.h> +#include <LongBow/unit-test.h> + +#include <ccnx/common/ccnx_Interest.h> +#include <ccnx/common/ccnx_InterestReturn.h> +#include <ccnx/common/ccnx_PayloadType.h> + +typedef struct test_data { + CCNxTlvDictionary *interest; + + CCNxName *name; + PARCBuffer *keyid; + PARCBuffer *contentObjectHash; + PARCBuffer *payload; + + // allocated data + uint8_t keyidArray[32]; + uint8_t contentObjectHashArray[32]; + uint8_t payloadArray[128]; + + uint32_t lifetime; + uint32_t hoplimit; + CCNxPayloadType payloadType; +} TestData; + + +LONGBOW_TEST_RUNNER(ccnx_InterestReturnFacadeV1) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(ccnx_InterestReturnFacadeV1) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(ccnx_InterestReturnFacadeV1) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// ======================================================================================== + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, ccnxInterestReturnFacadeV1_Create); + LONGBOW_RUN_TEST_CASE(Global, ccnxInterestReturnFacadeV1_GetReturnCode); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + TestData *data = parcMemory_AllocateAndClear(sizeof(TestData)); + assertNotNull(data, "parcMemory_AllocateAndClear(%lu) returned NULL", sizeof(TestData)); + data->name = ccnxName_CreateFromCString("lci:/once/upon/a/time"); + + for (int i = 0; i < 32; i++) { + data->keyidArray[i] = i * 7; + data->contentObjectHashArray[i] = i * 11; + } + + for (int i = 0; i < 128; i++) { + data->payloadArray[i] = i * 13; + } + + data->keyid = parcBuffer_Wrap(data->keyidArray, 32, 0, 32); + data->contentObjectHash = parcBuffer_Wrap(data->contentObjectHashArray, 32, 0, 32); + data->payloadType = CCNxPayloadType_DATA; + data->payload = parcBuffer_Wrap(data->payloadArray, 128, 0, 128); + + data->lifetime = 900; + data->hoplimit = 77; + + data->interest = ccnxInterest_Create(data->name, + data->lifetime, + data->keyid, + data->contentObjectHash); + + ccnxInterest_SetPayload(data->interest, data->payload); + + longBowTestCase_SetClipBoardData(testCase, data); + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + ccnxName_Release(&data->name); + parcBuffer_Release(&data->keyid); + parcBuffer_Release(&data->contentObjectHash); + parcBuffer_Release(&data->payload); + ccnxTlvDictionary_Release(&data->interest); + + parcMemory_Deallocate((void **) &data); + + if (parcSafeMemory_ReportAllocation(STDOUT_FILENO) != 0) { + printf("('%s' leaks memory by %d (allocs - frees)) ", longBowTestCase_GetName(testCase), parcMemory_Outstanding()); + return LONGBOW_STATUS_TEARDOWN_FAILED; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, ccnxInterestReturnFacadeV1_Create) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + CCNxTlvDictionary *interestReturn = + _ccnxInterestReturnFacadeV1_Create(data->interest, CCNxInterestReturn_ReturnCode_NoRoute); + assertNotNull(interestReturn, "Expect non-NULL interestReturn"); + _ccnxInterestReturnFacadeV1_AssertValid(interestReturn); + + CCNxInterestReturn_ReturnCode code = _ccnxInterestReturnFacadeV1_GetReturnCode(interestReturn); + assertTrue((CCNxInterestReturn_ReturnCode_NoRoute == code), "InterestReturn wrong Return Code"); + ccnxTlvDictionary_Release(&interestReturn); +} + +LONGBOW_TEST_CASE(Global, ccnxInterestReturnFacadeV1_GetReturnCode) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + CCNxTlvDictionary *interestReturn = + _ccnxInterestReturnFacadeV1_Create(data->interest, CCNxInterestReturn_ReturnCode_NoRoute); + _ccnxInterestReturnFacadeV1_AssertValid(interestReturn); + + CCNxInterestReturn_ReturnCode code = + _ccnxInterestReturnFacadeV1_GetReturnCode(interestReturn); + + assertTrue((CCNxInterestReturn_ReturnCode_NoRoute == code), "InterestReturn wrong Return Code"); + ccnxTlvDictionary_Release(&interestReturn); +} + + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(ccnx_InterestReturnFacadeV1); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-common/ccnx/common/internal/test/test_ccnx_InterestReturnInterface.c b/libccnx-common/ccnx/common/internal/test/test_ccnx_InterestReturnInterface.c new file mode 100755 index 00000000..a22ab4e9 --- /dev/null +++ b/libccnx-common/ccnx/common/internal/test/test_ccnx_InterestReturnInterface.c @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../ccnx_InterestReturnInterface.c" + +#include <ccnx/common/ccnx_InterestReturn.h> +#include <ccnx/common/internal/ccnx_InterestDefault.h> + +#include <parc/algol/parc_SafeMemory.h> +#include <LongBow/unit-test.h> + +#include <stdio.h> + +LONGBOW_TEST_RUNNER(ccnx_InterestReturnInterface) +{ + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(ccnx_InterestReturnInterface) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(ccnx_InterestReturnInterface) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// ====================================================================================== + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, ccnxInterestReturnInterface_GetImplementation); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +// ====================================================================================== + + +LONGBOW_TEST_CASE(Global, ccnxInterestReturnInterface_GetImplementation) +{ + CCNxName *name = ccnxName_CreateFromCString("lci:/boose/roo/pie"); + + CCNxInterest *interestV1 = + ccnxInterest_CreateWithImpl(&CCNxInterestFacadeV1_Implementation, + name, + CCNxInterestDefault_LifetimeMilliseconds, + NULL, + NULL, + CCNxInterestDefault_HopLimit); + ccnxName_Release(&name); + + CCNxInterestReturn *interestReturn = ccnxInterestReturn_Create(interestV1, CCNxInterestReturn_ReturnCode_Congestion); + + assertTrue(ccnxInterestReturnInterface_GetInterface(interestReturn) == &CCNxInterestReturnFacadeV1_Implementation, + "Expected V1 Implementation"); + + // Now unset the pointer and see if it gets derived properly. + ccnxTlvDictionary_SetMessageInterface(interestReturn, NULL); + + assertTrue(ccnxInterestReturnInterface_GetInterface(interestReturn) == &CCNxInterestReturnFacadeV1_Implementation, + "Expected V1 Implementation"); + + ccnxInterestReturn_Release(&interestReturn); + ccnxInterest_Release(&interestV1); +} + + +// ====================================================================================== + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(ccnx_InterestReturnInterface); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-common/ccnx/common/internal/test/test_ccnx_ManifestFacadeV1.c.c b/libccnx-common/ccnx/common/internal/test/test_ccnx_ManifestFacadeV1.c.c new file mode 100644 index 00000000..253b8e5a --- /dev/null +++ b/libccnx-common/ccnx/common/internal/test/test_ccnx_ManifestFacadeV1.c.c @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../ccnx_ManifestFacadeV1.c" +#include <parc/algol/parc_SafeMemory.h> +#include <parc/algol/parc_Memory.h> +#include <LongBow/unit-test.h> + +#include <ccnx/common/ccnx_Manifest.h> + +typedef struct test_data { + CCNxTlvDictionary *manifest; + + CCNxName *name; +} TestData; + +static TestData * +_commonSetup(void) +{ + TestData *data = parcMemory_AllocateAndClear(sizeof(TestData)); + assertNotNull(data, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(TestData)); + + data->name = ccnxName_CreateFromCString("lci:/once/upon/a/time"); + data->manifest = _ccnxManifestFacadeV1_Create(data->name); + + return data; +} + +static void +_commonTeardown(TestData *data) +{ + ccnxName_Release(&data->name); + ccnxTlvDictionary_Release(&data->manifest); + + parcMemory_Deallocate((void **) &data); +} + +LONGBOW_TEST_RUNNER(ccnx_ManifestFacadeV1) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(ccnx_ManifestFacadeV1) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(ccnx_ManifestFacadeV1) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// ======================================================================================== + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, ccnxManifestFacadeV1_Create); + LONGBOW_RUN_TEST_CASE(Global, ccnxManifestFacadeV1_GetName); + LONGBOW_RUN_TEST_CASE(Global, ccnxManifestFacadeV1_AddHashGroup); + LONGBOW_RUN_TEST_CASE(Global, ccnxManifestFacadeV1_GetHashGroup); + LONGBOW_RUN_TEST_CASE(Global, ccnxManifestFacadeV1_Equals); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + longBowTestCase_SetClipBoardData(testCase, _commonSetup()); + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + _commonTeardown(longBowTestCase_GetClipBoardData(testCase)); + + if (parcSafeMemory_ReportAllocation(STDOUT_FILENO) != 0) { + printf("('%s' leaks memory by %d (allocs - frees)) ", longBowTestCase_GetName(testCase), parcMemory_Outstanding()); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, ccnxManifestFacadeV1_Create) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + CCNxTlvDictionary *manifest = _ccnxManifestFacadeV1_Create(data->name); + + const CCNxName *test = _ccnxManifestFacadeV1_GetName(manifest); + assertTrue(ccnxName_Equals(test, data->name), "Names do not match"); + ccnxTlvDictionary_Release(&manifest); +} + +LONGBOW_TEST_CASE(Global, ccnxManifestFacadeV1_AddHashGroup) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + CCNxTlvDictionary *manifest = _ccnxManifestFacadeV1_Create(data->name); + + int actual = 0; + int numberOfGroups = _ccnxManifestFacadeV1_GetNumberOfHashGroups(manifest); + assertTrue(numberOfGroups == actual, "Expected %d, got %d", actual, numberOfGroups); + + CCNxManifestHashGroup *hashGroup = ccnxManifestHashGroup_Create(); + _ccnxManifestFacadeV1_AddHashGroup(manifest, hashGroup); + actual++; + numberOfGroups = _ccnxManifestFacadeV1_GetNumberOfHashGroups(manifest); + + assertTrue(numberOfGroups == actual, "Expected %d, got %d", actual, numberOfGroups); + + ccnxManifestHashGroup_Release(&hashGroup); + ccnxTlvDictionary_Release(&manifest); +} + +LONGBOW_TEST_CASE(Global, ccnxManifestFacadeV1_GetHashGroup) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + CCNxTlvDictionary *manifest = _ccnxManifestFacadeV1_Create(data->name); + + CCNxManifestHashGroup *hashGroup = ccnxManifestHashGroup_Create(); + _ccnxManifestFacadeV1_AddHashGroup(manifest, hashGroup); + + CCNxManifestHashGroup *firstGroup = _ccnxManifestFacadeV1_GetHashGroup(manifest, 0); + assertTrue(ccnxManifestHashGroup_Equals(hashGroup, firstGroup), "Expected the HashGroups to be equal"); + + ccnxManifestHashGroup_Release(&hashGroup); + ccnxManifestHashGroup_Release(&firstGroup); + ccnxTlvDictionary_Release(&manifest); +} + +LONGBOW_TEST_CASE(Global, ccnxManifestFacadeV1_GetNumberOfHashGroups) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + CCNxTlvDictionary *manifest = _ccnxManifestFacadeV1_Create(data->name); + + size_t numberOfGroups = _ccnxManifestFacadeV1_GetNumberOfHashGroups(manifest); + assertTrue(numberOfGroups == 1, "Expected 1 group, got %zu", numberOfGroups); + ccnxTlvDictionary_Release(&manifest); +} + +LONGBOW_TEST_CASE(Global, ccnxManifestFacadeV1_GetName) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + const CCNxName *test = _ccnxManifestFacadeV1_GetName(data->manifest); + assertTrue(ccnxName_Equals(test, data->name), "Names do not match") + { + printf("\ngot : \n"); ccnxName_Display(test, 3); + printf("\nexpected: \n"); ccnxName_Display(data->name, 3); + } +} + +LONGBOW_TEST_CASE(Global, ccnxManifestFacadeV1_Equals) +{ + CCNxName *name1 = ccnxName_CreateFromCString("lci:/name/1"); + CCNxName *name2 = ccnxName_CreateFromCString("lci:/name/2"); + + CCNxTlvDictionary *x = _ccnxManifestFacadeV1_Create(name1); + CCNxTlvDictionary *y = _ccnxManifestFacadeV1_Create(name1); + CCNxTlvDictionary *z = _ccnxManifestFacadeV1_Create(name1); + + CCNxTlvDictionary *diff = _ccnxManifestFacadeV1_Create(name2); + + assertEqualsContract(_ccnxManifestFacadeV1_Equals, x, y, z, diff, NULL); + + ccnxTlvDictionary_Release(&x); + ccnxTlvDictionary_Release(&y); + ccnxTlvDictionary_Release(&z); + ccnxTlvDictionary_Release(&diff); + + ccnxName_Release(&name1); + ccnxName_Release(&name2); +} + +// ======================================================================================== + +LONGBOW_TEST_FIXTURE(Local) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(ccnx_ManifestFacadeV1); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-common/ccnx/common/internal/test/test_ccnx_ManifestInterface.c.c b/libccnx-common/ccnx/common/internal/test/test_ccnx_ManifestInterface.c.c new file mode 100755 index 00000000..42ce095f --- /dev/null +++ b/libccnx-common/ccnx/common/internal/test/test_ccnx_ManifestInterface.c.c @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../ccnx_ManifestInterface.c" + +#include <ccnx/common/ccnx_Manifest.h> + +#include <parc/algol/parc_SafeMemory.h> +#include <LongBow/unit-test.h> + +#include <stdio.h> + +LONGBOW_TEST_RUNNER(ccnx_ManifestInterface) +{ + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(ccnx_ManifestInterface) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(ccnx_ManifestInterface) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// ====================================================================================== + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, ccnxManifestInterface_GetImplementation); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +// ====================================================================================== + + +LONGBOW_TEST_CASE(Global, ccnxManifestInterface_GetImplementation) +{ + CCNxName *name = ccnxName_CreateFromCString("lci:/boose/roo/pie"); + + CCNxManifest *manifestV1 = ccnxManifest_Create(name); + ccnxName_Release(&name); + + assertTrue(ccnxManifestInterface_GetInterface(manifestV1) == &CCNxManifestFacadeV1_Interface, "Expected V1 Implementation"); + + // Now unset the pointer and see if it gets derived properly. + ccnxTlvDictionary_SetMessageInterface(manifestV1, NULL); + + assertTrue(ccnxManifestInterface_GetInterface(manifestV1) == &CCNxManifestFacadeV1_Interface, "Expected V1 Implementation"); + + ccnxManifest_Release(&manifestV1); +} + + +// ====================================================================================== + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(ccnx_ManifestInterface); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-common/ccnx/common/internal/test/test_ccnx_TlvDictionary.c b/libccnx-common/ccnx/common/internal/test/test_ccnx_TlvDictionary.c new file mode 100755 index 00000000..51936d96 --- /dev/null +++ b/libccnx-common/ccnx/common/internal/test/test_ccnx_TlvDictionary.c @@ -0,0 +1,1142 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../ccnx_TlvDictionary.c" +#include <parc/algol/parc_SafeMemory.h> + +#include <ccnx/common/internal/ccnx_ContentObjectInterface.h> +#include <ccnx/common/internal/ccnx_InterestInterface.h> + +#include <LongBow/unit-test.h> + +typedef struct test_data { + CCNxTlvDictionary *dictionary; + size_t fastArraySize; + size_t listSize; +} TestData; + +typedef enum { + SchemaFree = 0, + SchemaBuffer = 1, + SchemaInteger = 2, + SchemaIoVec = 3, + SchemaJson = 4, + SchemaName = 5, + SchemaEnd = 6, +} TestSchema; + +static CCNxCodecNetworkBufferIoVec * +createIoVec(void) +{ + CCNxCodecNetworkBuffer *netbuff = ccnxCodecNetworkBuffer_Create(&ParcMemoryMemoryBlock, NULL); + CCNxCodecNetworkBufferIoVec *vec = ccnxCodecNetworkBuffer_CreateIoVec(netbuff); + ccnxCodecNetworkBuffer_Release(&netbuff); + return vec; +} + +static TestData * +_commonSetup(void) +{ + TestData *data = parcMemory_AllocateAndClear(sizeof(TestData)); + assertNotNull(data, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(TestData)); + data->fastArraySize = SchemaEnd + 2; + data->listSize = SchemaEnd + 10; + data->dictionary = ccnxTlvDictionary_Create(data->fastArraySize, data->listSize); + + // populate the know test vectors + PARCBuffer *buffer = parcBuffer_Allocate(5); + ccnxTlvDictionary_PutBuffer(data->dictionary, SchemaBuffer, buffer); + parcBuffer_Release(&buffer); + + ccnxTlvDictionary_PutInteger(data->dictionary, SchemaInteger, 42); + + CCNxCodecNetworkBufferIoVec *vec = createIoVec(); + ccnxTlvDictionary_PutIoVec(data->dictionary, SchemaIoVec, vec); + ccnxCodecNetworkBufferIoVec_Release(&vec); + + PARCJSON *json = parcJSON_ParseString("{\"KEY\": \"VALUE\"}"); + ccnxTlvDictionary_PutJson(data->dictionary, SchemaJson, json); + parcJSON_Release(&json); + + CCNxName *name = ccnxName_CreateFromCString("lci:/great/gatsby"); + ccnxTlvDictionary_PutName(data->dictionary, SchemaName, name); + ccnxName_Release(&name); + + return data; +} + +static void +_commonTeardown(TestData *data) +{ + ccnxTlvDictionary_Release(&data->dictionary); + parcMemory_Deallocate((void **) &data); +} + +// ============================================================= + +LONGBOW_TEST_RUNNER(rta_TlvDictionary) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(KnownKeys); + LONGBOW_RUN_TEST_FIXTURE(UnknownKeys); + LONGBOW_RUN_TEST_FIXTURE(Local); + + // type specific tests + LONGBOW_RUN_TEST_FIXTURE(Buffer); + LONGBOW_RUN_TEST_FIXTURE(Integer); + LONGBOW_RUN_TEST_FIXTURE(IoVec); + LONGBOW_RUN_TEST_FIXTURE(Json); + LONGBOW_RUN_TEST_FIXTURE(Name); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(rta_TlvDictionary) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(rta_TlvDictionary) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// ============================================================= + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, ccnxTlvDictionary_Performance); + LONGBOW_RUN_TEST_CASE(Global, ccnxTlvDictionary_Acquire); + LONGBOW_RUN_TEST_CASE(Global, ccnxTlvDictionary_Create); + LONGBOW_RUN_TEST_CASE(Global, ccnxTlvDictionary_Release); + LONGBOW_RUN_TEST_CASE(Global, ccnxTlvDictionary_SetMessageType_ContentObject); + LONGBOW_RUN_TEST_CASE(Global, ccnxTlvDictionary_SetMessageType_Interest); + LONGBOW_RUN_TEST_CASE(Global, ccnxTlvDictionary_SetMessageType_Control); + LONGBOW_RUN_TEST_CASE(Global, ccnxTlvDictionary_SetMessageType_InterestReturn); + LONGBOW_RUN_TEST_CASE(Global, ccnxTlvDictionary_SetGetMessageTypeImplementation); + + + LONGBOW_RUN_TEST_CASE(Global, ccnxTlvDictionary_Equals); + LONGBOW_RUN_TEST_CASE(Global, ccnxTlvDictionary_ShallowCopy); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + longBowTestCase_SetClipBoardData(testCase, _commonSetup()); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + _commonTeardown(longBowTestCase_GetClipBoardData(testCase)); + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, ccnxTlvDictionary_Performance) +{ + int reps = 100000; + struct timeval t0, t1; + gettimeofday(&t0, NULL); + for (int i = 0; i < reps; i++) { + CCNxTlvDictionary *dictionary = ccnxTlvDictionary_Create(10, 20); + ccnxTlvDictionary_Release(&dictionary); + } + gettimeofday(&t1, NULL); + timersub(&t1, &t0, &t1); + double seconds = t1.tv_sec + t1.tv_usec * 1E-6; + + printf("time %.6f seconds, tps = %.2f\n", seconds, (double) reps / seconds); +} + +LONGBOW_TEST_CASE(Global, ccnxTlvDictionary_Acquire) +{ + CCNxTlvDictionary *first = ccnxTlvDictionary_Create(10, 20); + CCNxTlvDictionary *second = ccnxTlvDictionary_Acquire(first); + + assertTrue(parcObject_GetReferenceCount(first) == 2, "Wrong ref count, got %" PRIu64 " expected %u", parcObject_GetReferenceCount(first), 2); + + ccnxTlvDictionary_Release(&second); + ccnxTlvDictionary_Release(&first); +} + +LONGBOW_TEST_CASE(Global, ccnxTlvDictionary_Create) +{ + CCNxTlvDictionary *dictionary = ccnxTlvDictionary_Create(20, 30); + assertNotNull(dictionary, "Got null dictionary from Create"); + assertNotNull(dictionary->directArray, "DirectArray is null"); + assertNotNull(dictionary->fixedListHeads, "fixedListHeads is null"); + + ccnxTlvDictionary_Release(&dictionary); +} + +LONGBOW_TEST_CASE(Global, ccnxTlvDictionary_Release) +{ + CCNxTlvDictionary *dictionary = ccnxTlvDictionary_Create(1, 1); + ccnxTlvDictionary_Release(&dictionary); + assertNull(dictionary, "Release did not null argument"); +} + +LONGBOW_TEST_CASE(Global, ccnxTlvDictionary_SetMessageType_ContentObject) +{ + CCNxTlvDictionary *dictionary = ccnxTlvDictionary_Create(1, 1); + ccnxTlvDictionary_SetMessageType_ContentObject(dictionary, CCNxTlvDictionary_SchemaVersion_V1); + assertTrue(ccnxTlvDictionary_IsContentObject(dictionary), "Wrong message type"); + assertFalse(ccnxTlvDictionary_IsControl(dictionary), "Wrong message type"); + assertFalse(ccnxTlvDictionary_IsInterest(dictionary), "Wrong message type"); + assertFalse(ccnxTlvDictionary_IsInterestReturn(dictionary), "Wrong message type"); + assertTrue(ccnxTlvDictionary_GetSchemaVersion(dictionary) == CCNxTlvDictionary_SchemaVersion_V1, "Wrong schema type"); + ccnxTlvDictionary_Release(&dictionary); +} + +LONGBOW_TEST_CASE(Global, ccnxTlvDictionary_SetMessageType_Interest) +{ + CCNxTlvDictionary *dictionary = ccnxTlvDictionary_Create(1, 1); + ccnxTlvDictionary_SetMessageType_Interest(dictionary, CCNxTlvDictionary_SchemaVersion_V1); + assertFalse(ccnxTlvDictionary_IsContentObject(dictionary), "Wrong message type"); + assertFalse(ccnxTlvDictionary_IsControl(dictionary), "Wrong message type"); + assertTrue(ccnxTlvDictionary_IsInterest(dictionary), "Wrong message type"); + assertFalse(ccnxTlvDictionary_IsInterestReturn(dictionary), "Wrong message type"); + assertTrue(ccnxTlvDictionary_GetSchemaVersion(dictionary) == CCNxTlvDictionary_SchemaVersion_V1, "Wrong schema type"); + ccnxTlvDictionary_Release(&dictionary); +} + +LONGBOW_TEST_CASE(Global, ccnxTlvDictionary_SetMessageType_Control) +{ + CCNxTlvDictionary *dictionary = ccnxTlvDictionary_Create(1, 1); + ccnxTlvDictionary_SetMessageType_Control(dictionary, CCNxTlvDictionary_SchemaVersion_V1); + assertFalse(ccnxTlvDictionary_IsContentObject(dictionary), "Wrong message type"); + assertTrue(ccnxTlvDictionary_IsControl(dictionary), "Wrong message type"); + assertFalse(ccnxTlvDictionary_IsInterest(dictionary), "Wrong message type"); + assertFalse(ccnxTlvDictionary_IsInterestReturn(dictionary), "Wrong message type"); + assertTrue(ccnxTlvDictionary_GetSchemaVersion(dictionary) == CCNxTlvDictionary_SchemaVersion_V1, "Wrong schema type"); + ccnxTlvDictionary_Release(&dictionary); +} + +LONGBOW_TEST_CASE(Global, ccnxTlvDictionary_SetMessageType_InterestReturn) +{ + CCNxTlvDictionary *dictionary = ccnxTlvDictionary_Create(1, 1); + ccnxTlvDictionary_SetMessageType_InterestReturn(dictionary, CCNxTlvDictionary_SchemaVersion_V1); + assertFalse(ccnxTlvDictionary_IsContentObject(dictionary), "Wrong message type"); + assertFalse(ccnxTlvDictionary_IsControl(dictionary), "Wrong message type"); + assertFalse(ccnxTlvDictionary_IsInterest(dictionary), "Wrong message type"); + assertTrue(ccnxTlvDictionary_IsInterestReturn(dictionary), "Wrong message type"); + assertTrue(ccnxTlvDictionary_GetSchemaVersion(dictionary) == CCNxTlvDictionary_SchemaVersion_V1, "Wrong schema type"); + ccnxTlvDictionary_Release(&dictionary); +} + +LONGBOW_TEST_CASE(Global, ccnxTlvDictionary_SetGetMessageTypeImplementation) +{ + CCNxTlvDictionary *dictionary = ccnxTlvDictionary_Create(1, 1); + void *impl = ccnxTlvDictionary_GetMessageInterface(dictionary); + + assertNull(impl, "Expected a NULL implementation by default"); + + ccnxTlvDictionary_SetMessageInterface(dictionary, &CCNxContentObjectFacadeV1_Implementation); + assertTrue(ccnxTlvDictionary_GetMessageInterface(dictionary) == &CCNxContentObjectFacadeV1_Implementation, + "Expected CCNxContentObjectFacadeV1_Implementation"); + + ccnxTlvDictionary_SetMessageInterface(dictionary, &CCNxContentObjectFacadeV1_Implementation); + assertTrue(ccnxTlvDictionary_GetMessageInterface(dictionary) == &CCNxContentObjectFacadeV1_Implementation, + "Expected CCNxContentObjectFacadeV1_Implementation"); + + ccnxTlvDictionary_SetMessageInterface(dictionary, &CCNxInterestFacadeV1_Implementation); + assertTrue(ccnxTlvDictionary_GetMessageInterface(dictionary) == &CCNxInterestFacadeV1_Implementation, + "Expected CCNxInterestFacadeV1_Implementation"); + + ccnxTlvDictionary_SetMessageInterface(dictionary, &CCNxInterestFacadeV1_Implementation); + assertTrue(ccnxTlvDictionary_GetMessageInterface(dictionary) == &CCNxInterestFacadeV1_Implementation, + "Expected CCNxInterestFacadeV1_Implementation"); + + ccnxTlvDictionary_Release(&dictionary); +} + +LONGBOW_TEST_CASE(Global, ccnxTlvDictionary_Equals) +{ + CCNxTlvDictionary *a = ccnxTlvDictionary_Create(1, 1); + CCNxTlvDictionary *b = ccnxTlvDictionary_Create(1, 1); + CCNxTlvDictionary *c = ccnxTlvDictionary_Create(1, 1); + + ccnxTlvDictionary_SetMessageType_Interest(a, CCNxTlvDictionary_SchemaVersion_V1); + ccnxTlvDictionary_SetMessageType_Interest(b, CCNxTlvDictionary_SchemaVersion_V1); + ccnxTlvDictionary_SetMessageType_Interest(c, CCNxTlvDictionary_SchemaVersion_V1); + + CCNxTlvDictionary *diffArraySize = ccnxTlvDictionary_Create(2, 1); + CCNxTlvDictionary *diffListSize = ccnxTlvDictionary_Create(1, 2); + CCNxTlvDictionary *diffType = ccnxTlvDictionary_Create(1, 1); + ccnxTlvDictionary_SetMessageType_Control(diffType, CCNxTlvDictionary_SchemaVersion_V1); + + CCNxTlvDictionary *diffType2 = ccnxTlvDictionary_Create(1, 1); + ccnxTlvDictionary_SetMessageType_ContentObject(diffType2, CCNxTlvDictionary_SchemaVersion_V1); + + assertEqualsContract(ccnxTlvDictionary_Equals, a, b, c, diffArraySize, diffListSize, diffType, diffType2, NULL); + + ccnxTlvDictionary_Release(&diffType2); + ccnxTlvDictionary_Release(&diffType); + ccnxTlvDictionary_Release(&diffListSize); + ccnxTlvDictionary_Release(&diffArraySize); + ccnxTlvDictionary_Release(&c); + ccnxTlvDictionary_Release(&b); + ccnxTlvDictionary_Release(&a); +} + +LONGBOW_TEST_CASE(Global, ccnxTlvDictionary_ShallowCopy) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + CCNxTlvDictionary *a = data->dictionary; + + PARCBuffer *buffer = parcBuffer_WrapCString("Some Stuff"); + ccnxTlvDictionary_PutListBuffer(a, SchemaEnd, 23, buffer); + parcBuffer_Release(&buffer); + + CCNxTlvDictionary *b = ccnxTlvDictionary_ShallowCopy(a); + + assertTrue(ccnxTlvDictionary_Equals(a, b), "Expected Dictionaries to be Equal after ShallowCopy"); + + ccnxTlvDictionary_Release(&b); +} + +// ================================================================ + +LONGBOW_TEST_FIXTURE(KnownKeys) +{ + LONGBOW_RUN_TEST_CASE(KnownKeys, ccnxTlvDictionary_Get_Exists); + LONGBOW_RUN_TEST_CASE(KnownKeys, ccnxTlvDictionary_Get_NotExists); + LONGBOW_RUN_TEST_CASE(KnownKeys, ccnxTlvDictionary_Put_Unique); + LONGBOW_RUN_TEST_CASE(KnownKeys, ccnxTlvDictionary_Put_Duplicate); + + LONGBOW_RUN_TEST_CASE(KnownKeys, ccnxTlvDictionary_PutList_Unique); + LONGBOW_RUN_TEST_CASE(KnownKeys, ccnxTlvDictionary_PutList_Duplicate); +} + +LONGBOW_TEST_FIXTURE_SETUP(KnownKeys) +{ + longBowTestCase_SetClipBoardData(testCase, _commonSetup()); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(KnownKeys) +{ + _commonTeardown(longBowTestCase_GetClipBoardData(testCase)); + + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(KnownKeys, ccnxTlvDictionary_Get_Exists) +{ + uint32_t key = SchemaEnd; + + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCBuffer *buffer = parcBuffer_Allocate(1); + + ccnxTlvDictionary_PutBuffer(data->dictionary, key, buffer); + PARCBuffer *test = ccnxTlvDictionary_GetBuffer(data->dictionary, key); + assertTrue(test == buffer, "Get value wrong, got %p expected %p", (void *) test, (void *) buffer); + + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(KnownKeys, ccnxTlvDictionary_Get_NotExists) +{ + uint32_t key = SchemaEnd; + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + PARCBuffer *buffer = parcBuffer_Allocate(1); + ccnxTlvDictionary_PutBuffer(data->dictionary, key, buffer); + PARCBuffer *test = ccnxTlvDictionary_GetBuffer(data->dictionary, key + 1); + assertNull(test, "Get for missing key should return null"); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(KnownKeys, ccnxTlvDictionary_Put_Unique) +{ + uint32_t key = SchemaEnd; + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCBuffer *buffer = parcBuffer_Allocate(1); + bool success = ccnxTlvDictionary_PutBuffer(data->dictionary, key, buffer); + assertTrue(success, "Put returned false adding a unique key"); + parcBuffer_Release(&buffer); + + // one extra test particular to it being in the fast array + assertTrue(data->dictionary->directArray[key].entryType == ENTRY_BUFFER, "Not buffer type, got %d", data->dictionary->directArray[key].entryType); + assertNotNull(data->dictionary->directArray[key]._entry.buffer, "They fast array entry for key is null"); +} + +LONGBOW_TEST_CASE(KnownKeys, ccnxTlvDictionary_Put_Duplicate) +{ + uint32_t key = SchemaEnd; + + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCBuffer *buffer = parcBuffer_Allocate(1); + ccnxTlvDictionary_PutBuffer(data->dictionary, key, buffer); + bool success = ccnxTlvDictionary_PutBuffer(data->dictionary, key, buffer); + assertFalse(success, "Put returned true adding a duplicate key"); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(KnownKeys, ccnxTlvDictionary_PutList_Unique) +{ + uint32_t listKey = SchemaEnd; + uint32_t bufferKey = 1000; + + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCBuffer *buffer = parcBuffer_Allocate(1); + bool success = ccnxTlvDictionary_PutListBuffer(data->dictionary, listKey, bufferKey, buffer); + assertTrue(success, "Put returned false adding a unique key"); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(KnownKeys, ccnxTlvDictionary_PutList_Duplicate) +{ + uint32_t listKey = SchemaEnd; + uint32_t bufferKey = 1000; + + // its ok to have duplicates of the custom keys + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCBuffer *buffer = parcBuffer_Allocate(1); + ccnxTlvDictionary_PutListBuffer(data->dictionary, listKey, bufferKey, buffer); + bool success = ccnxTlvDictionary_PutListBuffer(data->dictionary, listKey, bufferKey, buffer); + assertTrue(success, "Put returned false adding a duplicate key to list"); + parcBuffer_Release(&buffer); +} + +// ================================================================ + +LONGBOW_TEST_FIXTURE(UnknownKeys) +{ + LONGBOW_RUN_TEST_CASE(UnknownKeys, ccnxTlvDictionary_PutList_Unique); + LONGBOW_RUN_TEST_CASE(UnknownKeys, ccnxTlvDictionary_PutList_Duplicate); + + LONGBOW_RUN_TEST_CASE(UnknownKeys, ccnxTlvDictionary_ListGetByPosition); + LONGBOW_RUN_TEST_CASE(UnknownKeys, ccnxTlvDictionary_ListGetByType); + LONGBOW_RUN_TEST_CASE(UnknownKeys, ccnxTlvDictionary_ListSize); + LONGBOW_RUN_TEST_CASE(UnknownKeys, ccnxTlvDictionary_ListEquals); +} + +LONGBOW_TEST_FIXTURE_SETUP(UnknownKeys) +{ + longBowTestCase_SetClipBoardData(testCase, _commonSetup()); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(UnknownKeys) +{ + _commonTeardown(longBowTestCase_GetClipBoardData(testCase)); + + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(UnknownKeys, ccnxTlvDictionary_PutList_Unique) +{ + uint32_t listKey = FIXED_LIST_LENGTH + 1; + uint32_t bufferKey = 1000; + + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCBuffer *buffer = parcBuffer_Allocate(1); + bool success = ccnxTlvDictionary_PutListBuffer(data->dictionary, listKey, bufferKey, buffer); + assertTrue(success, "Put returned false adding a unique key"); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(UnknownKeys, ccnxTlvDictionary_PutList_Duplicate) +{ + uint32_t listKey = FIXED_LIST_LENGTH + 1; + uint32_t bufferKey = 1000; + + // its ok to have duplicates of the custom keys + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCBuffer *buffer = parcBuffer_Allocate(1); + ccnxTlvDictionary_PutListBuffer(data->dictionary, listKey, bufferKey, buffer); + bool success = ccnxTlvDictionary_PutListBuffer(data->dictionary, listKey, bufferKey, buffer); + assertTrue(success, "Put returned false adding a duplicate key to list"); + parcBuffer_Release(&buffer); +} + +/* + * Add 3 items to list then make sure we can retrieve the 2nd + */ +LONGBOW_TEST_CASE(UnknownKeys, ccnxTlvDictionary_ListGetByPosition) +{ + uint32_t listKey = SchemaEnd; + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCBuffer *a = parcBuffer_Allocate(1); + PARCBuffer *b = parcBuffer_Allocate(1); + PARCBuffer *c = parcBuffer_Allocate(1); + + ccnxTlvDictionary_PutListBuffer(data->dictionary, listKey, 1000, a); + ccnxTlvDictionary_PutListBuffer(data->dictionary, listKey, 1001, b); + ccnxTlvDictionary_PutListBuffer(data->dictionary, listKey, 1002, c); + + PARCBuffer *test = NULL; + uint32_t testkey = 0; + ccnxTlvDictionary_ListGetByPosition(data->dictionary, listKey, 1, &test, &testkey); + + assertTrue(testkey == 1001, "Wrong key, expected %u got %u", 1001, testkey); + assertTrue(test == b, "Wrong buffer, expected %p got %p", (void *) b, (void *) test); + + parcBuffer_Release(&a); + parcBuffer_Release(&b); + parcBuffer_Release(&c); +} + +/* + * Add 3 items to list then make sure we can retrieve the 2nd + */ +LONGBOW_TEST_CASE(UnknownKeys, ccnxTlvDictionary_ListGetByType) +{ + uint32_t listKey = SchemaEnd; + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCBuffer *a = parcBuffer_Allocate(1); + PARCBuffer *b = parcBuffer_Allocate(1); + PARCBuffer *c = parcBuffer_Allocate(1); + + ccnxTlvDictionary_PutListBuffer(data->dictionary, listKey, 1000, a); + ccnxTlvDictionary_PutListBuffer(data->dictionary, listKey, 1001, b); + ccnxTlvDictionary_PutListBuffer(data->dictionary, listKey, 1002, c); + + PARCBuffer *test = ccnxTlvDictionary_ListGetByType(data->dictionary, listKey, 1001); + assertTrue(test == b, "Wrong buffer, expected %p got %p", (void *) b, (void *) test); + + parcBuffer_Release(&a); + parcBuffer_Release(&b); + parcBuffer_Release(&c); +} + +LONGBOW_TEST_CASE(UnknownKeys, ccnxTlvDictionary_ListSize) +{ + uint32_t listKey = SchemaEnd; + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCBuffer *a = parcBuffer_Allocate(1); + PARCBuffer *b = parcBuffer_Allocate(1); + PARCBuffer *c = parcBuffer_Allocate(1); + + ccnxTlvDictionary_PutListBuffer(data->dictionary, listKey, 1000, a); + ccnxTlvDictionary_PutListBuffer(data->dictionary, listKey, 1001, b); + ccnxTlvDictionary_PutListBuffer(data->dictionary, listKey, 1002, c); + + size_t length = ccnxTlvDictionary_ListSize(data->dictionary, listKey); + assertTrue(length == 3, "Wrong length, expected %u got %zu", 3, length); + + parcBuffer_Release(&a); + parcBuffer_Release(&b); + parcBuffer_Release(&c); +} + +LONGBOW_TEST_CASE(UnknownKeys, ccnxTlvDictionary_ListEquals) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCBuffer *a = parcBuffer_Allocate(1); + PARCBuffer *b = parcBuffer_Allocate(1); + PARCBuffer *c = parcBuffer_Allocate(1); + + ccnxTlvDictionary_PutListBuffer(data->dictionary, 6, 1000, a); + ccnxTlvDictionary_PutListBuffer(data->dictionary, 6, 1001, b); + ccnxTlvDictionary_PutListBuffer(data->dictionary, 6, 1002, c); + + ccnxTlvDictionary_PutListBuffer(data->dictionary, 7, 1000, a); + ccnxTlvDictionary_PutListBuffer(data->dictionary, 7, 1001, b); + ccnxTlvDictionary_PutListBuffer(data->dictionary, 7, 1002, c); + + bool equals = _ccnxTlvDictionary_ListEquals(_getListHead(data->dictionary, 6), _getListHead(data->dictionary, 7)); + assertTrue(equals, "Lists should be equal"); + + parcBuffer_Release(&a); + parcBuffer_Release(&b); + parcBuffer_Release(&c); +} + + + +// ============================================================= + +LONGBOW_TEST_FIXTURE(Buffer) +{ + LONGBOW_RUN_TEST_CASE(Buffer, ccnxTlvDictionary_GetBuffer_Exists); + LONGBOW_RUN_TEST_CASE(Buffer, ccnxTlvDictionary_GetBuffer_Missing); + LONGBOW_RUN_TEST_CASE(Buffer, ccnxTlvDictionary_PutBuffer_OK); + LONGBOW_RUN_TEST_CASE(Buffer, ccnxTlvDictionary_PutBuffer_Duplicate); + LONGBOW_RUN_TEST_CASE(Buffer, ccnxTlvDictionary_IsValueBuffer_True); + LONGBOW_RUN_TEST_CASE(Buffer, ccnxTlvDictionary_IsValueBuffer_False); +} + +LONGBOW_TEST_FIXTURE_SETUP(Buffer) +{ + longBowTestCase_SetClipBoardData(testCase, _commonSetup()); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Buffer) +{ + _commonTeardown(longBowTestCase_GetClipBoardData(testCase)); + + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Buffer, ccnxTlvDictionary_GetBuffer_Exists) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCBuffer *test = ccnxTlvDictionary_GetBuffer(data->dictionary, SchemaBuffer); + assertNotNull(test, "Got null buffer from key that hsould be buffer"); +} + +LONGBOW_TEST_CASE(Buffer, ccnxTlvDictionary_GetBuffer_Missing) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + ccnxTlvDictionary_GetBuffer(data->dictionary, SchemaFree); +} + +LONGBOW_TEST_CASE(Buffer, ccnxTlvDictionary_PutBuffer_OK) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCBuffer *buffer = parcBuffer_Allocate(1); + bool success = ccnxTlvDictionary_PutBuffer(data->dictionary, SchemaFree, buffer); + assertTrue(success, "Did not put buffer in to available slot"); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Buffer, ccnxTlvDictionary_PutBuffer_Duplicate) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCBuffer *buffer = parcBuffer_Allocate(1); + bool success = ccnxTlvDictionary_PutBuffer(data->dictionary, SchemaBuffer, buffer); + assertFalse(success, "Should have failed putting duplicate"); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Buffer, ccnxTlvDictionary_IsValueBuffer_True) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + bool success = ccnxTlvDictionary_IsValueBuffer(data->dictionary, SchemaBuffer); + assertTrue(success, "Should have succeeded on a buffer key"); +} + +LONGBOW_TEST_CASE(Buffer, ccnxTlvDictionary_IsValueBuffer_False) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + bool success = ccnxTlvDictionary_IsValueBuffer(data->dictionary, SchemaInteger); + assertFalse(success, "Should have failed on a non-buffer"); +} + +// ============================================================= + +LONGBOW_TEST_FIXTURE(Integer) +{ + LONGBOW_RUN_TEST_CASE(Integer, ccnxTlvDictionary_GetInteger_Exists); + LONGBOW_RUN_TEST_CASE(Integer, ccnxTlvDictionary_GetInteger_Missing); + LONGBOW_RUN_TEST_CASE(Integer, ccnxTlvDictionary_PutInteger_OK); + LONGBOW_RUN_TEST_CASE(Integer, ccnxTlvDictionary_PutInteger_Duplicate); + LONGBOW_RUN_TEST_CASE(Integer, ccnxTlvDictionary_PutInteger_OverBuffer); + LONGBOW_RUN_TEST_CASE(Integer, ccnxTlvDictionary_IsValueInteger_True); + LONGBOW_RUN_TEST_CASE(Integer, ccnxTlvDictionary_IsValueInteger_False); +} + +LONGBOW_TEST_FIXTURE_SETUP(Integer) +{ + longBowTestCase_SetClipBoardData(testCase, _commonSetup()); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Integer) +{ + _commonTeardown(longBowTestCase_GetClipBoardData(testCase)); + + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Integer, ccnxTlvDictionary_GetInteger_Exists) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + uint64_t test = ccnxTlvDictionary_GetInteger(data->dictionary, SchemaInteger); + assertTrue(test == 42, "Got wrong integer, got %" PRIu64 " expected 42", test); +} + +LONGBOW_TEST_CASE_EXPECTS(Integer, ccnxTlvDictionary_GetInteger_Missing, .event = &LongBowTrapIllegalValue) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + ccnxTlvDictionary_GetInteger(data->dictionary, SchemaBuffer); +} + +LONGBOW_TEST_CASE(Integer, ccnxTlvDictionary_PutInteger_OK) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + bool success = ccnxTlvDictionary_PutInteger(data->dictionary, SchemaFree, 69); + assertTrue(success, "Did not put integer in to available slot"); +} + +LONGBOW_TEST_CASE(Integer, ccnxTlvDictionary_PutInteger_Duplicate) +{ + // we allow replacing integer + TestData *data = longBowTestCase_GetClipBoardData(testCase); + bool success = ccnxTlvDictionary_PutInteger(data->dictionary, SchemaInteger, 69); + assertTrue(success, "Should have succeeded putting duplicate"); +} + +LONGBOW_TEST_CASE(Integer, ccnxTlvDictionary_PutInteger_OverBuffer) +{ + // This will fail, cannot change a buffer to an integer + TestData *data = longBowTestCase_GetClipBoardData(testCase); + bool success = ccnxTlvDictionary_PutInteger(data->dictionary, SchemaBuffer, 69); + assertFalse(success, "Should not be able to change a buffer to an integer"); +} + +LONGBOW_TEST_CASE(Integer, ccnxTlvDictionary_IsValueInteger_True) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + bool success = ccnxTlvDictionary_IsValueInteger(data->dictionary, SchemaInteger); + assertTrue(success, "Should have succeeded on a integer key"); +} + +LONGBOW_TEST_CASE(Integer, ccnxTlvDictionary_IsValueInteger_False) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + bool success = ccnxTlvDictionary_IsValueInteger(data->dictionary, SchemaBuffer); + assertFalse(success, "Should have failed on a non-integer"); +} + +// ============================================================= + +LONGBOW_TEST_FIXTURE(IoVec) +{ + LONGBOW_RUN_TEST_CASE(IoVec, ccnxTlvDictionary_GetIoVec_Exists); + LONGBOW_RUN_TEST_CASE(IoVec, ccnxTlvDictionary_GetIoVec_Missing); + LONGBOW_RUN_TEST_CASE(IoVec, ccnxTlvDictionary_PutIoVec_OK); + LONGBOW_RUN_TEST_CASE(IoVec, ccnxTlvDictionary_PutIoVec_Duplicate); + LONGBOW_RUN_TEST_CASE(IoVec, ccnxTlvDictionary_IsValueIoVec_True); + LONGBOW_RUN_TEST_CASE(IoVec, ccnxTlvDictionary_IsValueIoVec_False); +} + +LONGBOW_TEST_FIXTURE_SETUP(IoVec) +{ + longBowTestCase_SetClipBoardData(testCase, _commonSetup()); + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(IoVec) +{ + _commonTeardown(longBowTestCase_GetClipBoardData(testCase)); + + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(IoVec, ccnxTlvDictionary_GetIoVec_Exists) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + CCNxCodecNetworkBufferIoVec *test = ccnxTlvDictionary_GetIoVec(data->dictionary, SchemaIoVec); + assertNotNull(test, "Got null buffer from key that hsould be buffer"); +} + +LONGBOW_TEST_CASE(IoVec, ccnxTlvDictionary_GetIoVec_Missing) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + ccnxTlvDictionary_GetIoVec(data->dictionary, SchemaFree); +} + +LONGBOW_TEST_CASE(IoVec, ccnxTlvDictionary_PutIoVec_OK) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + CCNxCodecNetworkBufferIoVec *vec = createIoVec(); + bool success = ccnxTlvDictionary_PutIoVec(data->dictionary, SchemaFree, vec); + assertTrue(success, "Did not put vec in to available slot"); + ccnxCodecNetworkBufferIoVec_Release(&vec); +} + +LONGBOW_TEST_CASE(IoVec, ccnxTlvDictionary_PutIoVec_Duplicate) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + CCNxCodecNetworkBufferIoVec *vec = createIoVec(); + bool success = ccnxTlvDictionary_PutIoVec(data->dictionary, SchemaIoVec, vec); + assertFalse(success, "Should have failed putting duplicate"); + ccnxCodecNetworkBufferIoVec_Release(&vec); +} + +LONGBOW_TEST_CASE(IoVec, ccnxTlvDictionary_IsValueIoVec_True) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + bool success = ccnxTlvDictionary_IsValueIoVec(data->dictionary, SchemaIoVec); + assertTrue(success, "Should have succeeded on a vec key"); +} + +LONGBOW_TEST_CASE(IoVec, ccnxTlvDictionary_IsValueIoVec_False) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + bool success = ccnxTlvDictionary_IsValueIoVec(data->dictionary, SchemaInteger); + assertFalse(success, "Should have failed on a non-vec"); +} + +// ============================================================= + +LONGBOW_TEST_FIXTURE(Json) +{ + LONGBOW_RUN_TEST_CASE(Json, ccnxTlvDictionary_GetJson_Exists); + LONGBOW_RUN_TEST_CASE(Json, ccnxTlvDictionary_GetJson_Missing); + LONGBOW_RUN_TEST_CASE(Json, ccnxTlvDictionary_PutJson_OK); + LONGBOW_RUN_TEST_CASE(Json, ccnxTlvDictionary_PutJson_Duplicate); + LONGBOW_RUN_TEST_CASE(Json, ccnxTlvDictionary_IsValueJson_True); + LONGBOW_RUN_TEST_CASE(Json, ccnxTlvDictionary_IsValueJson_False); +} + +LONGBOW_TEST_FIXTURE_SETUP(Json) +{ + longBowTestCase_SetClipBoardData(testCase, _commonSetup()); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Json) +{ + _commonTeardown(longBowTestCase_GetClipBoardData(testCase)); + + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Json, ccnxTlvDictionary_GetJson_Exists) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCJSON *test = ccnxTlvDictionary_GetJson(data->dictionary, SchemaJson); + assertNotNull(test, "Got null json from key that should be json"); +} + +LONGBOW_TEST_CASE(Json, ccnxTlvDictionary_GetJson_Missing) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCJSON *test = ccnxTlvDictionary_GetJson(data->dictionary, SchemaFree); + assertNull(test, "Should have gotten null for non-json key"); +} + +LONGBOW_TEST_CASE(Json, ccnxTlvDictionary_PutJson_OK) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCJSON *json = parcJSON_Create(); + bool success = ccnxTlvDictionary_PutJson(data->dictionary, SchemaFree, json); + assertTrue(success, "Did not put buffer in to available slot"); + parcJSON_Release(&json); +} + +LONGBOW_TEST_CASE(Json, ccnxTlvDictionary_PutJson_Duplicate) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCJSON *json = parcJSON_Create(); + bool success = ccnxTlvDictionary_PutJson(data->dictionary, SchemaJson, json); + assertFalse(success, "Should have failed putting duplicate"); + parcJSON_Release(&json); +} + +LONGBOW_TEST_CASE(Json, ccnxTlvDictionary_IsValueJson_True) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + bool success = ccnxTlvDictionary_IsValueJson(data->dictionary, SchemaJson); + assertTrue(success, "Should have succeeded on a json key"); +} + +LONGBOW_TEST_CASE(Json, ccnxTlvDictionary_IsValueJson_False) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + bool success = ccnxTlvDictionary_IsValueJson(data->dictionary, SchemaInteger); + assertFalse(success, "Should have failed on a non-json"); +} + +// ============================================================= + +LONGBOW_TEST_FIXTURE(Name) +{ + LONGBOW_RUN_TEST_CASE(Name, ccnxTlvDictionary_GetName_Exists); + LONGBOW_RUN_TEST_CASE(Name, ccnxTlvDictionary_GetName_Missing); + LONGBOW_RUN_TEST_CASE(Name, ccnxTlvDictionary_PutName_OK); + LONGBOW_RUN_TEST_CASE(Name, ccnxTlvDictionary_PutName_Duplicate); + LONGBOW_RUN_TEST_CASE(Name, ccnxTlvDictionary_IsValueName_True); + LONGBOW_RUN_TEST_CASE(Name, ccnxTlvDictionary_IsValueName_False); +} + +LONGBOW_TEST_FIXTURE_SETUP(Name) +{ + longBowTestCase_SetClipBoardData(testCase, _commonSetup()); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Name) +{ + _commonTeardown(longBowTestCase_GetClipBoardData(testCase)); + + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Name, ccnxTlvDictionary_GetName_Exists) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + CCNxName *test = ccnxTlvDictionary_GetName(data->dictionary, SchemaName); + assertNotNull(test, "Got null json from key that should be name"); +} + +LONGBOW_TEST_CASE(Name, ccnxTlvDictionary_GetName_Missing) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + CCNxName *test = ccnxTlvDictionary_GetName(data->dictionary, SchemaFree); + assertNull(test, "Should have gotten null for non-name key"); +} + +LONGBOW_TEST_CASE(Name, ccnxTlvDictionary_PutName_OK) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + CCNxName *name = ccnxName_Create(); + bool success = ccnxTlvDictionary_PutName(data->dictionary, SchemaFree, name); + assertTrue(success, "Did not put name in to available slot"); + ccnxName_Release(&name); +} + +LONGBOW_TEST_CASE(Name, ccnxTlvDictionary_PutName_Duplicate) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + CCNxName *name = ccnxName_Create(); + bool success = ccnxTlvDictionary_PutName(data->dictionary, SchemaName, name); + assertFalse(success, "Should have failed putting duplicate"); + ccnxName_Release(&name); +} + +LONGBOW_TEST_CASE(Name, ccnxTlvDictionary_IsValueName_True) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + bool success = ccnxTlvDictionary_IsValueName(data->dictionary, SchemaName); + assertTrue(success, "Should have succeeded on a json key"); +} + +LONGBOW_TEST_CASE(Name, ccnxTlvDictionary_IsValueName_False) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + bool success = ccnxTlvDictionary_IsValueName(data->dictionary, SchemaInteger); + assertFalse(success, "Should have failed on a non-json"); +} + +// ============================================================= + +LONGBOW_TEST_FIXTURE(Local) +{ + LONGBOW_RUN_TEST_CASE(Local, _rtaTlvEntry_Equals_Unset); + LONGBOW_RUN_TEST_CASE(Local, _rtaTlvEntry_Equals_Buffer); + LONGBOW_RUN_TEST_CASE(Local, _rtaTlvEntry_Equals_Integer); + LONGBOW_RUN_TEST_CASE(Local, _rtaTlvEntry_Equals_IoVec); + LONGBOW_RUN_TEST_CASE(Local, _rtaTlvEntry_Equals_Json); + LONGBOW_RUN_TEST_CASE(Local, _rtaTlvEntry_Equals_Name); +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +static void +assertRtaTlvEntryEquals(int length, _CCNxTlvDictionaryEntry array[length]) +{ + assertTrue(length == 5, "Must provide 5 array elements"); + assertEqualsContract((bool (*)(void *, void *))_ccnxTlvDictionaryEntry_Equals, &array[0], &array[1], &array[2], &array[3], &array[4], NULL); +} + +LONGBOW_TEST_CASE(Local, _rtaTlvEntry_Equals_Unset) +{ + _CCNxTlvDictionaryEntry array[5]; + memset(&array, 0, sizeof(array)); + + array[3].entryType = ENTRY_JSON; + array[4].entryType = ENTRY_BUFFER; + assertRtaTlvEntryEquals(5, array); +} + +LONGBOW_TEST_CASE(Local, _rtaTlvEntry_Equals_Buffer) +{ + char apple[] = "apple"; + char bananna[] = "bannana"; + + _CCNxTlvDictionaryEntry array[5]; + memset(&array, 0, sizeof(array)); + + for (int i = 0; i < 3; i++) { + array[i].entryType = ENTRY_BUFFER; + array[i]._entry.buffer = parcBuffer_Flip(parcBuffer_PutArray(parcBuffer_Allocate(20), sizeof(apple), (uint8_t *) apple)); + } + + array[3].entryType = ENTRY_JSON; + array[4].entryType = ENTRY_BUFFER; + array[4]._entry.buffer = parcBuffer_Flip(parcBuffer_PutArray(parcBuffer_Allocate(20), sizeof(bananna), (uint8_t *) bananna)); + assertRtaTlvEntryEquals(5, array); + + for (int i = 0; i < 5; i++) { + if (array[i]._entry.buffer) { + parcBuffer_Release(&array[i]._entry.buffer); + } + } +} + +LONGBOW_TEST_CASE(Local, _rtaTlvEntry_Equals_Integer) +{ + _CCNxTlvDictionaryEntry array[5]; + memset(&array, 0, sizeof(array)); + + for (int i = 0; i < 3; i++) { + array[i].entryType = ENTRY_INTEGER; + array[i]._entry.integer = 13; + } + + array[3].entryType = ENTRY_JSON; + array[4].entryType = ENTRY_INTEGER; + array[4]._entry.integer = 99; + + assertRtaTlvEntryEquals(5, array); +} + +LONGBOW_TEST_CASE(Local, _rtaTlvEntry_Equals_IoVec) +{ + CCNxCodecNetworkBuffer *netbuff = ccnxCodecNetworkBuffer_Create(&ParcMemoryMemoryBlock, NULL); + ccnxCodecNetworkBuffer_PutUint8(netbuff, 0); + CCNxCodecNetworkBufferIoVec *unequal = ccnxCodecNetworkBuffer_CreateIoVec(netbuff); + ccnxCodecNetworkBuffer_Release(&netbuff); + + _CCNxTlvDictionaryEntry array[5]; + memset(&array, 0, sizeof(array)); + + for (int i = 0; i < 3; i++) { + array[i].entryType = ENTRY_IOVEC; + array[i]._entry.vec = createIoVec(); + } + + array[3].entryType = ENTRY_JSON; + array[4].entryType = ENTRY_IOVEC; + array[4]._entry.vec = unequal; + assertRtaTlvEntryEquals(5, array); + + for (int i = 0; i < 5; i++) { + if (array[i]._entry.vec) { + ccnxCodecNetworkBufferIoVec_Release(&array[i]._entry.vec); + } + } +} + +LONGBOW_TEST_CASE(Local, _rtaTlvEntry_Equals_Json) +{ + char apple[] = "{\"apple\": 0}"; + char bananna[] = "{\"bannana\": 1}"; + + _CCNxTlvDictionaryEntry array[5]; + memset(&array, 0, sizeof(array)); + + for (int i = 0; i < 3; i++) { + array[i].entryType = ENTRY_JSON; + array[i]._entry.json = parcJSON_ParseString(apple); + } + + array[3].entryType = ENTRY_JSON; + array[4].entryType = ENTRY_JSON; + array[4]._entry.json = parcJSON_ParseString(bananna); + assertRtaTlvEntryEquals(5, array); + + for (int i = 0; i < 5; i++) { + if (array[i]._entry.json) { + parcJSON_Release(&array[i]._entry.json); + } + } +} + +LONGBOW_TEST_CASE(Local, _rtaTlvEntry_Equals_Name) +{ + char apple[] = "lci:/apple"; + char bananna[] = "lci:/bannana"; + + _CCNxTlvDictionaryEntry array[5]; + memset(&array, 0, sizeof(array)); + + for (int i = 0; i < 3; i++) { + array[i].entryType = ENTRY_NAME; + array[i]._entry.name = ccnxName_CreateFromCString(apple); + } + + array[3].entryType = ENTRY_JSON; + array[4].entryType = ENTRY_NAME; + array[4]._entry.name = ccnxName_CreateFromCString(bananna); + assertRtaTlvEntryEquals(5, array); + + for (int i = 0; i < 5; i++) { + if (array[i]._entry.name) { + ccnxName_Release(&array[i]._entry.name); + } + } +} + +// ============================================================= + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(rta_TlvDictionary); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-common/ccnx/common/internal/test/test_ccnx_ValidationFacade b/libccnx-common/ccnx/common/internal/test/test_ccnx_ValidationFacade Binary files differnew file mode 100755 index 00000000..39a27830 --- /dev/null +++ b/libccnx-common/ccnx/common/internal/test/test_ccnx_ValidationFacade diff --git a/libccnx-common/ccnx/common/internal/test/test_ccnx_ValidationFacadeV1.c b/libccnx-common/ccnx/common/internal/test/test_ccnx_ValidationFacadeV1.c new file mode 100755 index 00000000..fe3ead29 --- /dev/null +++ b/libccnx-common/ccnx/common/internal/test/test_ccnx_ValidationFacadeV1.c @@ -0,0 +1,444 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Setter tests use ground truth by examining the dictionary. + * Getter tests use the setters to set values. + * + */ + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../ccnx_ValidationFacadeV1.c" + +#include <stdio.h> +#include <inttypes.h> + +#include <parc/algol/parc_SafeMemory.h> + +#include <LongBow/unit-test.h> + +//#include "../validation/test/testrig_validation.c" + +LONGBOW_TEST_RUNNER(ccnx_ValidationFacadeV1) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Setters); + LONGBOW_RUN_TEST_FIXTURE(Getters); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(ccnx_ValidationFacadeV1) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(ccnx_ValidationFacadeV1) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Setters) +{ + LONGBOW_RUN_TEST_CASE(Setters, ccnxValidationFacadeV1_SetKeyId); + LONGBOW_RUN_TEST_CASE(Setters, ccnxValidationFacadeV1_SetKeyName); + LONGBOW_RUN_TEST_CASE(Setters, ccnxValidationFacadeV1_SetPublicKey); + LONGBOW_RUN_TEST_CASE(Setters, ccnxValidationFacadeV1_SetCertificate); + LONGBOW_RUN_TEST_CASE(Setters, ccnxValidationFacadeV1_SetPayload); + LONGBOW_RUN_TEST_CASE(Setters, ccnxValidationFacadeV1_SetCryptoSuite); + LONGBOW_RUN_TEST_CASE(Setters, ccnxValidationFacadeV1_SetSigningTime); +} + +LONGBOW_TEST_FIXTURE_SETUP(Setters) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Setters) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Setters, ccnxValidationFacadeV1_SetKeyId) +{ + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + PARCBuffer *keyid = parcBuffer_Wrap((uint8_t []) { 1, 2, 3, 4, 5 }, 5, 0, 5); + bool success = ccnxValidationFacadeV1_SetKeyId(dictionary, keyid); + assertTrue(success, "Failed to set keyid"); + + PARCBuffer *test = ccnxTlvDictionary_GetBuffer(dictionary, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_KEYID); + assertTrue(parcBuffer_Equals(test, keyid), "Buffer mismatch") + { + printf("Expected\n"); + parcBuffer_Display(keyid, 3); + printf("Got\n"); + parcBuffer_Display(test, 3); + } + + parcBuffer_Release(&keyid); + ccnxTlvDictionary_Release(&dictionary); +} + + +LONGBOW_TEST_CASE(Setters, ccnxValidationFacadeV1_SetKeyName) +{ + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + PARCBuffer *keyid = parcBuffer_Wrap((uint8_t []) { 1, 2, 3, 4, 5 }, 5, 0, 5); + PARCBuffer *hash = parcBuffer_Wrap((uint8_t []) { 11, 12, 13, 14 }, 4, 0, 4); + CCNxName *name = ccnxName_CreateFromCString("lci:/foo"); + CCNxLink *link = ccnxLink_Create(name, keyid, hash); + + bool success = ccnxValidationFacadeV1_SetKeyName(dictionary, link); + assertTrue(success, "Failed to set keyname"); + + CCNxName *testName = ccnxTlvDictionary_GetName(dictionary, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_KEYNAME_NAME); + assertTrue(ccnxName_Equals(testName, name), "Wrong name") + { + printf("Expected\n"); + ccnxName_Display(name, 3); + printf("Got\n"); + ccnxName_Display(testName, 3); + } + + PARCBuffer *testKeyid = ccnxTlvDictionary_GetBuffer(dictionary, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_KEYNAME_KEYID); + assertTrue(parcBuffer_Equals(testKeyid, keyid), "Wrong keyid") + { + printf("Expected\n"); + parcBuffer_Display(keyid, 3); + printf("Got\n"); + parcBuffer_Display(testKeyid, 3); + } + + PARCBuffer *testHash = ccnxTlvDictionary_GetBuffer(dictionary, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_KEYNAME_OBJHASH); + assertTrue(parcBuffer_Equals(testHash, hash), "Wrong hash") + { + printf("Expected\n"); + parcBuffer_Display(hash, 3); + printf("Got\n"); + parcBuffer_Display(testHash, 3); + } + + parcBuffer_Release(&hash); + parcBuffer_Release(&keyid); + ccnxName_Release(&name); + ccnxLink_Release(&link); + ccnxTlvDictionary_Release(&dictionary); +} + +LONGBOW_TEST_CASE(Setters, ccnxValidationFacadeV1_SetPublicKey) +{ + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + PARCBuffer *key = parcBuffer_Wrap((uint8_t []) { 1, 2, 3, 4, 5 }, 5, 0, 5); + bool success = ccnxValidationFacadeV1_SetPublicKey(dictionary, key); + assertTrue(success, "Failed to set keyid"); + + PARCBuffer *test = ccnxTlvDictionary_GetBuffer(dictionary, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_KEY); + assertTrue(parcBuffer_Equals(test, key), "Buffer mismatch") + { + printf("Expected\n"); + parcBuffer_Display(key, 3); + printf("Got\n"); + parcBuffer_Display(test, 3); + } + + parcBuffer_Release(&key); + ccnxTlvDictionary_Release(&dictionary); +} + +LONGBOW_TEST_CASE(Setters, ccnxValidationFacadeV1_SetCertificate) +{ + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + PARCBuffer *cert = parcBuffer_Wrap((uint8_t []) { 1, 2, 3, 4, 5 }, 5, 0, 5); + bool success = ccnxValidationFacadeV1_SetCertificate(dictionary, cert); + assertTrue(success, "Failed to set keyid"); + + PARCBuffer *test = ccnxTlvDictionary_GetBuffer(dictionary, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_CERT); + assertTrue(parcBuffer_Equals(test, cert), "Buffer mismatch") + { + printf("Expected\n"); + parcBuffer_Display(cert, 3); + printf("Got\n"); + parcBuffer_Display(test, 3); + } + + parcBuffer_Release(&cert); + ccnxTlvDictionary_Release(&dictionary); +} + +LONGBOW_TEST_CASE(Setters, ccnxValidationFacadeV1_SetPayload) +{ + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + PARCBuffer *payload = parcBuffer_Wrap((uint8_t []) { 1, 2, 3, 4, 5 }, 5, 0, 5); + bool success = ccnxValidationFacadeV1_SetPayload(dictionary, payload); + assertTrue(success, "Failed to set keyid"); + + PARCBuffer *test = ccnxTlvDictionary_GetBuffer(dictionary, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_PAYLOAD); + assertTrue(parcBuffer_Equals(test, payload), "Buffer mismatch") + { + printf("Expected\n"); + parcBuffer_Display(payload, 3); + printf("Got\n"); + parcBuffer_Display(test, 3); + } + + parcBuffer_Release(&payload); + ccnxTlvDictionary_Release(&dictionary); +} + +LONGBOW_TEST_CASE(Setters, ccnxValidationFacadeV1_SetCryptoSuite) +{ + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + PARCCryptoSuite suite = PARCCryptoSuite_RSA_SHA256; + bool success = ccnxValidationFacadeV1_SetCryptoSuite(dictionary, suite); + assertTrue(success, "Failed to set keyid"); + + bool hasCryptoSuite = ccnxTlvDictionary_IsValueInteger(dictionary, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_CRYPTO_SUITE); + assertTrue(hasCryptoSuite, "Dictionary does not have a crypto suite value in it"); + + PARCCryptoSuite test = (PARCCryptoSuite) ccnxTlvDictionary_GetInteger(dictionary, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_CRYPTO_SUITE); + assertTrue(test == suite, "Wrong suite, expected %d got %d", suite, test); + + ccnxTlvDictionary_Release(&dictionary); +} + +LONGBOW_TEST_CASE(Setters, ccnxValidationFacadeV1_SetSigningTime) +{ + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + uint64_t signingTime = 0x0102030405060708ULL; + + bool success = ccnxValidationFacadeV1_SetSigningTime(dictionary, signingTime); + assertTrue(success, "Failed to set signing time"); + + bool hasSigningTime = ccnxTlvDictionary_IsValueInteger(dictionary, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_SIGNTIME); + assertTrue(hasSigningTime, "Dictionary does not have a signing time value in it"); + + uint64_t test = ccnxTlvDictionary_GetInteger(dictionary, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_SIGNTIME); + assertTrue(test == signingTime, "Wrong signing time, expected %" PRIx64 " got %" PRIx64, signingTime, test); + + ccnxTlvDictionary_Release(&dictionary); +} + +// ============================================================= + +LONGBOW_TEST_FIXTURE(Getters) +{ + LONGBOW_RUN_TEST_CASE(Setters, ccnxValidationFacadeV1_GetKeyId); + LONGBOW_RUN_TEST_CASE(Setters, ccnxValidationFacadeV1_GetKeyName); + LONGBOW_RUN_TEST_CASE(Setters, ccnxValidationFacadeV1_GetPublicKey); + LONGBOW_RUN_TEST_CASE(Setters, ccnxValidationFacadeV1_GetCertificate); + LONGBOW_RUN_TEST_CASE(Setters, ccnxValidationFacadeV1_GetPayload); + LONGBOW_RUN_TEST_CASE(Setters, ccnxValidationFacadeV1_HasCryptoSuite_True); + LONGBOW_RUN_TEST_CASE(Setters, ccnxValidationFacadeV1_HasCryptoSuite_False); + LONGBOW_RUN_TEST_CASE(Setters, ccnxValidationFacadeV1_GetCryptoSuite); + LONGBOW_RUN_TEST_CASE(Setters, ccnxValidationFacadeV1_HasSigningTime_True); + LONGBOW_RUN_TEST_CASE(Setters, ccnxValidationFacadeV1_HasSigningTime_False); + LONGBOW_RUN_TEST_CASE(Setters, ccnxValidationFacadeV1_GetSigningTime); +} + +LONGBOW_TEST_FIXTURE_SETUP(Getters) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Getters) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Setters, ccnxValidationFacadeV1_GetKeyId) +{ + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + PARCBuffer *keyid = parcBuffer_Wrap((uint8_t []) { 1, 2, 3, 4, 5 }, 5, 0, 5); + ccnxValidationFacadeV1_SetKeyId(dictionary, keyid); + + PARCBuffer *test = ccnxValidationFacadeV1_GetKeyId(dictionary); + assertTrue(parcBuffer_Equals(test, keyid), "Buffer mismatch") + { + printf("Expected\n"); + parcBuffer_Display(keyid, 3); + printf("Got\n"); + parcBuffer_Display(test, 3); + } + + parcBuffer_Release(&keyid); + ccnxTlvDictionary_Release(&dictionary); +} + +LONGBOW_TEST_CASE(Setters, ccnxValidationFacadeV1_GetKeyName) +{ + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + PARCBuffer *keyid = parcBuffer_Wrap((uint8_t []) { 1, 2, 3, 4, 5 }, 5, 0, 5); + PARCBuffer *hash = parcBuffer_Wrap((uint8_t []) { 11, 12, 13, 14 }, 4, 0, 4); + CCNxName *name = ccnxName_CreateFromCString("lci:/foo"); + CCNxLink *link = ccnxLink_Create(name, keyid, hash); + + ccnxValidationFacadeV1_SetKeyName(dictionary, link); + + CCNxLink *testLink = ccnxValidationFacadeV1_GetKeyName(dictionary); + assertTrue(ccnxLink_Equals(testLink, link), "Wrong link"); + // no ccnxLink_Dispay, so cannot easily display items + + ccnxLink_Release(&testLink); + parcBuffer_Release(&hash); + parcBuffer_Release(&keyid); + ccnxName_Release(&name); + ccnxLink_Release(&link); + ccnxTlvDictionary_Release(&dictionary); +} + +LONGBOW_TEST_CASE(Setters, ccnxValidationFacadeV1_GetPublicKey) +{ + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + PARCBuffer *key = parcBuffer_Wrap((uint8_t []) { 1, 2, 3, 4, 5 }, 5, 0, 5); + ccnxValidationFacadeV1_SetPublicKey(dictionary, key); + + PARCBuffer *test = ccnxValidationFacadeV1_GetPublicKey(dictionary); + assertTrue(parcBuffer_Equals(test, key), "Buffer mismatch") + { + printf("Expected\n"); + parcBuffer_Display(key, 3); + printf("Got\n"); + parcBuffer_Display(test, 3); + } + + parcBuffer_Release(&key); + ccnxTlvDictionary_Release(&dictionary); +} + +LONGBOW_TEST_CASE(Setters, ccnxValidationFacadeV1_GetCertificate) +{ + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + PARCBuffer *cert = parcBuffer_Wrap((uint8_t []) { 1, 2, 3, 4, 5 }, 5, 0, 5); + ccnxValidationFacadeV1_SetCertificate(dictionary, cert); + + PARCBuffer *test = ccnxValidationFacadeV1_GetCertificate(dictionary); + assertTrue(parcBuffer_Equals(test, cert), "Buffer mismatch") + { + printf("Expected\n"); + parcBuffer_Display(cert, 3); + printf("Got\n"); + parcBuffer_Display(test, 3); + } + + parcBuffer_Release(&cert); + ccnxTlvDictionary_Release(&dictionary); +} + +LONGBOW_TEST_CASE(Setters, ccnxValidationFacadeV1_GetPayload) +{ + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + PARCBuffer *payload = parcBuffer_Wrap((uint8_t []) { 1, 2, 3, 4, 5 }, 5, 0, 5); + ccnxValidationFacadeV1_SetPayload(dictionary, payload); + + PARCBuffer *test = ccnxValidationFacadeV1_GetPayload(dictionary); + assertTrue(parcBuffer_Equals(test, payload), "Buffer mismatch") + { + printf("Expected\n"); + parcBuffer_Display(payload, 3); + printf("Got\n"); + parcBuffer_Display(test, 3); + } + + parcBuffer_Release(&payload); + ccnxTlvDictionary_Release(&dictionary); +} + +LONGBOW_TEST_CASE(Setters, ccnxValidationFacadeV1_HasCryptoSuite_True) +{ + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + PARCCryptoSuite suite = PARCCryptoSuite_RSA_SHA256; + ccnxValidationFacadeV1_SetCryptoSuite(dictionary, suite); + + bool hasCryptoSuite = ccnxValidationFacadeV1_HasCryptoSuite(dictionary); + assertTrue(hasCryptoSuite, "Dictionary does not have a crypto suite value in it"); + ccnxTlvDictionary_Release(&dictionary); +} + +LONGBOW_TEST_CASE(Setters, ccnxValidationFacadeV1_HasCryptoSuite_False) +{ + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + + bool hasCryptoSuite = ccnxValidationFacadeV1_HasCryptoSuite(dictionary); + assertFalse(hasCryptoSuite, "Dictionary says it has a crypto suite when none was set"); + ccnxTlvDictionary_Release(&dictionary); +} + +LONGBOW_TEST_CASE(Setters, ccnxValidationFacadeV1_GetCryptoSuite) +{ + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + PARCCryptoSuite suite = PARCCryptoSuite_RSA_SHA256; + ccnxValidationFacadeV1_SetCryptoSuite(dictionary, suite); + + PARCCryptoSuite test = ccnxValidationFacadeV1_GetCryptoSuite(dictionary); + assertTrue(test == suite, "Wrong crypto suite, expected %d got %d", suite, test); + ccnxTlvDictionary_Release(&dictionary); +} + +LONGBOW_TEST_CASE(Setters, ccnxValidationFacadeV1_HasSigningTime_True) +{ + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + uint64_t signingTime = 0x0102030405060708ULL; + ccnxValidationFacadeV1_SetSigningTime(dictionary, signingTime); + + bool hasSigningTime = ccnxValidationFacadeV1_HasSigningTime(dictionary); + assertTrue(hasSigningTime, "Dictionary does not have a signing time value in it"); + ccnxTlvDictionary_Release(&dictionary); +} + +LONGBOW_TEST_CASE(Setters, ccnxValidationFacadeV1_HasSigningTime_False) +{ + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + + bool hasSigningTime = ccnxValidationFacadeV1_HasSigningTime(dictionary); + assertFalse(hasSigningTime, "Dictionary says it has a signing time when none was set"); + ccnxTlvDictionary_Release(&dictionary); +} + +LONGBOW_TEST_CASE(Setters, ccnxValidationFacadeV1_GetSigningTime) +{ + CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + uint64_t signingTime = 0x0102030405060708ULL; + ccnxValidationFacadeV1_SetSigningTime(dictionary, signingTime); + + uint64_t test = ccnxValidationFacadeV1_GetSigningTime(dictionary); + assertTrue(test == signingTime, "Wrong signing time, expected %" PRIx64 " got %" PRIx64, signingTime, test); + ccnxTlvDictionary_Release(&dictionary); +} + + +// ============================================================= + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(ccnx_ValidationFacadeV1); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-common/ccnx/common/internal/test/test_ccnx_WireFormatFacadeV1.c b/libccnx-common/ccnx/common/internal/test/test_ccnx_WireFormatFacadeV1.c new file mode 100755 index 00000000..194ef02c --- /dev/null +++ b/libccnx-common/ccnx/common/internal/test/test_ccnx_WireFormatFacadeV1.c @@ -0,0 +1,594 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../ccnx_WireFormatFacadeV1.c" + +#include <parc/algol/parc_SafeMemory.h> + +#include <LongBow/unit-test.h> + +#include <ccnx/common/ccnx_ContentObject.h> +#include <ccnx/common/ccnx_WireFormatMessage.h> +#include <ccnx/common/codec/ccnxCodec_TlvPacket.h> + +#include <ccnx/common/codec/schema_v1/testdata/v1_interest_nameA.h> +#include <ccnx/common/codec/schema_v1/testdata/v1_content_nameA_crc32c.h> +#include <ccnx/common/codec/schema_v1/testdata/v1_cpi_add_route.h> + +LONGBOW_TEST_RUNNER(ccnx_WireFormatFacade) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(SchemaV1); + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(ccnx_WireFormatFacade) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(ccnx_WireFormatFacade) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +/* + * Small size allocator for creating a network buffer + */ +static size_t +allocator(void *userarg, size_t bytes, void **output) +{ + const size_t allocationSize = *(size_t *) userarg; + void *allocated = parcMemory_Allocate(allocationSize); + *output = allocated; + return allocationSize; +} + +/* + * deallocator for network buffer + */ +static void +deallocator(void *userarg, void **memoryPtr) +{ + parcMemory_Deallocate(memoryPtr); +} + +static const CCNxCodecNetworkBufferMemoryBlockFunctions memory = { .allocator = allocator, .deallocator = deallocator }; + +/* + * Create a network buffer that looks like this. The actual number of iovecs might + * be a little different, but the digest area will span several iovec. + * + * +-----------+-----------+-----------+-----------+-----------+ + * iov[0] iov[1] iov[2] iov[3] + * +-----------+-----------+-----------+-----------+-----------+ + * ^ ^ + * | | + * start end + */ +static CCNxCodecNetworkBufferIoVec * +createNetworkBufferIoVec(size_t allocationSize, size_t padlen, uint8_t pad[padlen], size_t datalen, uint8_t data[datalen]) +{ + CCNxCodecNetworkBuffer *netbuff = ccnxCodecNetworkBuffer_Create(&memory, &allocationSize); + // build the network buffer + ccnxCodecNetworkBuffer_PutArray(netbuff, padlen, pad); + ccnxCodecNetworkBuffer_PutArray(netbuff, datalen, data); + ccnxCodecNetworkBuffer_PutArray(netbuff, padlen, pad); + + CCNxCodecNetworkBufferIoVec *vec = ccnxCodecNetworkBuffer_CreateIoVec(netbuff); + ccnxCodecNetworkBuffer_Release(&netbuff); + return vec; +} + +// ======================================================================= + +LONGBOW_TEST_FIXTURE(SchemaV1) +{ + LONGBOW_RUN_TEST_CASE(SchemaV1, ccnxWireFormatFacadeV1_FromContentObjectPacketType); + LONGBOW_RUN_TEST_CASE(SchemaV1, ccnxWireFormatFacadeV1_FromControlPacketType); + LONGBOW_RUN_TEST_CASE(SchemaV1, ccnxWireFormatFacadeV1_FromInterestPacketType); + LONGBOW_RUN_TEST_CASE(SchemaV1, ccnxWireFormatFacadeV1_FromInterestReturnPacketType); + LONGBOW_RUN_TEST_CASE(SchemaV1, ccnxWireFormatFacadeV1_Get); + LONGBOW_RUN_TEST_CASE(SchemaV1, ccnxWireFormatFacadeV1_Put); + LONGBOW_RUN_TEST_CASE(SchemaV1, ccnxWireFormatFacadeV1_WriteToFile); + LONGBOW_RUN_TEST_CASE(SchemaV1, ccnxWireFormatFacadeV1_FromInterestPacketTypeIoVec); + LONGBOW_RUN_TEST_CASE(SchemaV1, ccnxWireFormatFacadeV1_GetIoVec); + LONGBOW_RUN_TEST_CASE(SchemaV1, ccnxWireFormatFacadeV1_SetHopLimit); + + LONGBOW_RUN_TEST_CASE(SchemaV1, ccnxWireFormatFacadeV1_SetProtectedRegionStart); + LONGBOW_RUN_TEST_CASE(SchemaV1, ccnxWireFormatFacadeV1_SetProtectedRegionLength); + LONGBOW_RUN_TEST_CASE(SchemaV1, ccnxWireFormatFacadeV1_HashProtectedRegion_Buffer); + LONGBOW_RUN_TEST_CASE(SchemaV1, ccnxWireFormatFacadeV1_HashProtectedRegion_IoVec); + LONGBOW_RUN_TEST_CASE(SchemaV1, ccnxWireFormatFacadeV1_ComputeContentObjectHash); + + LONGBOW_RUN_TEST_CASE(SchemaV1, ccnxWireFormatFacadeV1_Create_Interest); + LONGBOW_RUN_TEST_CASE(SchemaV1, ccnxWireFormatFacadeV1_Create_ContentObject); + LONGBOW_RUN_TEST_CASE(SchemaV1, ccnxWireFormatFacadeV1_Create_Control); + LONGBOW_RUN_TEST_CASE(SchemaV1, ccnxWireFormatFacadeV1_Create_InterestReturn); + LONGBOW_RUN_TEST_CASE(SchemaV1, ccnxWireFormatFacadeV1_Create_UnknownPacketType); +} + +LONGBOW_TEST_FIXTURE_SETUP(SchemaV1) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(SchemaV1) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(SchemaV1, ccnxWireFormatFacadeV1_FromContentObjectPacketType) +{ + PARCBuffer *buffer = parcBuffer_Allocate(1); + CCNxTlvDictionary *wireformat = _ccnxWireFormatFacadeV1_FromContentObjectPacketType(buffer); + assertNotNull(wireformat, "Got null wireformat"); + assertTrue(ccnxTlvDictionary_IsContentObject(wireformat), "Wrong message type"); + assertTrue(ccnxTlvDictionary_GetSchemaVersion(wireformat) == CCNxTlvDictionary_SchemaVersion_V1, + "Wrong schema, got %d expected %d", + ccnxTlvDictionary_GetSchemaVersion(wireformat), CCNxTlvDictionary_SchemaVersion_V1); + + ccnxTlvDictionary_Release(&wireformat); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(SchemaV1, ccnxWireFormatFacadeV1_FromControlPacketType) +{ + PARCBuffer *buffer = parcBuffer_Allocate(1); + CCNxTlvDictionary *wireformat = _ccnxWireFormatFacadeV1_FromControlPacketType(buffer); + assertNotNull(wireformat, "Got null wireformat"); + assertTrue(ccnxTlvDictionary_IsControl(wireformat), "Wrong message type"); + assertTrue(ccnxTlvDictionary_GetSchemaVersion(wireformat) == CCNxTlvDictionary_SchemaVersion_V1, + "Wrong schema, got %d expected %d", + ccnxTlvDictionary_GetSchemaVersion(wireformat), CCNxTlvDictionary_SchemaVersion_V1); + + ccnxTlvDictionary_Release(&wireformat); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(SchemaV1, ccnxWireFormatFacadeV1_FromInterestPacketType) +{ + PARCBuffer *buffer = parcBuffer_Allocate(1); + CCNxTlvDictionary *wireformat = _ccnxWireFormatFacadeV1_FromInterestPacketType(buffer); + assertNotNull(wireformat, "Got null wireformat"); + assertTrue(ccnxTlvDictionary_IsInterest(wireformat), "Wrong message type"); + assertTrue(ccnxTlvDictionary_GetSchemaVersion(wireformat) == CCNxTlvDictionary_SchemaVersion_V1, + "Wrong schema, got %d expected %d", + ccnxTlvDictionary_GetSchemaVersion(wireformat), CCNxTlvDictionary_SchemaVersion_V1); + + ccnxTlvDictionary_Release(&wireformat); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(SchemaV1, ccnxWireFormatFacadeV1_FromInterestReturnPacketType) +{ + PARCBuffer *buffer = parcBuffer_Allocate(1); + CCNxTlvDictionary *wireformat = _ccnxWireFormatFacadeV1_FromInterestReturnPacketType(buffer); + assertNotNull(wireformat, "Got null wireformat"); + assertTrue(ccnxTlvDictionary_IsInterestReturn(wireformat), "Wrong message type"); + assertTrue(ccnxTlvDictionary_GetSchemaVersion(wireformat) == CCNxTlvDictionary_SchemaVersion_V1, + "Wrong schema, got %d expected %d", + ccnxTlvDictionary_GetSchemaVersion(wireformat), CCNxTlvDictionary_SchemaVersion_V1); + + ccnxTlvDictionary_Release(&wireformat); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(SchemaV1, ccnxWireFormatFacadeV1_Get) +{ + PARCBuffer *buffer = parcBuffer_Allocate(1); + CCNxTlvDictionary *wireformat = _ccnxWireFormatFacadeV1_FromInterestPacketType(buffer); + + PARCBuffer *test = _ccnxWireFormatFacadeV1_GetWireFormatBuffer(wireformat); + assertTrue(test == buffer, "Wrong iovec: got %p expected %p", (void *) test, (void *) buffer); + + ccnxTlvDictionary_Release(&wireformat); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(SchemaV1, ccnxWireFormatFacadeV1_Put) +{ + PARCBuffer *buffer = parcBuffer_Allocate(1); + CCNxTlvDictionary *packet = ccnxTlvDictionary_Create(20, 20); + ccnxTlvDictionary_SetMessageType_Interest(packet, CCNxTlvDictionary_SchemaVersion_V1); + bool success = _ccnxWireFormatFacadeV1_PutWireFormatBuffer(packet, buffer); + + assertTrue(success, "Failed to put buffer in to dictionary"); + + ccnxTlvDictionary_Release(&packet); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(SchemaV1, ccnxWireFormatFacadeV1_WriteToFile) +{ + const char string[] = "Hello dev null\n"; + PARCBuffer *buffer = parcBuffer_Wrap((void *) string, sizeof(string), 0, sizeof(string)); + CCNxTlvDictionary *wireformat = _ccnxWireFormatFacadeV1_FromInterestPacketType(buffer); + + _ccnxWireFormatFacadeV1_WriteToFile(wireformat, "/dev/null"); + + ccnxTlvDictionary_Release(&wireformat); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(SchemaV1, ccnxWireFormatFacadeV1_FromInterestPacketTypeIoVec) +{ + uint8_t data[64]; + + uint8_t pad[32]; + CCNxCodecNetworkBufferIoVec *vec = createNetworkBufferIoVec(512, 32, pad, 64, data); + + CCNxTlvDictionary *wireformat = _ccnxWireFormatFacadeV1_FromInterestPacketTypeIoVec(vec); + assertNotNull(wireformat, "Got null wireformat"); + assertTrue(ccnxTlvDictionary_IsInterest(wireformat), "Wrong message type"); + assertTrue(ccnxTlvDictionary_GetSchemaVersion(wireformat) == CCNxTlvDictionary_SchemaVersion_V1, + "Wrong schema, got %d expected %d", + ccnxTlvDictionary_GetSchemaVersion(wireformat), CCNxTlvDictionary_SchemaVersion_V1); + + ccnxTlvDictionary_Release(&wireformat); + ccnxCodecNetworkBufferIoVec_Release(&vec); +} + +LONGBOW_TEST_CASE(SchemaV1, ccnxWireFormatFacadeV1_GetIoVec) +{ + uint8_t *data = parcMemory_Allocate(64); + memset(data, 0, 64); + + PARCBuffer *buffer = parcBuffer_Allocate(1); + CCNxCodecNetworkBuffer *netbuff = ccnxCodecNetworkBuffer_CreateFromArray(&ParcMemoryMemoryBlock, NULL, 64, data); + CCNxCodecNetworkBufferIoVec *iovec = ccnxCodecNetworkBuffer_CreateIoVec(netbuff); + + CCNxTlvDictionary *packet = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + _ccnxWireFormatFacadeV1_PutIoVec(packet, iovec); + + CCNxCodecNetworkBufferIoVec *test = _ccnxWireFormatFacadeV1_GetIoVec(packet); + assertTrue(test == iovec, "Failed to get iovec from dictionary, expected %p got %p", (void *) iovec, (void *) test); + + ccnxTlvDictionary_Release(&packet); + parcBuffer_Release(&buffer); + ccnxCodecNetworkBufferIoVec_Release(&iovec); + ccnxCodecNetworkBuffer_Release(&netbuff); +} + +LONGBOW_TEST_CASE(SchemaV1, ccnxWireFormatFacadeV1_SetHopLimit) +{ + uint8_t *data = parcMemory_Allocate(64); + memset(data, 0, 64); + + PARCBuffer *buffer = parcBuffer_Allocate(1); + CCNxCodecNetworkBuffer *netbuff = ccnxCodecNetworkBuffer_CreateFromArray(&ParcMemoryMemoryBlock, NULL, 64, data); + CCNxCodecNetworkBufferIoVec *iovec = ccnxCodecNetworkBuffer_CreateIoVec(netbuff); + + CCNxTlvDictionary *packet = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + _ccnxWireFormatFacadeV1_PutIoVec(packet, iovec); + + _ccnxWireFormatFacadeV1_SetHopLimit(packet, 10); + + ccnxTlvDictionary_Release(&packet); + parcBuffer_Release(&buffer); + ccnxCodecNetworkBufferIoVec_Release(&iovec); + ccnxCodecNetworkBuffer_Release(&netbuff); + + CCNxCodecSchemaV1InterestHeader header; + + buffer = parcBuffer_Wrap((void *) &header, sizeof(header), 0, sizeof(header)); + + packet = _ccnxWireFormatFacadeV1_FromContentObjectPacketType(buffer); + + _ccnxWireFormatFacadeV1_SetHopLimit(packet, 10); + + ccnxTlvDictionary_Release(&packet); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(SchemaV1, ccnxWireFormatFacadeV1_SetProtectedRegionStart) +{ + const char string[] = "Hello dev null\n"; + PARCBuffer *buffer = parcBuffer_Wrap((void *) string, sizeof(string), 0, sizeof(string)); + + CCNxTlvDictionary *packet = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + _ccnxWireFormatFacadeV1_PutWireFormatBuffer(packet, buffer); + + size_t start = 5; + bool success = _ccnxWireFormatFacadeV1_SetProtectedRegionStart(packet, start); + assertTrue(success, "Failed to put integer in to dictionary"); + + assertTrue(ccnxTlvDictionary_IsValueInteger(packet, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_ProtectedStart), "ProtectedStart not set"); + + ccnxTlvDictionary_Release(&packet); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(SchemaV1, ccnxWireFormatFacadeV1_SetProtectedRegionLength) +{ + const char string[] = "Hello dev null\n"; + PARCBuffer *buffer = parcBuffer_Wrap((void *) string, sizeof(string), 0, sizeof(string)); + + CCNxTlvDictionary *packet = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + _ccnxWireFormatFacadeV1_PutWireFormatBuffer(packet, buffer); + + size_t length = 5; + bool success = _ccnxWireFormatFacadeV1_SetProtectedRegionLength(packet, length); + assertTrue(success, "Failed to put integer in to dictionary"); + + assertTrue(ccnxTlvDictionary_IsValueInteger(packet, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_ProtectedLength), "ProtectedLength not set"); + + ccnxTlvDictionary_Release(&packet); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(SchemaV1, ccnxWireFormatFacadeV1_HashProtectedRegion_Buffer) +{ + // >1234< + const char string[] = "Hello dev null\n"; + + PARCBuffer *buffer = parcBuffer_Wrap((void *) string, sizeof(string), 0, sizeof(string)); + size_t start = 5; + size_t length = 4; + + CCNxTlvDictionary *packet = _ccnxWireFormatFacadeV1_FromContentObjectPacketType(buffer); + _ccnxWireFormatFacadeV1_SetProtectedRegionStart(packet, start); + _ccnxWireFormatFacadeV1_SetProtectedRegionLength(packet, length); + + PARCCryptoHasher *hasher = parcCryptoHasher_Create(PARCCryptoHashType_SHA256); + PARCCryptoHash *hash = _ccnxWireFormatFacadeV1_HashProtectedRegion(packet, hasher); + + // the correctness of the has is tested in _ccnxWireFormatFacadeV1_ComputeHash + assertNotNull(hash, "Got null hash from a good packet"); + + ccnxTlvDictionary_Release(&packet); + parcCryptoHash_Release(&hash); + parcBuffer_Release(&buffer); + parcCryptoHasher_Release(&hasher); +} + +LONGBOW_TEST_CASE(SchemaV1, ccnxWireFormatFacadeV1_HashProtectedRegion_IoVec) +{ + uint8_t *data = parcMemory_Allocate(64); + memset(data, 0, 64); + + PARCBuffer *buffer = parcBuffer_Allocate(1); + CCNxCodecNetworkBuffer *netbuff = ccnxCodecNetworkBuffer_CreateFromArray(&ParcMemoryMemoryBlock, NULL, 64, data); + CCNxCodecNetworkBufferIoVec *iovec = ccnxCodecNetworkBuffer_CreateIoVec(netbuff); + + CCNxTlvDictionary *packet = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + _ccnxWireFormatFacadeV1_PutIoVec(packet, iovec); + + _ccnxWireFormatFacadeV1_SetProtectedRegionStart(packet, 0); + _ccnxWireFormatFacadeV1_SetProtectedRegionLength(packet, 64); + + PARCCryptoHasher *hasher = parcCryptoHasher_Create(PARCCryptoHashType_SHA256); + PARCCryptoHash *hash = _ccnxWireFormatFacadeV1_HashProtectedRegion(packet, hasher); + + // the correctness of the has is tested in _ccnxWireFormatFacadeV1_ComputeHash + assertNotNull(hash, "Got null hash from a good packet"); + + ccnxTlvDictionary_Release(&packet); + parcCryptoHash_Release(&hash); + parcBuffer_Release(&buffer); + parcCryptoHasher_Release(&hasher); + ccnxCodecNetworkBufferIoVec_Release(&iovec); + ccnxCodecNetworkBuffer_Release(&netbuff); +} + +LONGBOW_TEST_CASE(SchemaV1, ccnxWireFormatFacadeV1_Create_Interest) +{ + PARCBuffer *wireFormat = parcBuffer_Wrap(v1_interest_nameA, sizeof(v1_interest_nameA), 0, sizeof(v1_interest_nameA)); + CCNxTlvDictionary *test = _ccnxWireFormatFacadeV1_CreateFromV1(wireFormat); + assertNotNull(test, "Got null dictionary for good interest"); + assertTrue(ccnxTlvDictionary_IsInterest(test), "Dictionary says it is not an Interest"); + assertTrue(ccnxTlvDictionary_GetSchemaVersion(test) == 1, "Schema says it is not v1"); + parcBuffer_Release(&wireFormat); + ccnxTlvDictionary_Release(&test); +} + +LONGBOW_TEST_CASE(SchemaV1, ccnxWireFormatFacadeV1_Create_ContentObject) +{ + PARCBuffer *wireFormat = parcBuffer_Wrap(v1_content_nameA_crc32c, sizeof(v1_content_nameA_crc32c), 0, sizeof(v1_content_nameA_crc32c)); + CCNxTlvDictionary *test = _ccnxWireFormatFacadeV1_CreateFromV1(wireFormat); + assertNotNull(test, "Got null dictionary for good content object"); + assertTrue(ccnxTlvDictionary_IsContentObject(test), "Dictionary says it is not a Content Object"); + assertTrue(ccnxTlvDictionary_GetSchemaVersion(test) == 1, "Schema says it is not v1"); + parcBuffer_Release(&wireFormat); + ccnxTlvDictionary_Release(&test); +} + +LONGBOW_TEST_CASE(SchemaV1, ccnxWireFormatFacadeV1_Create_Control) +{ + PARCBuffer *wireFormat = parcBuffer_Wrap(v1_cpi_add_route, sizeof(v1_cpi_add_route), 0, sizeof(v1_cpi_add_route)); + CCNxTlvDictionary *test = _ccnxWireFormatFacadeV1_CreateFromV1(wireFormat); + assertNotNull(test, "Got null dictionary for good control"); + assertTrue(ccnxTlvDictionary_IsControl(test), "Dictionary says it is not a control"); + assertTrue(ccnxTlvDictionary_GetSchemaVersion(test) == 1, "Schema says it is not v1"); + parcBuffer_Release(&wireFormat); + ccnxTlvDictionary_Release(&test); +} + +LONGBOW_TEST_CASE(SchemaV1, ccnxWireFormatFacadeV1_Create_InterestReturn) +{ + uint8_t encoded[] = { 1, CCNxCodecSchemaV1Types_PacketType_InterestReturn, 0, 23 }; + PARCBuffer *wireFormat = parcBuffer_Wrap(encoded, sizeof(encoded), 0, sizeof(encoded)); + CCNxTlvDictionary *test = _ccnxWireFormatFacadeV1_CreateFromV1(wireFormat); + assertNotNull(test, "Got null dictionary for good InterestReturn"); + assertTrue(ccnxTlvDictionary_IsInterestReturn(test), "Expected IsInterestReturn() to be true"); + assertTrue(ccnxTlvDictionary_GetSchemaVersion(test) == 1, "Schema says it is not v1"); + parcBuffer_Release(&wireFormat); + ccnxTlvDictionary_Release(&test); +} + +LONGBOW_TEST_CASE(SchemaV1, ccnxWireFormatFacadeV1_Create_UnknownPacketType) +{ + uint8_t encoded[] = { 1, 99, 0, 23 }; + PARCBuffer *wireFormat = parcBuffer_Wrap(encoded, sizeof(encoded), 0, sizeof(encoded)); + CCNxTlvDictionary *test = _ccnxWireFormatFacadeV1_CreateFromV1(wireFormat); + assertNull(test, "Should have gotten null dictionary for unknown packet type"); + parcBuffer_Release(&wireFormat); +} + + +static uint8_t _v1_ContentObject_WithKnownHash[] = { + // Name: lci:/boose/roo/pie + // Payload: "this is the payload" + // Signer: CRC32 + // CO Hash: 4FB301EA5FD523B9A71287B721DC20C94B2D4827674A8CA275B7D57C60447876 + + 0x01, 0x01, 0x00, 0x4e, // Fixed Header + 0x00, 0x00, 0x00, 0x08, + + 0x00, 0x02, 0x00, 0x32, // Type 2 == ContentObject, length 50 (0x32) + 0x00, 0x00, 0x00, 0x17, // Name, length 23 (0x17) + + 0x00, 0x01, 0x00, 0x05, // NameSegment, length 5 + 0x62, 0x6f, 0x6f, 0x73, // "boose" + 0x65, + + 0x00, 0x01, 0x00, 0x03, // NameSegment, length 3 + 0x72, 0x6f, 0x6f, // "roo" + + 0x00, 0x01, 0x00, 0x03, // NameSegment, length 3 + 0x70, 0x69, 0x65, // "pie" + + 0x00, 0x01, 0x00, 0x13, // Payload, length 19 (0x13) + 0x74, 0x68, 0x69, 0x73, // "this is the payload" + 0x20, 0x69, 0x73, 0x20, + 0x74, 0x68, 0x65, 0x20, + 0x70, 0x61, 0x79, 0x6c, + 0x6f, 0x61, 0x64, + + 0x00, 0x03, 0x00, 0x04, // Validation Alg, length 4 + 0x00, 0x02, 0x00, 0x00, // CRC32, length 0 + + 0x00, 0x04, 0x00, 0x04, // Validation Payload, length 4 + 0x7e, 0x60, 0x54, 0xc4, // The payload (the CRC32) +}; + +LONGBOW_TEST_CASE(SchemaV1, ccnxWireFormatFacadeV1_ComputeContentObjectHash) +{ + PARCBuffer *wireFormatBuffer = parcBuffer_Wrap(_v1_ContentObject_WithKnownHash, sizeof(_v1_ContentObject_WithKnownHash), + 0, sizeof(_v1_ContentObject_WithKnownHash)); + + CCNxWireFormatMessage *message = ccnxWireFormatMessage_Create(wireFormatBuffer); + + CCNxTlvDictionary *contentObject = ccnxWireFormatMessage_GetDictionary(message); + + // We have a partially unpacked dictionary now, but we need to more fully unpack it for our processing. + assertTrue(ccnxCodecTlvPacket_BufferDecode(wireFormatBuffer, contentObject), "Expected to decode the wireformat buffer"); + + + PARCCryptoHash *coHash = _ccnxWireFormatFacadeV1_ComputeContentObjectHash(contentObject); + + assertNotNull(coHash, "Expected a non-NULL content object hash"); + + char *computedHash = parcBuffer_ToHexString(parcCryptoHash_GetDigest(coHash)); + + char *knownHash = "4FB301EA5FD523B9A71287B721DC20C94B2D4827674A8CA275B7D57C60447876"; + + assertTrue(strncasecmp(computedHash, knownHash, strlen(knownHash)) == 0, "Expected a matching ContentObject hash"); + + parcMemory_Deallocate(&computedHash); + parcCryptoHash_Release(&coHash); + parcBuffer_Release(&wireFormatBuffer); + + ccnxContentObject_Release(&contentObject); +} + +// ======================================================================= + +LONGBOW_TEST_FIXTURE(Local) +{ + LONGBOW_RUN_TEST_CASE(Local, _ccnxWireFormatFacadeV1_ComputeHash); +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + + +LONGBOW_TEST_CASE(Local, _ccnxWireFormatFacadeV1_ComputeHash) +{ + // >1234< + const char string[] = "Hello dev null\n"; + const char substring[] = " dev"; + + PARCBuffer *buffer = parcBuffer_Wrap((void *) string, sizeof(string), 0, sizeof(string)); + size_t start = 5; + size_t length = 4; + + + // compute the true hash + PARCCryptoHasher *hasher = parcCryptoHasher_Create(PARCCryptoHashType_SHA256); + parcCryptoHasher_Init(hasher); + parcCryptoHasher_UpdateBytes(hasher, substring, length); + PARCCryptoHash *truthHash = parcCryptoHasher_Finalize(hasher); + + // Compute the test hash + PARCCryptoHash *testHash = _ccnxWireFormatFacadeV1_ComputeBufferHash(buffer, hasher, start, length); + + // Test + bool equals = parcCryptoHash_Equals(truthHash, testHash); + assertTrue(equals, "Hashes do not match") + { + PARCBuffer *truthBuffer = parcCryptoHash_GetDigest(truthHash); + PARCBuffer *testBuffer = parcCryptoHash_GetDigest(testHash); + + printf("Expected:\n"); + parcBuffer_Display(truthBuffer, 3); + + printf("Got:\n"); + parcBuffer_Display(testBuffer, 3); + } + + parcCryptoHash_Release(&truthHash); + parcCryptoHash_Release(&testHash); + parcBuffer_Release(&buffer); + parcCryptoHasher_Release(&hasher); +} + +// ======================================================================= + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(ccnx_WireFormatFacade); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-common/ccnx/common/libccnxCommon_About.c b/libccnx-common/ccnx/common/libccnxCommon_About.c new file mode 100644 index 00000000..88f44e17 --- /dev/null +++ b/libccnx-common/ccnx/common/libccnxCommon_About.c @@ -0,0 +1,44 @@ +// DO NOT EDIT THIS FILE. IT IS AUTOMATICALLY GENERATED. +// longbow-generate-about 1.0.20170215.46e2c73a 2017-02-15T08:00:49Z + +#include "libccnxCommon_About.h" + +const char *libccnxCommon_What = "@(#)" "Libccnx Common " RELEASE_VERSION " 2017-02-20T22:04:39.067932" + "@(#)" "\tCopyright (c) 2017 Cisco and/or its affiliates."; + +const char * +libccnxCommonAbout_Name(void) +{ + return "Libccnx Common"; +} + +const char * +libccnxCommonAbout_Version(void) +{ + return RELEASE_VERSION; +} + +const char * +libccnxCommonAbout_About(void) +{ + return "Libccnx Common "RELEASE_VERSION " 2017-02-20T22:04:39.067932" "\nCopyright (c) 2017 Cisco and/or its affiliates.\n"; +} + +const char * +libccnxCommonAbout_MiniNotice(void) +{ + return "Copyright (c) 2017 Cisco and/or its affiliates.\n"; +} + +const char * +libccnxCommonAbout_ShortNotice(void) +{ + return "Copyright (c) 2017 Cisco and/or its affiliates.\n"; +} + +const char * +libccnxCommonAbout_LongNotice(void) +{ + return "Copyright (c) 2017 Cisco and/or its affiliates.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at:\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n"; +} + diff --git a/libccnx-common/ccnx/common/libccnxCommon_About.h b/libccnx-common/ccnx/common/libccnxCommon_About.h new file mode 100755 index 00000000..af28dbdc --- /dev/null +++ b/libccnx-common/ccnx/common/libccnxCommon_About.h @@ -0,0 +1,54 @@ +// DO NOT EDIT THIS FILE. IT IS AUTOMATICALLY GENERATED. +// longbow-generate-about 1.0.20170215.46e2c73a 2017-02-15T08:00:49Z + +#ifndef libccnxCommon_About_h +#define libccnxCommon_About_h +/** + * Embedded string containing information for the what(1) command. + * + */ +extern const char *libccnxCommon_What; + +/** + * Return the name as a C string. + * + * @return The name as a C string. + */ +const char *libccnxCommonAbout_Name(void); + +/** + * Return the version as a C string. + * + * @return The version as a C string. + */ +const char *libccnxCommonAbout_Version(void); + +/** + * Return the About text as a C string. + * + * @return The About text as a C string. + */ +const char *libccnxCommonAbout_About(void); + +/** + * Return the minimum copyright notice as a C string. + * + * @return The minimum copyright notice as a C string. + */ +const char *libccnxCommonAbout_MiniNotice(void); + +/** + * Return the short copyright notice as a C string. + * + * @return The short copyright notice as a C string. + */ +const char *libccnxCommonAbout_ShortNotice(void); + +/** + * Return the long copyright notice as a C string. + * + * @return The long copyright notice as a C string. + */ +const char *libccnxCommonAbout_LongNotice(void); + +#endif // libccnxCommon_About_h diff --git a/libccnx-common/ccnx/common/test/.gitignore b/libccnx-common/ccnx/common/test/.gitignore new file mode 100644 index 00000000..dc9b70b3 --- /dev/null +++ b/libccnx-common/ccnx/common/test/.gitignore @@ -0,0 +1,22 @@ +./Makefile.am +./Makefile.in +test_ccnx_BufferChunker +test_ccnx_FileChunker +test_ccnx_Chunker +test_ccnx_ContentObject +test_ccnx_Interest +test_ccnx_Json +test_ccnx_Key +test_ccnx_KeyListEntry +test_ccnx_KeyLocator +test_ccnx_Link +test_ccnx_Manifest +test_ccnx_ManifestSection +test_ccnx_Name +test_ccnx_NameLabel +test_ccnx_NameSegment +test_ccnx_NameSegmentNumber +test_ccnx_NameType +test_ccnx_NetworkBuffer +test_ccnx_TimeStamp +test_ccnx_WireFormatMessage diff --git a/libccnx-common/ccnx/common/test/CMakeLists.txt b/libccnx-common/ccnx/common/test/CMakeLists.txt new file mode 100644 index 00000000..4cf70a14 --- /dev/null +++ b/libccnx-common/ccnx/common/test/CMakeLists.txt @@ -0,0 +1,29 @@ +# Enable gcov output for the tests +add_definitions(--coverage) +set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} " --coverage") + +configure_file(data.json data.json COPYONLY) + +set(TestsExpectedToPass + test_ccnx_ContentObject + test_ccnx_Interest + test_ccnx_InterestPayloadId + test_ccnx_InterestReturn + test_ccnx_KeyLocator + test_ccnx_KeystoreUtilities + test_ccnx_Link + test_ccnx_Manifest + test_ccnx_ManifestHashGroup + test_ccnx_Name + test_ccnx_NameLabel + test_ccnx_NameSegment + test_ccnx_NameSegmentNumber + test_ccnx_TimeStamp + test_ccnx_WireFormatMessage +) + + +foreach(test ${TestsExpectedToPass}) + AddTest(${test}) +endforeach() + diff --git a/libccnx-common/ccnx/common/test/data.json b/libccnx-common/ccnx/common/test/data.json new file mode 100644 index 00000000..a146f6ff --- /dev/null +++ b/libccnx-common/ccnx/common/test/data.json @@ -0,0 +1,2614 @@ +{ + "array" : [ + { + "id": 6104546, + "name": "-REPONAME", + "full_name": "mralexgray/-REPONAME", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/-REPONAME", + "description": null, + "fork": false, + "url": "https://api.github.com/repos/mralexgray/-REPONAME", + "forks_url": "https://api.github.com/repos/mralexgray/-REPONAME/forks", + "keys_url": "https://api.github.com/repos/mralexgray/-REPONAME/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/-REPONAME/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/-REPONAME/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/-REPONAME/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/-REPONAME/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/-REPONAME/events", + "assignees_url": "https://api.github.com/repos/mralexgray/-REPONAME/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/-REPONAME/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/-REPONAME/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/-REPONAME/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/-REPONAME/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/-REPONAME/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/-REPONAME/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/-REPONAME/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/-REPONAME/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/-REPONAME/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/-REPONAME/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/-REPONAME/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/-REPONAME/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/-REPONAME/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/-REPONAME/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/-REPONAME/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/-REPONAME/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/-REPONAME/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/-REPONAME/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/-REPONAME/merges", + "archive_url": "https://api.github.com/repos/mralexgray/-REPONAME/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/-REPONAME/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/-REPONAME/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/-REPONAME/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/-REPONAME/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/-REPONAME/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/-REPONAME/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/-REPONAME/releases{/id}", + "created_at": "2012-10-06T16:37:39Z", + "updated_at": "2013-01-12T13:39:30Z", + "pushed_at": "2012-10-06T16:37:39Z", + "git_url": "git://github.com/mralexgray/-REPONAME.git", + "ssh_url": "git@github.com:mralexgray/-REPONAME.git", + "clone_url": "https://github.com/mralexgray/-REPONAME.git", + "svn_url": "https://github.com/mralexgray/-REPONAME", + "homepage": null, + "size": 48, + "stargazers_count": 0, + "watchers_count": 0, + "language": null, + "has_issues": true, + "has_downloads": true, + "has_wiki": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 0, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "master", + "master_branch": "master" + }, + { + "id": 13121042, + "name": "ace", + "full_name": "mralexgray/ace", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/ace", + "description": "Ace (Ajax.org Cloud9 Editor)", + "fork": true, + "url": "https://api.github.com/repos/mralexgray/ace", + "forks_url": "https://api.github.com/repos/mralexgray/ace/forks", + "keys_url": "https://api.github.com/repos/mralexgray/ace/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/ace/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/ace/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/ace/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/ace/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/ace/events", + "assignees_url": "https://api.github.com/repos/mralexgray/ace/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/ace/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/ace/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/ace/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/ace/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/ace/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/ace/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/ace/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/ace/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/ace/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/ace/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/ace/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/ace/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/ace/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/ace/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/ace/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/ace/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/ace/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/ace/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/ace/merges", + "archive_url": "https://api.github.com/repos/mralexgray/ace/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/ace/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/ace/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/ace/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/ace/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/ace/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/ace/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/ace/releases{/id}", + "created_at": "2013-09-26T11:58:10Z", + "updated_at": "2013-10-26T12:34:49Z", + "pushed_at": "2013-10-26T12:34:48Z", + "git_url": "git://github.com/mralexgray/ace.git", + "ssh_url": "git@github.com:mralexgray/ace.git", + "clone_url": "https://github.com/mralexgray/ace.git", + "svn_url": "https://github.com/mralexgray/ace", + "homepage": "http://ace.c9.io", + "size": 21080, + "stargazers_count": 0, + "watchers_count": 0, + "language": "JavaScript", + "has_issues": false, + "has_downloads": true, + "has_wiki": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 0, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "master", + "master_branch": "master" + }, + { + "id": 10791045, + "name": "ACEView", + "full_name": "mralexgray/ACEView", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/ACEView", + "description": "Use the wonderful ACE editor in your Cocoa applications", + "fork": true, + "url": "https://api.github.com/repos/mralexgray/ACEView", + "forks_url": "https://api.github.com/repos/mralexgray/ACEView/forks", + "keys_url": "https://api.github.com/repos/mralexgray/ACEView/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/ACEView/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/ACEView/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/ACEView/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/ACEView/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/ACEView/events", + "assignees_url": "https://api.github.com/repos/mralexgray/ACEView/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/ACEView/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/ACEView/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/ACEView/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/ACEView/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/ACEView/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/ACEView/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/ACEView/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/ACEView/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/ACEView/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/ACEView/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/ACEView/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/ACEView/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/ACEView/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/ACEView/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/ACEView/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/ACEView/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/ACEView/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/ACEView/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/ACEView/merges", + "archive_url": "https://api.github.com/repos/mralexgray/ACEView/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/ACEView/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/ACEView/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/ACEView/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/ACEView/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/ACEView/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/ACEView/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/ACEView/releases{/id}", + "created_at": "2013-06-19T12:15:04Z", + "updated_at": "2013-10-30T12:39:24Z", + "pushed_at": "2013-10-30T12:39:18Z", + "git_url": "git://github.com/mralexgray/ACEView.git", + "ssh_url": "git@github.com:mralexgray/ACEView.git", + "clone_url": "https://github.com/mralexgray/ACEView.git", + "svn_url": "https://github.com/mralexgray/ACEView", + "homepage": null, + "size": 1661, + "stargazers_count": 0, + "watchers_count": 0, + "language": "Objective-C", + "has_issues": false, + "has_downloads": true, + "has_wiki": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 0, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "master", + "master_branch": "master" + }, + { + "id": 13623648, + "name": "ActiveLog", + "full_name": "mralexgray/ActiveLog", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/ActiveLog", + "description": "Shut up all logs with active filter.", + "fork": true, + "url": "https://api.github.com/repos/mralexgray/ActiveLog", + "forks_url": "https://api.github.com/repos/mralexgray/ActiveLog/forks", + "keys_url": "https://api.github.com/repos/mralexgray/ActiveLog/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/ActiveLog/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/ActiveLog/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/ActiveLog/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/ActiveLog/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/ActiveLog/events", + "assignees_url": "https://api.github.com/repos/mralexgray/ActiveLog/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/ActiveLog/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/ActiveLog/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/ActiveLog/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/ActiveLog/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/ActiveLog/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/ActiveLog/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/ActiveLog/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/ActiveLog/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/ActiveLog/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/ActiveLog/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/ActiveLog/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/ActiveLog/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/ActiveLog/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/ActiveLog/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/ActiveLog/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/ActiveLog/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/ActiveLog/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/ActiveLog/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/ActiveLog/merges", + "archive_url": "https://api.github.com/repos/mralexgray/ActiveLog/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/ActiveLog/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/ActiveLog/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/ActiveLog/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/ActiveLog/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/ActiveLog/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/ActiveLog/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/ActiveLog/releases{/id}", + "created_at": "2013-10-16T15:52:37Z", + "updated_at": "2013-10-16T15:52:37Z", + "pushed_at": "2011-07-03T06:28:59Z", + "git_url": "git://github.com/mralexgray/ActiveLog.git", + "ssh_url": "git@github.com:mralexgray/ActiveLog.git", + "clone_url": "https://github.com/mralexgray/ActiveLog.git", + "svn_url": "https://github.com/mralexgray/ActiveLog", + "homepage": "http://deepitpro.com/en/articles/ActiveLog/info/", + "size": 60, + "stargazers_count": 0, + "watchers_count": 0, + "language": "Objective-C", + "has_issues": false, + "has_downloads": true, + "has_wiki": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 0, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "master", + "master_branch": "master" + }, + { + "id": 9716210, + "name": "adium", + "full_name": "mralexgray/adium", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/adium", + "description": "Official mirror of hg.adium.im", + "fork": true, + "url": "https://api.github.com/repos/mralexgray/adium", + "forks_url": "https://api.github.com/repos/mralexgray/adium/forks", + "keys_url": "https://api.github.com/repos/mralexgray/adium/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/adium/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/adium/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/adium/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/adium/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/adium/events", + "assignees_url": "https://api.github.com/repos/mralexgray/adium/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/adium/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/adium/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/adium/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/adium/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/adium/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/adium/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/adium/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/adium/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/adium/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/adium/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/adium/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/adium/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/adium/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/adium/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/adium/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/adium/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/adium/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/adium/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/adium/merges", + "archive_url": "https://api.github.com/repos/mralexgray/adium/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/adium/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/adium/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/adium/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/adium/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/adium/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/adium/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/adium/releases{/id}", + "created_at": "2013-04-27T14:59:33Z", + "updated_at": "2013-04-27T14:59:33Z", + "pushed_at": "2013-04-26T16:43:53Z", + "git_url": "git://github.com/mralexgray/adium.git", + "ssh_url": "git@github.com:mralexgray/adium.git", + "clone_url": "https://github.com/mralexgray/adium.git", + "svn_url": "https://github.com/mralexgray/adium", + "homepage": null, + "size": 277719, + "stargazers_count": 0, + "watchers_count": 0, + "language": "Objective-C", + "has_issues": false, + "has_downloads": true, + "has_wiki": false, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 0, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "master", + "master_branch": "master" + }, + { + "id": 12752329, + "name": "ADLivelyTableView", + "full_name": "mralexgray/ADLivelyTableView", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/ADLivelyTableView", + "description": "Lively UITableView", + "fork": true, + "url": "https://api.github.com/repos/mralexgray/ADLivelyTableView", + "forks_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/forks", + "keys_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/events", + "assignees_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/merges", + "archive_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/ADLivelyTableView/releases{/id}", + "created_at": "2013-09-11T09:18:01Z", + "updated_at": "2013-09-11T09:18:03Z", + "pushed_at": "2012-05-10T10:40:15Z", + "git_url": "git://github.com/mralexgray/ADLivelyTableView.git", + "ssh_url": "git@github.com:mralexgray/ADLivelyTableView.git", + "clone_url": "https://github.com/mralexgray/ADLivelyTableView.git", + "svn_url": "https://github.com/mralexgray/ADLivelyTableView", + "homepage": "http://applidium.com/en/news/lively_uitableview/", + "size": 73, + "stargazers_count": 0, + "watchers_count": 0, + "language": "Objective-C", + "has_issues": false, + "has_downloads": true, + "has_wiki": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 0, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "master", + "master_branch": "master" + }, + { + "id": 5697379, + "name": "AFIncrementalStore", + "full_name": "mralexgray/AFIncrementalStore", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/AFIncrementalStore", + "description": "Core Data Persistence with AFNetworking, Done Right", + "fork": true, + "url": "https://api.github.com/repos/mralexgray/AFIncrementalStore", + "forks_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/forks", + "keys_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/events", + "assignees_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/merges", + "archive_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/AFIncrementalStore/releases{/id}", + "created_at": "2012-09-06T04:20:33Z", + "updated_at": "2013-01-12T03:15:29Z", + "pushed_at": "2012-09-01T22:46:25Z", + "git_url": "git://github.com/mralexgray/AFIncrementalStore.git", + "ssh_url": "git@github.com:mralexgray/AFIncrementalStore.git", + "clone_url": "https://github.com/mralexgray/AFIncrementalStore.git", + "svn_url": "https://github.com/mralexgray/AFIncrementalStore", + "homepage": null, + "size": 139, + "stargazers_count": 0, + "watchers_count": 0, + "language": "Objective-C", + "has_issues": false, + "has_downloads": true, + "has_wiki": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 0, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "master", + "master_branch": "master" + }, + { + "id": 6969621, + "name": "AFNetworking", + "full_name": "mralexgray/AFNetworking", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/AFNetworking", + "description": "A delightful iOS and OS X networking framework", + "fork": true, + "url": "https://api.github.com/repos/mralexgray/AFNetworking", + "forks_url": "https://api.github.com/repos/mralexgray/AFNetworking/forks", + "keys_url": "https://api.github.com/repos/mralexgray/AFNetworking/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/AFNetworking/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/AFNetworking/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/AFNetworking/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/AFNetworking/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/AFNetworking/events", + "assignees_url": "https://api.github.com/repos/mralexgray/AFNetworking/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/AFNetworking/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/AFNetworking/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/AFNetworking/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/AFNetworking/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/AFNetworking/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/AFNetworking/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/AFNetworking/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/AFNetworking/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/AFNetworking/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/AFNetworking/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/AFNetworking/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/AFNetworking/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/AFNetworking/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/AFNetworking/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/AFNetworking/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/AFNetworking/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/AFNetworking/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/AFNetworking/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/AFNetworking/merges", + "archive_url": "https://api.github.com/repos/mralexgray/AFNetworking/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/AFNetworking/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/AFNetworking/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/AFNetworking/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/AFNetworking/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/AFNetworking/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/AFNetworking/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/AFNetworking/releases{/id}", + "created_at": "2012-12-02T17:00:04Z", + "updated_at": "2014-01-24T07:14:33Z", + "pushed_at": "2014-01-24T07:14:32Z", + "git_url": "git://github.com/mralexgray/AFNetworking.git", + "ssh_url": "git@github.com:mralexgray/AFNetworking.git", + "clone_url": "https://github.com/mralexgray/AFNetworking.git", + "svn_url": "https://github.com/mralexgray/AFNetworking", + "homepage": "http://afnetworking.com", + "size": 4341, + "stargazers_count": 1, + "watchers_count": 1, + "language": "Objective-C", + "has_issues": false, + "has_downloads": true, + "has_wiki": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 0, + "forks": 0, + "open_issues": 0, + "watchers": 1, + "default_branch": "master", + "master_branch": "master" + }, + { + "id": 9485541, + "name": "AGNSSplitView", + "full_name": "mralexgray/AGNSSplitView", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/AGNSSplitView", + "description": "Simple NSSplitView additions.", + "fork": true, + "url": "https://api.github.com/repos/mralexgray/AGNSSplitView", + "forks_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/forks", + "keys_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/events", + "assignees_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/merges", + "archive_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/AGNSSplitView/releases{/id}", + "created_at": "2013-04-17T00:10:13Z", + "updated_at": "2013-04-17T00:10:13Z", + "pushed_at": "2013-02-26T00:32:32Z", + "git_url": "git://github.com/mralexgray/AGNSSplitView.git", + "ssh_url": "git@github.com:mralexgray/AGNSSplitView.git", + "clone_url": "https://github.com/mralexgray/AGNSSplitView.git", + "svn_url": "https://github.com/mralexgray/AGNSSplitView", + "homepage": null, + "size": 68, + "stargazers_count": 0, + "watchers_count": 0, + "language": "Objective-C", + "has_issues": false, + "has_downloads": true, + "has_wiki": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 0, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "master", + "master_branch": "master" + }, + { + "id": 12767784, + "name": "AGScopeBar", + "full_name": "mralexgray/AGScopeBar", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/AGScopeBar", + "description": "Custom scope bar implementation for Cocoa", + "fork": true, + "url": "https://api.github.com/repos/mralexgray/AGScopeBar", + "forks_url": "https://api.github.com/repos/mralexgray/AGScopeBar/forks", + "keys_url": "https://api.github.com/repos/mralexgray/AGScopeBar/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/AGScopeBar/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/AGScopeBar/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/AGScopeBar/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/AGScopeBar/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/AGScopeBar/events", + "assignees_url": "https://api.github.com/repos/mralexgray/AGScopeBar/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/AGScopeBar/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/AGScopeBar/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/AGScopeBar/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/AGScopeBar/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/AGScopeBar/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/AGScopeBar/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/AGScopeBar/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/AGScopeBar/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/AGScopeBar/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/AGScopeBar/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/AGScopeBar/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/AGScopeBar/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/AGScopeBar/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/AGScopeBar/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/AGScopeBar/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/AGScopeBar/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/AGScopeBar/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/AGScopeBar/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/AGScopeBar/merges", + "archive_url": "https://api.github.com/repos/mralexgray/AGScopeBar/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/AGScopeBar/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/AGScopeBar/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/AGScopeBar/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/AGScopeBar/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/AGScopeBar/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/AGScopeBar/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/AGScopeBar/releases{/id}", + "created_at": "2013-09-11T21:06:54Z", + "updated_at": "2013-09-11T21:06:54Z", + "pushed_at": "2013-05-07T03:35:29Z", + "git_url": "git://github.com/mralexgray/AGScopeBar.git", + "ssh_url": "git@github.com:mralexgray/AGScopeBar.git", + "clone_url": "https://github.com/mralexgray/AGScopeBar.git", + "svn_url": "https://github.com/mralexgray/AGScopeBar", + "homepage": null, + "size": 64, + "stargazers_count": 0, + "watchers_count": 0, + "language": "Objective-C", + "has_issues": false, + "has_downloads": true, + "has_wiki": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 0, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "master", + "master_branch": "master" + }, + { + "id": 9227846, + "name": "AHContentBrowser", + "full_name": "mralexgray/AHContentBrowser", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/AHContentBrowser", + "description": "A Mac only webview that loads a fast readable version of the website if available.", + "fork": true, + "url": "https://api.github.com/repos/mralexgray/AHContentBrowser", + "forks_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/forks", + "keys_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/events", + "assignees_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/merges", + "archive_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/AHContentBrowser/releases{/id}", + "created_at": "2013-04-04T20:56:16Z", + "updated_at": "2013-04-04T20:56:16Z", + "pushed_at": "2013-03-13T17:38:23Z", + "git_url": "git://github.com/mralexgray/AHContentBrowser.git", + "ssh_url": "git@github.com:mralexgray/AHContentBrowser.git", + "clone_url": "https://github.com/mralexgray/AHContentBrowser.git", + "svn_url": "https://github.com/mralexgray/AHContentBrowser", + "homepage": "", + "size": 223, + "stargazers_count": 0, + "watchers_count": 0, + "language": "Objective-C", + "has_issues": false, + "has_downloads": true, + "has_wiki": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 0, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "master", + "master_branch": "master" + }, + { + "id": 9167473, + "name": "AHLayout", + "full_name": "mralexgray/AHLayout", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/AHLayout", + "description": "AHLayout", + "fork": true, + "url": "https://api.github.com/repos/mralexgray/AHLayout", + "forks_url": "https://api.github.com/repos/mralexgray/AHLayout/forks", + "keys_url": "https://api.github.com/repos/mralexgray/AHLayout/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/AHLayout/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/AHLayout/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/AHLayout/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/AHLayout/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/AHLayout/events", + "assignees_url": "https://api.github.com/repos/mralexgray/AHLayout/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/AHLayout/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/AHLayout/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/AHLayout/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/AHLayout/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/AHLayout/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/AHLayout/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/AHLayout/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/AHLayout/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/AHLayout/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/AHLayout/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/AHLayout/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/AHLayout/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/AHLayout/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/AHLayout/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/AHLayout/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/AHLayout/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/AHLayout/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/AHLayout/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/AHLayout/merges", + "archive_url": "https://api.github.com/repos/mralexgray/AHLayout/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/AHLayout/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/AHLayout/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/AHLayout/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/AHLayout/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/AHLayout/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/AHLayout/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/AHLayout/releases{/id}", + "created_at": "2013-04-02T10:10:30Z", + "updated_at": "2013-07-08T02:31:17Z", + "pushed_at": "2013-07-08T02:31:14Z", + "git_url": "git://github.com/mralexgray/AHLayout.git", + "ssh_url": "git@github.com:mralexgray/AHLayout.git", + "clone_url": "https://github.com/mralexgray/AHLayout.git", + "svn_url": "https://github.com/mralexgray/AHLayout", + "homepage": null, + "size": 359, + "stargazers_count": 0, + "watchers_count": 0, + "language": "Objective-C", + "has_issues": false, + "has_downloads": true, + "has_wiki": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 0, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "master", + "master_branch": "master" + }, + { + "id": 18450201, + "name": "Airmail-Plug-In-Framework", + "full_name": "mralexgray/Airmail-Plug-In-Framework", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/Airmail-Plug-In-Framework", + "description": "", + "fork": true, + "url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework", + "forks_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/forks", + "keys_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/events", + "assignees_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/merges", + "archive_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/Airmail-Plug-In-Framework/releases{/id}", + "created_at": "2014-04-04T19:33:54Z", + "updated_at": "2014-04-04T19:33:54Z", + "pushed_at": "2014-03-27T15:42:19Z", + "git_url": "git://github.com/mralexgray/Airmail-Plug-In-Framework.git", + "ssh_url": "git@github.com:mralexgray/Airmail-Plug-In-Framework.git", + "clone_url": "https://github.com/mralexgray/Airmail-Plug-In-Framework.git", + "svn_url": "https://github.com/mralexgray/Airmail-Plug-In-Framework", + "homepage": null, + "size": 888, + "stargazers_count": 0, + "watchers_count": 0, + "language": null, + "has_issues": false, + "has_downloads": true, + "has_wiki": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 0, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "master", + "master_branch": "master" + }, + { + "id": 5203219, + "name": "AJS-iTunes-API", + "full_name": "mralexgray/AJS-iTunes-API", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/AJS-iTunes-API", + "description": "Cocoa wrapper for the iTunes search API - for iOS and Mac OSX projects", + "fork": true, + "url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API", + "forks_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/forks", + "keys_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/events", + "assignees_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/merges", + "archive_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/AJS-iTunes-API/releases{/id}", + "created_at": "2012-07-27T10:20:58Z", + "updated_at": "2013-01-11T11:00:05Z", + "pushed_at": "2011-10-30T22:26:48Z", + "git_url": "git://github.com/mralexgray/AJS-iTunes-API.git", + "ssh_url": "git@github.com:mralexgray/AJS-iTunes-API.git", + "clone_url": "https://github.com/mralexgray/AJS-iTunes-API.git", + "svn_url": "https://github.com/mralexgray/AJS-iTunes-API", + "homepage": "", + "size": 103, + "stargazers_count": 1, + "watchers_count": 1, + "language": "Objective-C", + "has_issues": false, + "has_downloads": true, + "has_wiki": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 0, + "forks": 0, + "open_issues": 0, + "watchers": 1, + "default_branch": "master", + "master_branch": "master" + }, + { + "id": 10093801, + "name": "Alcatraz", + "full_name": "mralexgray/Alcatraz", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/Alcatraz", + "description": "The most awesome (and only) Xcode package manager!", + "fork": true, + "url": "https://api.github.com/repos/mralexgray/Alcatraz", + "forks_url": "https://api.github.com/repos/mralexgray/Alcatraz/forks", + "keys_url": "https://api.github.com/repos/mralexgray/Alcatraz/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/Alcatraz/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/Alcatraz/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/Alcatraz/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/Alcatraz/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/Alcatraz/events", + "assignees_url": "https://api.github.com/repos/mralexgray/Alcatraz/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/Alcatraz/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/Alcatraz/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/Alcatraz/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/Alcatraz/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/Alcatraz/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/Alcatraz/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/Alcatraz/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/Alcatraz/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/Alcatraz/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/Alcatraz/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/Alcatraz/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/Alcatraz/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/Alcatraz/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/Alcatraz/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/Alcatraz/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/Alcatraz/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/Alcatraz/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/Alcatraz/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/Alcatraz/merges", + "archive_url": "https://api.github.com/repos/mralexgray/Alcatraz/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/Alcatraz/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/Alcatraz/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/Alcatraz/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/Alcatraz/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/Alcatraz/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/Alcatraz/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/Alcatraz/releases{/id}", + "created_at": "2013-05-16T04:41:13Z", + "updated_at": "2014-03-19T20:38:35Z", + "pushed_at": "2014-03-19T12:50:37Z", + "git_url": "git://github.com/mralexgray/Alcatraz.git", + "ssh_url": "git@github.com:mralexgray/Alcatraz.git", + "clone_url": "https://github.com/mralexgray/Alcatraz.git", + "svn_url": "https://github.com/mralexgray/Alcatraz", + "homepage": "mneorr.github.com/Alcatraz", + "size": 3668, + "stargazers_count": 0, + "watchers_count": 0, + "language": "Objective-C", + "has_issues": false, + "has_downloads": true, + "has_wiki": false, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 0, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "master", + "master_branch": "master" + }, + { + "id": 12916552, + "name": "alcatraz-packages", + "full_name": "mralexgray/alcatraz-packages", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/alcatraz-packages", + "description": "Package list repository for Alcatraz", + "fork": true, + "url": "https://api.github.com/repos/mralexgray/alcatraz-packages", + "forks_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/forks", + "keys_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/events", + "assignees_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/merges", + "archive_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/alcatraz-packages/releases{/id}", + "created_at": "2013-09-18T07:15:24Z", + "updated_at": "2013-09-18T07:15:25Z", + "pushed_at": "2013-09-09T07:51:48Z", + "git_url": "git://github.com/mralexgray/alcatraz-packages.git", + "ssh_url": "git@github.com:mralexgray/alcatraz-packages.git", + "clone_url": "https://github.com/mralexgray/alcatraz-packages.git", + "svn_url": "https://github.com/mralexgray/alcatraz-packages", + "homepage": "mneorr.github.com/Alcatraz", + "size": 482, + "stargazers_count": 0, + "watchers_count": 0, + "language": "Ruby", + "has_issues": false, + "has_downloads": true, + "has_wiki": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 0, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "master", + "master_branch": "master" + }, + { + "id": 10476467, + "name": "Alfred-Google-Translate", + "full_name": "mralexgray/Alfred-Google-Translate", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/Alfred-Google-Translate", + "description": "Extension for Alfred that will do a Google translate for you", + "fork": true, + "url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate", + "forks_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/forks", + "keys_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/events", + "assignees_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/merges", + "archive_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/Alfred-Google-Translate/releases{/id}", + "created_at": "2013-06-04T10:45:10Z", + "updated_at": "2013-06-04T10:45:10Z", + "pushed_at": "2013-01-12T19:39:03Z", + "git_url": "git://github.com/mralexgray/Alfred-Google-Translate.git", + "ssh_url": "git@github.com:mralexgray/Alfred-Google-Translate.git", + "clone_url": "https://github.com/mralexgray/Alfred-Google-Translate.git", + "svn_url": "https://github.com/mralexgray/Alfred-Google-Translate", + "homepage": null, + "size": 103, + "stargazers_count": 0, + "watchers_count": 0, + "language": "Shell", + "has_issues": false, + "has_downloads": true, + "has_wiki": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 0, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "master", + "master_branch": "master" + }, + { + "id": 5524019, + "name": "Amber", + "full_name": "mralexgray/Amber", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/Amber", + "description": "Fork of the difficult-to-deal-with Amber.framework", + "fork": false, + "url": "https://api.github.com/repos/mralexgray/Amber", + "forks_url": "https://api.github.com/repos/mralexgray/Amber/forks", + "keys_url": "https://api.github.com/repos/mralexgray/Amber/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/Amber/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/Amber/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/Amber/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/Amber/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/Amber/events", + "assignees_url": "https://api.github.com/repos/mralexgray/Amber/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/Amber/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/Amber/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/Amber/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/Amber/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/Amber/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/Amber/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/Amber/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/Amber/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/Amber/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/Amber/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/Amber/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/Amber/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/Amber/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/Amber/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/Amber/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/Amber/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/Amber/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/Amber/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/Amber/merges", + "archive_url": "https://api.github.com/repos/mralexgray/Amber/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/Amber/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/Amber/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/Amber/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/Amber/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/Amber/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/Amber/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/Amber/releases{/id}", + "created_at": "2012-08-23T10:38:24Z", + "updated_at": "2013-01-11T22:25:35Z", + "pushed_at": "2012-08-23T10:38:25Z", + "git_url": "git://github.com/mralexgray/Amber.git", + "ssh_url": "git@github.com:mralexgray/Amber.git", + "clone_url": "https://github.com/mralexgray/Amber.git", + "svn_url": "https://github.com/mralexgray/Amber", + "homepage": null, + "size": 48, + "stargazers_count": 0, + "watchers_count": 0, + "language": null, + "has_issues": true, + "has_downloads": true, + "has_wiki": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 0, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "master", + "master_branch": "master" + }, + { + "id": 10809060, + "name": "Amethyst", + "full_name": "mralexgray/Amethyst", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/Amethyst", + "description": "Tiling window manager for OS X.", + "fork": true, + "url": "https://api.github.com/repos/mralexgray/Amethyst", + "forks_url": "https://api.github.com/repos/mralexgray/Amethyst/forks", + "keys_url": "https://api.github.com/repos/mralexgray/Amethyst/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/Amethyst/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/Amethyst/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/Amethyst/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/Amethyst/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/Amethyst/events", + "assignees_url": "https://api.github.com/repos/mralexgray/Amethyst/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/Amethyst/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/Amethyst/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/Amethyst/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/Amethyst/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/Amethyst/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/Amethyst/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/Amethyst/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/Amethyst/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/Amethyst/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/Amethyst/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/Amethyst/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/Amethyst/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/Amethyst/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/Amethyst/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/Amethyst/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/Amethyst/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/Amethyst/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/Amethyst/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/Amethyst/merges", + "archive_url": "https://api.github.com/repos/mralexgray/Amethyst/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/Amethyst/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/Amethyst/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/Amethyst/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/Amethyst/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/Amethyst/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/Amethyst/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/Amethyst/releases{/id}", + "created_at": "2013-06-20T00:34:22Z", + "updated_at": "2013-06-20T00:34:22Z", + "pushed_at": "2013-06-18T02:54:11Z", + "git_url": "git://github.com/mralexgray/Amethyst.git", + "ssh_url": "git@github.com:mralexgray/Amethyst.git", + "clone_url": "https://github.com/mralexgray/Amethyst.git", + "svn_url": "https://github.com/mralexgray/Amethyst", + "homepage": "http://ianyh.github.io/Amethyst/", + "size": 12623, + "stargazers_count": 0, + "watchers_count": 0, + "language": "Objective-C", + "has_issues": false, + "has_downloads": true, + "has_wiki": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 0, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "master", + "master_branch": "master" + }, + { + "id": 3684286, + "name": "Animated-Paths", + "full_name": "mralexgray/Animated-Paths", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/Animated-Paths", + "description": "Demo project: Animating the drawing of a CGPath with CAShapeLayer.strokeEnd", + "fork": true, + "url": "https://api.github.com/repos/mralexgray/Animated-Paths", + "forks_url": "https://api.github.com/repos/mralexgray/Animated-Paths/forks", + "keys_url": "https://api.github.com/repos/mralexgray/Animated-Paths/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/Animated-Paths/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/Animated-Paths/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/Animated-Paths/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/Animated-Paths/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/Animated-Paths/events", + "assignees_url": "https://api.github.com/repos/mralexgray/Animated-Paths/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/Animated-Paths/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/Animated-Paths/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/Animated-Paths/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/Animated-Paths/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/Animated-Paths/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/Animated-Paths/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/Animated-Paths/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/Animated-Paths/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/Animated-Paths/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/Animated-Paths/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/Animated-Paths/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/Animated-Paths/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/Animated-Paths/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/Animated-Paths/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/Animated-Paths/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/Animated-Paths/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/Animated-Paths/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/Animated-Paths/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/Animated-Paths/merges", + "archive_url": "https://api.github.com/repos/mralexgray/Animated-Paths/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/Animated-Paths/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/Animated-Paths/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/Animated-Paths/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/Animated-Paths/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/Animated-Paths/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/Animated-Paths/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/Animated-Paths/releases{/id}", + "created_at": "2012-03-11T02:56:38Z", + "updated_at": "2013-01-08T04:12:21Z", + "pushed_at": "2010-12-30T20:56:51Z", + "git_url": "git://github.com/mralexgray/Animated-Paths.git", + "ssh_url": "git@github.com:mralexgray/Animated-Paths.git", + "clone_url": "https://github.com/mralexgray/Animated-Paths.git", + "svn_url": "https://github.com/mralexgray/Animated-Paths", + "homepage": "http://oleb.net/blog/2010/12/animating-drawing-of-cgpath-with-cashapelayer/", + "size": 411, + "stargazers_count": 1, + "watchers_count": 1, + "language": "Objective-C", + "has_issues": false, + "has_downloads": true, + "has_wiki": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 0, + "forks": 0, + "open_issues": 0, + "watchers": 1, + "default_branch": "master", + "master_branch": "master" + }, + { + "id": 16662874, + "name": "AnsiLove.framework", + "full_name": "mralexgray/AnsiLove.framework", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/AnsiLove.framework", + "description": "Cocoa Framework for rendering ANSi / ASCII art", + "fork": true, + "url": "https://api.github.com/repos/mralexgray/AnsiLove.framework", + "forks_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/forks", + "keys_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/events", + "assignees_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/merges", + "archive_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/AnsiLove.framework/releases{/id}", + "created_at": "2014-02-09T08:30:27Z", + "updated_at": "2014-02-09T08:30:32Z", + "pushed_at": "2013-10-04T14:08:38Z", + "git_url": "git://github.com/mralexgray/AnsiLove.framework.git", + "ssh_url": "git@github.com:mralexgray/AnsiLove.framework.git", + "clone_url": "https://github.com/mralexgray/AnsiLove.framework.git", + "svn_url": "https://github.com/mralexgray/AnsiLove.framework", + "homepage": "http://byteproject.net", + "size": 3780, + "stargazers_count": 0, + "watchers_count": 0, + "language": "M", + "has_issues": false, + "has_downloads": true, + "has_wiki": false, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 0, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "master", + "master_branch": "master" + }, + { + "id": 5189563, + "name": "ANTrackBar", + "full_name": "mralexgray/ANTrackBar", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/ANTrackBar", + "description": "An easy-to-use Cocoa seek bar with a pleasing appearance", + "fork": true, + "url": "https://api.github.com/repos/mralexgray/ANTrackBar", + "forks_url": "https://api.github.com/repos/mralexgray/ANTrackBar/forks", + "keys_url": "https://api.github.com/repos/mralexgray/ANTrackBar/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/ANTrackBar/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/ANTrackBar/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/ANTrackBar/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/ANTrackBar/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/ANTrackBar/events", + "assignees_url": "https://api.github.com/repos/mralexgray/ANTrackBar/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/ANTrackBar/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/ANTrackBar/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/ANTrackBar/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/ANTrackBar/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/ANTrackBar/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/ANTrackBar/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/ANTrackBar/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/ANTrackBar/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/ANTrackBar/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/ANTrackBar/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/ANTrackBar/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/ANTrackBar/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/ANTrackBar/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/ANTrackBar/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/ANTrackBar/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/ANTrackBar/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/ANTrackBar/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/ANTrackBar/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/ANTrackBar/merges", + "archive_url": "https://api.github.com/repos/mralexgray/ANTrackBar/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/ANTrackBar/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/ANTrackBar/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/ANTrackBar/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/ANTrackBar/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/ANTrackBar/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/ANTrackBar/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/ANTrackBar/releases{/id}", + "created_at": "2012-07-26T08:17:22Z", + "updated_at": "2013-01-11T10:29:56Z", + "pushed_at": "2012-03-09T01:40:02Z", + "git_url": "git://github.com/mralexgray/ANTrackBar.git", + "ssh_url": "git@github.com:mralexgray/ANTrackBar.git", + "clone_url": "https://github.com/mralexgray/ANTrackBar.git", + "svn_url": "https://github.com/mralexgray/ANTrackBar", + "homepage": "", + "size": 94, + "stargazers_count": 1, + "watchers_count": 1, + "language": "Objective-C", + "has_issues": false, + "has_downloads": true, + "has_wiki": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 0, + "forks": 0, + "open_issues": 0, + "watchers": 1, + "default_branch": "master", + "master_branch": "master" + }, + { + "id": 16240152, + "name": "AOP-in-Objective-C", + "full_name": "mralexgray/AOP-in-Objective-C", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/AOP-in-Objective-C", + "description": "An NSProxy based library for easily enabling AOP like functionality in Objective-C.", + "fork": true, + "url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C", + "forks_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/forks", + "keys_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/events", + "assignees_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/merges", + "archive_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/AOP-in-Objective-C/releases{/id}", + "created_at": "2014-01-25T21:18:04Z", + "updated_at": "2014-02-12T16:23:21Z", + "pushed_at": "2014-02-12T16:23:20Z", + "git_url": "git://github.com/mralexgray/AOP-in-Objective-C.git", + "ssh_url": "git@github.com:mralexgray/AOP-in-Objective-C.git", + "clone_url": "https://github.com/mralexgray/AOP-in-Objective-C.git", + "svn_url": "https://github.com/mralexgray/AOP-in-Objective-C", + "homepage": "http://innoli.hu/en/opensource/", + "size": 340, + "stargazers_count": 0, + "watchers_count": 0, + "language": "Objective-C", + "has_issues": false, + "has_downloads": true, + "has_wiki": true, + "forks_count": 1, + "mirror_url": null, + "open_issues_count": 0, + "forks": 1, + "open_issues": 0, + "watchers": 0, + "default_branch": "travis-coveralls", + "master_branch": "travis-coveralls" + }, + { + "id": 13141936, + "name": "Apaxy", + "full_name": "mralexgray/Apaxy", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/Apaxy", + "description": "A simple, customisable theme for your Apache directory listing.", + "fork": true, + "url": "https://api.github.com/repos/mralexgray/Apaxy", + "forks_url": "https://api.github.com/repos/mralexgray/Apaxy/forks", + "keys_url": "https://api.github.com/repos/mralexgray/Apaxy/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/Apaxy/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/Apaxy/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/Apaxy/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/Apaxy/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/Apaxy/events", + "assignees_url": "https://api.github.com/repos/mralexgray/Apaxy/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/Apaxy/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/Apaxy/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/Apaxy/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/Apaxy/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/Apaxy/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/Apaxy/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/Apaxy/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/Apaxy/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/Apaxy/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/Apaxy/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/Apaxy/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/Apaxy/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/Apaxy/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/Apaxy/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/Apaxy/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/Apaxy/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/Apaxy/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/Apaxy/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/Apaxy/merges", + "archive_url": "https://api.github.com/repos/mralexgray/Apaxy/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/Apaxy/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/Apaxy/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/Apaxy/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/Apaxy/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/Apaxy/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/Apaxy/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/Apaxy/releases{/id}", + "created_at": "2013-09-27T05:05:35Z", + "updated_at": "2013-09-27T05:05:36Z", + "pushed_at": "2013-08-02T16:01:32Z", + "git_url": "git://github.com/mralexgray/Apaxy.git", + "ssh_url": "git@github.com:mralexgray/Apaxy.git", + "clone_url": "https://github.com/mralexgray/Apaxy.git", + "svn_url": "https://github.com/mralexgray/Apaxy", + "homepage": null, + "size": 113, + "stargazers_count": 0, + "watchers_count": 0, + "language": "CSS", + "has_issues": false, + "has_downloads": true, + "has_wiki": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 0, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "master", + "master_branch": "master" + }, + { + "id": 10048098, + "name": "appledoc", + "full_name": "mralexgray/appledoc", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/appledoc", + "description": "Objective-c code Apple style documentation set generator.", + "fork": true, + "url": "https://api.github.com/repos/mralexgray/appledoc", + "forks_url": "https://api.github.com/repos/mralexgray/appledoc/forks", + "keys_url": "https://api.github.com/repos/mralexgray/appledoc/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/appledoc/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/appledoc/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/appledoc/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/appledoc/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/appledoc/events", + "assignees_url": "https://api.github.com/repos/mralexgray/appledoc/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/appledoc/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/appledoc/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/appledoc/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/appledoc/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/appledoc/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/appledoc/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/appledoc/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/appledoc/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/appledoc/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/appledoc/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/appledoc/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/appledoc/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/appledoc/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/appledoc/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/appledoc/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/appledoc/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/appledoc/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/appledoc/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/appledoc/merges", + "archive_url": "https://api.github.com/repos/mralexgray/appledoc/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/appledoc/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/appledoc/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/appledoc/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/appledoc/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/appledoc/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/appledoc/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/appledoc/releases{/id}", + "created_at": "2013-05-14T05:45:44Z", + "updated_at": "2014-02-09T08:14:42Z", + "pushed_at": "2014-02-09T08:14:42Z", + "git_url": "git://github.com/mralexgray/appledoc.git", + "ssh_url": "git@github.com:mralexgray/appledoc.git", + "clone_url": "https://github.com/mralexgray/appledoc.git", + "svn_url": "https://github.com/mralexgray/appledoc", + "homepage": "http://gentlebytes.com", + "size": 10336, + "stargazers_count": 0, + "watchers_count": 0, + "language": "Objective-C", + "has_issues": false, + "has_downloads": false, + "has_wiki": false, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 0, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "master", + "master_branch": "master" + }, + { + "id": 16160992, + "name": "Appstore-Through-Terminal", + "full_name": "mralexgray/Appstore-Through-Terminal", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/Appstore-Through-Terminal", + "description": "A simple debian package, made for iPhone, to open the iOS AppStore through terminal. Simple. Slightly usless. My work as a beginner.", + "fork": true, + "url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal", + "forks_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/forks", + "keys_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/events", + "assignees_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/merges", + "archive_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/Appstore-Through-Terminal/releases{/id}", + "created_at": "2014-01-23T03:21:34Z", + "updated_at": "2014-01-23T03:21:34Z", + "pushed_at": "2011-12-21T06:40:25Z", + "git_url": "git://github.com/mralexgray/Appstore-Through-Terminal.git", + "ssh_url": "git@github.com:mralexgray/Appstore-Through-Terminal.git", + "clone_url": "https://github.com/mralexgray/Appstore-Through-Terminal.git", + "svn_url": "https://github.com/mralexgray/Appstore-Through-Terminal", + "homepage": "http://getagripon.com/tillie/projects.html", + "size": 104, + "stargazers_count": 0, + "watchers_count": 0, + "language": "Shell", + "has_issues": false, + "has_downloads": true, + "has_wiki": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 0, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "master", + "master_branch": "master" + }, + { + "id": 13709216, + "name": "appweb-4", + "full_name": "mralexgray/appweb-4", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/appweb-4", + "description": "Appweb Embeddable Web Server 4", + "fork": true, + "url": "https://api.github.com/repos/mralexgray/appweb-4", + "forks_url": "https://api.github.com/repos/mralexgray/appweb-4/forks", + "keys_url": "https://api.github.com/repos/mralexgray/appweb-4/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/appweb-4/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/appweb-4/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/appweb-4/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/appweb-4/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/appweb-4/events", + "assignees_url": "https://api.github.com/repos/mralexgray/appweb-4/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/appweb-4/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/appweb-4/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/appweb-4/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/appweb-4/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/appweb-4/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/appweb-4/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/appweb-4/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/appweb-4/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/appweb-4/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/appweb-4/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/appweb-4/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/appweb-4/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/appweb-4/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/appweb-4/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/appweb-4/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/appweb-4/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/appweb-4/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/appweb-4/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/appweb-4/merges", + "archive_url": "https://api.github.com/repos/mralexgray/appweb-4/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/appweb-4/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/appweb-4/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/appweb-4/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/appweb-4/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/appweb-4/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/appweb-4/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/appweb-4/releases{/id}", + "created_at": "2013-10-19T21:36:10Z", + "updated_at": "2013-10-19T21:36:11Z", + "pushed_at": "2013-10-19T00:35:06Z", + "git_url": "git://github.com/mralexgray/appweb-4.git", + "ssh_url": "git@github.com:mralexgray/appweb-4.git", + "clone_url": "https://github.com/mralexgray/appweb-4.git", + "svn_url": "https://github.com/mralexgray/appweb-4", + "homepage": "http://appwebserver.org", + "size": 58244, + "stargazers_count": 0, + "watchers_count": 0, + "language": "C", + "has_issues": false, + "has_downloads": true, + "has_wiki": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 0, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "master", + "master_branch": "master" + }, + { + "id": 12501983, + "name": "arbor", + "full_name": "mralexgray/arbor", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/arbor", + "description": "a graph visualization library using web workers and jQuery", + "fork": true, + "url": "https://api.github.com/repos/mralexgray/arbor", + "forks_url": "https://api.github.com/repos/mralexgray/arbor/forks", + "keys_url": "https://api.github.com/repos/mralexgray/arbor/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/arbor/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/arbor/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/arbor/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/arbor/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/arbor/events", + "assignees_url": "https://api.github.com/repos/mralexgray/arbor/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/arbor/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/arbor/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/arbor/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/arbor/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/arbor/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/arbor/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/arbor/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/arbor/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/arbor/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/arbor/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/arbor/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/arbor/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/arbor/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/arbor/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/arbor/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/arbor/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/arbor/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/arbor/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/arbor/merges", + "archive_url": "https://api.github.com/repos/mralexgray/arbor/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/arbor/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/arbor/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/arbor/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/arbor/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/arbor/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/arbor/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/arbor/releases{/id}", + "created_at": "2013-08-31T07:07:05Z", + "updated_at": "2013-08-31T07:07:06Z", + "pushed_at": "2012-05-28T00:47:58Z", + "git_url": "git://github.com/mralexgray/arbor.git", + "ssh_url": "git@github.com:mralexgray/arbor.git", + "clone_url": "https://github.com/mralexgray/arbor.git", + "svn_url": "https://github.com/mralexgray/arbor", + "homepage": "http://arborjs.org", + "size": 237, + "stargazers_count": 0, + "watchers_count": 0, + "language": "JavaScript", + "has_issues": false, + "has_downloads": true, + "has_wiki": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 0, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "master", + "master_branch": "master" + }, + { + "id": 13537888, + "name": "Archimedes", + "full_name": "mralexgray/Archimedes", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/Archimedes", + "description": "Geometry functions for Cocoa and Cocoa Touch", + "fork": true, + "url": "https://api.github.com/repos/mralexgray/Archimedes", + "forks_url": "https://api.github.com/repos/mralexgray/Archimedes/forks", + "keys_url": "https://api.github.com/repos/mralexgray/Archimedes/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/Archimedes/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/Archimedes/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/Archimedes/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/Archimedes/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/Archimedes/events", + "assignees_url": "https://api.github.com/repos/mralexgray/Archimedes/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/Archimedes/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/Archimedes/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/Archimedes/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/Archimedes/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/Archimedes/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/Archimedes/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/Archimedes/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/Archimedes/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/Archimedes/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/Archimedes/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/Archimedes/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/Archimedes/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/Archimedes/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/Archimedes/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/Archimedes/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/Archimedes/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/Archimedes/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/Archimedes/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/Archimedes/merges", + "archive_url": "https://api.github.com/repos/mralexgray/Archimedes/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/Archimedes/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/Archimedes/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/Archimedes/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/Archimedes/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/Archimedes/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/Archimedes/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/Archimedes/releases{/id}", + "created_at": "2013-10-13T11:08:19Z", + "updated_at": "2014-04-06T00:41:21Z", + "pushed_at": "2014-04-06T00:41:20Z", + "git_url": "git://github.com/mralexgray/Archimedes.git", + "ssh_url": "git@github.com:mralexgray/Archimedes.git", + "clone_url": "https://github.com/mralexgray/Archimedes.git", + "svn_url": "https://github.com/mralexgray/Archimedes", + "homepage": null, + "size": 217, + "stargazers_count": 0, + "watchers_count": 0, + "language": "Objective-C", + "has_issues": false, + "has_downloads": true, + "has_wiki": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 0, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "master", + "master_branch": "master" + }, + { + "id": 5260205, + "name": "arrsync", + "full_name": "mralexgray/arrsync", + "owner": { + "login": "mralexgray", + "id": 262517, + "avatar_url": "https://avatars.githubusercontent.com/u/262517?", + "gravatar_id": "50e7ed4eb2e7af000ea7d161748958f1", + "url": "https://api.github.com/users/mralexgray", + "html_url": "https://github.com/mralexgray", + "followers_url": "https://api.github.com/users/mralexgray/followers", + "following_url": "https://api.github.com/users/mralexgray/following{/other_user}", + "gists_url": "https://api.github.com/users/mralexgray/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mralexgray/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mralexgray/subscriptions", + "organizations_url": "https://api.github.com/users/mralexgray/orgs", + "repos_url": "https://api.github.com/users/mralexgray/repos", + "events_url": "https://api.github.com/users/mralexgray/events{/privacy}", + "received_events_url": "https://api.github.com/users/mralexgray/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/mralexgray/arrsync", + "description": "", + "fork": true, + "url": "https://api.github.com/repos/mralexgray/arrsync", + "forks_url": "https://api.github.com/repos/mralexgray/arrsync/forks", + "keys_url": "https://api.github.com/repos/mralexgray/arrsync/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/mralexgray/arrsync/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/mralexgray/arrsync/teams", + "hooks_url": "https://api.github.com/repos/mralexgray/arrsync/hooks", + "issue_events_url": "https://api.github.com/repos/mralexgray/arrsync/issues/events{/number}", + "events_url": "https://api.github.com/repos/mralexgray/arrsync/events", + "assignees_url": "https://api.github.com/repos/mralexgray/arrsync/assignees{/user}", + "branches_url": "https://api.github.com/repos/mralexgray/arrsync/branches{/branch}", + "tags_url": "https://api.github.com/repos/mralexgray/arrsync/tags", + "blobs_url": "https://api.github.com/repos/mralexgray/arrsync/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/mralexgray/arrsync/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/mralexgray/arrsync/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/mralexgray/arrsync/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/mralexgray/arrsync/statuses/{sha}", + "languages_url": "https://api.github.com/repos/mralexgray/arrsync/languages", + "stargazers_url": "https://api.github.com/repos/mralexgray/arrsync/stargazers", + "contributors_url": "https://api.github.com/repos/mralexgray/arrsync/contributors", + "subscribers_url": "https://api.github.com/repos/mralexgray/arrsync/subscribers", + "subscription_url": "https://api.github.com/repos/mralexgray/arrsync/subscription", + "commits_url": "https://api.github.com/repos/mralexgray/arrsync/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/mralexgray/arrsync/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/mralexgray/arrsync/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/mralexgray/arrsync/issues/comments/{number}", + "contents_url": "https://api.github.com/repos/mralexgray/arrsync/contents/{+path}", + "compare_url": "https://api.github.com/repos/mralexgray/arrsync/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/mralexgray/arrsync/merges", + "archive_url": "https://api.github.com/repos/mralexgray/arrsync/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/mralexgray/arrsync/downloads", + "issues_url": "https://api.github.com/repos/mralexgray/arrsync/issues{/number}", + "pulls_url": "https://api.github.com/repos/mralexgray/arrsync/pulls{/number}", + "milestones_url": "https://api.github.com/repos/mralexgray/arrsync/milestones{/number}", + "notifications_url": "https://api.github.com/repos/mralexgray/arrsync/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/mralexgray/arrsync/labels{/name}", + "releases_url": "https://api.github.com/repos/mralexgray/arrsync/releases{/id}", + "created_at": "2012-08-01T14:21:49Z", + "updated_at": "2013-01-11T13:12:21Z", + "pushed_at": "2011-05-09T18:56:56Z", + "git_url": "git://github.com/mralexgray/arrsync.git", + "ssh_url": "git@github.com:mralexgray/arrsync.git", + "clone_url": "https://github.com/mralexgray/arrsync.git", + "svn_url": "https://github.com/mralexgray/arrsync", + "homepage": "", + "size": 194, + "stargazers_count": 2, + "watchers_count": 2, + "language": "Objective-C", + "has_issues": false, + "has_downloads": true, + "has_wiki": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 0, + "forks": 0, + "open_issues": 0, + "watchers": 2, + "default_branch": "master", + "master_branch": "master" + } +] +}
\ No newline at end of file diff --git a/libccnx-common/ccnx/common/test/test_ccnx_ContentObject.c b/libccnx-common/ccnx/common/test/test_ccnx_ContentObject.c new file mode 100755 index 00000000..20ed8c13 --- /dev/null +++ b/libccnx-common/ccnx/common/test/test_ccnx_ContentObject.c @@ -0,0 +1,634 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../ccnx_ContentObject.c" + +#include <parc/algol/parc_SafeMemory.h> +#include <LongBow/unit-test.h> + +#include <inttypes.h> +#include <stdio.h> + +typedef struct test_data { + CCNxContentObjectInterface impl; + CCNxName *name; + CCNxContentObject *contentObject; + CCNxContentObject *namelessContentObject; +} TestData; + +LONGBOW_TEST_RUNNER(ccnx_ContentObject) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(EmptyImpl); +} + +static TestData * +_commonSetup(void) +{ + TestData *data = parcMemory_AllocateAndClear(sizeof(TestData)); + + CCNxName *name = ccnxName_CreateFromCString("ccnx:/default/testData/content"); + PARCBuffer *payload = parcBuffer_WrapCString("hello"); + + data->impl = CCNxContentObjectFacadeV1_Implementation; + data->name = name; + data->contentObject = ccnxContentObject_CreateWithImplAndPayload(&data->impl, name, CCNxPayloadType_DATA, payload); + data->namelessContentObject = ccnxContentObject_CreateWithImplAndPayload(&data->impl, NULL, CCNxPayloadType_DATA, payload); + + parcBuffer_Release(&payload); + return data; +} + +static void +_commonTeardown(TestData *data) +{ + if (data->contentObject) { + ccnxContentObject_Release(&data->contentObject); + } + if (data->namelessContentObject) { + ccnxContentObject_Release(&data->namelessContentObject); + } + if (data->name) { + ccnxName_Release(&data->name); + } + + parcMemory_Deallocate((void **) &data); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(ccnx_ContentObject) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(ccnx_ContentObject) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, ccnxContentObject_Equals); + LONGBOW_RUN_TEST_CASE(Global, ccnxContentObject_SetSignature); + LONGBOW_RUN_TEST_CASE(Global, ccnxContentObject_GetKeyId); + LONGBOW_RUN_TEST_CASE(Global, ccnxContentObject_CreateWithNameAndPayload); + LONGBOW_RUN_TEST_CASE(Global, ccnxContentObject_CreateWithPayload); + + LONGBOW_RUN_TEST_CASE(Global, ccnxContentObject_HasFinalChunkNumber); + LONGBOW_RUN_TEST_CASE(Global, ccnxContentObject_GetSetFinalChunkNumber); + + LONGBOW_RUN_TEST_CASE(Global, ccnxContentObject_GetName); + LONGBOW_RUN_TEST_CASE(Global, ccnxContentObject_GetNameWithNameless); + LONGBOW_RUN_TEST_CASE(Global, ccnxContentObject_GetPayload); + LONGBOW_RUN_TEST_CASE(Global, ccnxContentObject_GetPayloadType); + LONGBOW_RUN_TEST_CASE(Global, ccnxContentObject_AcquireRelease); + + LONGBOW_RUN_TEST_CASE(Global, ccnxContentObject_HasExpiryTime); + LONGBOW_RUN_TEST_CASE(Global, ccnxContentObject_SetGetExpiryTime); + + LONGBOW_RUN_TEST_CASE(Global, ccnxContentObject_Equals); + LONGBOW_RUN_TEST_CASE(Global, ccnxContentObject_Display); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + longBowTestCase_SetClipBoardData(testCase, _commonSetup()); + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + _commonTeardown(longBowTestCase_GetClipBoardData(testCase)); + + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDOUT_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + + +LONGBOW_TEST_CASE(Global, ccnxContentObject_CreateWithNameAndPayload) +{ + CCNxName *name = ccnxName_CreateFromCString("ccnx:/foo/bar"); + PARCBuffer *payload = parcBuffer_Allocate(100); + + CCNxContentObject *contentObject = ccnxContentObject_CreateWithNameAndPayload(name, payload); + ccnxContentObject_AssertValid(contentObject); + + ccnxName_Release(&name); + parcBuffer_Release(&payload); + ccnxContentObject_Release(&contentObject); +} + +LONGBOW_TEST_CASE(Global, ccnxContentObject_CreateWithPayload) +{ + PARCBuffer *payload = parcBuffer_Allocate(100); + + CCNxContentObject *contentObject = ccnxContentObject_CreateWithPayload(payload); + ccnxContentObject_AssertValid(contentObject); + + parcBuffer_Release(&payload); + ccnxContentObject_Release(&contentObject); +} + + +LONGBOW_TEST_CASE(Global, ccnxContentObject_Equals) +{ + CCNxName *nameA = ccnxName_CreateFromCString("ccnx:/foo/bar/A"); + PARCBuffer *payloadA = parcBuffer_Allocate(100); + + CCNxContentObject *objectA = ccnxContentObject_CreateWithNameAndPayload(nameA, payloadA); + ccnxContentObject_AssertValid(objectA); + + assertTrue(ccnxContentObject_Equals(objectA, objectA), "Expected same instance to be equal"); + + CCNxContentObject *objectA2 = ccnxContentObject_CreateWithNameAndPayload(nameA, payloadA); + ccnxContentObject_AssertValid(objectA2); + + assertTrue(ccnxContentObject_Equals(objectA, objectA2), "Expected ContentObject with same payload and name to be equal"); + + CCNxName *nameB = ccnxName_CreateFromCString("ccnx:/foo/bar/B"); + CCNxContentObject *objectB = ccnxContentObject_CreateWithNameAndPayload(nameB, payloadA); + ccnxContentObject_AssertValid(objectB); + + assertFalse(ccnxContentObject_Equals(objectA, objectB), "Expected ContentObject with same payload and different name"); + + ccnxName_Release(&nameA); + ccnxName_Release(&nameB); + parcBuffer_Release(&payloadA); + + ccnxContentObject_Release(&objectA); + ccnxContentObject_Release(&objectA2); + ccnxContentObject_Release(&objectB); +} + +LONGBOW_TEST_CASE(Global, ccnxContentObject_AcquireRelease) +{ + CCNxName *name = ccnxName_CreateFromCString("ccnx:/foo/bar"); + PARCBuffer *payload = parcBuffer_Allocate(100); + + CCNxContentObject *contentObject = ccnxContentObject_CreateWithNameAndPayload(name, payload); + ccnxContentObject_AssertValid(contentObject); + + CCNxContentObject *reference = ccnxContentObject_Acquire(contentObject); + assertTrue(reference == contentObject, "Expected acquired reference to be equal to original"); + + ccnxName_Release(&name); + parcBuffer_Release(&payload); + + ccnxContentObject_AssertValid(contentObject); + ccnxContentObject_AssertValid(reference); + + ccnxContentObject_Release(&contentObject); + + assertTrue(contentObject == NULL, "Expected contentObject pointer to be null"); + ccnxContentObject_AssertValid(reference); + + ccnxContentObject_Release(&reference); + + assertTrue(reference == NULL, "Expected contentObject pointer to be null"); +} + +LONGBOW_TEST_CASE(Global, ccnxContentObject_HasFinalChunkNumber) +{ + CCNxName *name = ccnxName_CreateFromCString("ccnx:/foo/bar"); + PARCBuffer *payload = parcBuffer_Allocate(100); + + CCNxContentObject *contentObject = ccnxContentObject_CreateWithNameAndPayload(name, payload); + assertFalse(ccnxContentObject_HasFinalChunkNumber(contentObject), "Expected no final chunk number"); + + ccnxContentObject_SetFinalChunkNumber(contentObject, 100); + ccnxContentObject_AssertValid(contentObject); + assertTrue(ccnxContentObject_HasFinalChunkNumber(contentObject), "Expected HasFinalChunkNumber to return true"); + assertTrue(ccnxContentObject_GetFinalChunkNumber(contentObject) == 100, "Expected final chunk number to be 100"); + + ccnxName_Release(&name); + parcBuffer_Release(&payload); + ccnxContentObject_Release(&contentObject); +} + +LONGBOW_TEST_CASE(Global, ccnxContentObject_GetSetFinalChunkNumber) +{ + CCNxName *name = ccnxName_CreateFromCString("ccnx:/foo/bar"); + PARCBuffer *payload = parcBuffer_Allocate(100); + + CCNxContentObject *contentObject = ccnxContentObject_CreateWithNameAndPayload(name, payload); + + ccnxContentObject_SetFinalChunkNumber(contentObject, 100); + ccnxContentObject_AssertValid(contentObject); + assertTrue(ccnxContentObject_GetFinalChunkNumber(contentObject) == 100, "Expected final chunk number to be 100"); + + ccnxContentObject_SetFinalChunkNumber(contentObject, 20010); + ccnxContentObject_AssertValid(contentObject); + assertTrue(ccnxContentObject_GetFinalChunkNumber(contentObject) == 20010, "Expected final chunk number to be 20010"); + + ccnxName_Release(&name); + parcBuffer_Release(&payload); + ccnxContentObject_Release(&contentObject); +} + +LONGBOW_TEST_CASE(Global, ccnxContentObject_GetName) +{ + CCNxName *name = ccnxName_CreateFromCString("ccnx:/foo/bar/baz"); + PARCBuffer *payload = parcBuffer_Allocate(100); + + CCNxContentObject *contentObject = ccnxContentObject_CreateWithNameAndPayload(name, payload); + ccnxContentObject_AssertValid(contentObject); + + CCNxName *actual = ccnxContentObject_GetName(contentObject); + + assertTrue(actual == name, "Expected GetName() to return the original CCNxName"); + + ccnxName_Release(&name); + parcBuffer_Release(&payload); + ccnxContentObject_Release(&contentObject); +} + +LONGBOW_TEST_CASE(Global, ccnxContentObject_GetNameWithNameless) +{ + PARCBuffer *payload = parcBuffer_Allocate(100); + + CCNxContentObject *contentObject = ccnxContentObject_CreateWithPayload(payload); + ccnxContentObject_AssertValid(contentObject); + + CCNxName *actual = ccnxContentObject_GetName(contentObject); + + assertNull(actual, "Nameless CCNxContentObjects have no name and must therefore be null."); + + parcBuffer_Release(&payload); + ccnxContentObject_Release(&contentObject); +} + +LONGBOW_TEST_CASE(Global, ccnxContentObject_GetPayload) +{ + CCNxName *name = ccnxName_CreateFromCString("ccnx:/foo/bar"); + PARCBuffer *payload = parcBuffer_Allocate(100); + + CCNxContentObject *contentObject = ccnxContentObject_CreateWithNameAndPayload(name, payload); + ccnxContentObject_AssertValid(contentObject); + + PARCBuffer *actual = ccnxContentObject_GetPayload(contentObject); + + assertTrue(actual == payload, "Expected GetPayload() to return the original PARCBuffer"); + + ccnxName_Release(&name); + parcBuffer_Release(&payload); + ccnxContentObject_Release(&contentObject); +} + +LONGBOW_TEST_CASE(Global, ccnxContentObject_GetPayloadType) +{ + CCNxName *name = ccnxName_CreateFromCString("ccnx:/name"); + PARCBuffer *payload = parcBuffer_Allocate(100); + + CCNxPayloadType types[] = { + CCNxPayloadType_DATA, + CCNxPayloadType_KEY, + CCNxPayloadType_LINK, + CCNxPayloadType_MANIFEST, + }; + + + for (int i = 0; i < sizeof(types) / sizeof(CCNxPayloadType); i++) { + CCNxPayloadType type = types[i]; + CCNxContentObject *contentObject = ccnxContentObject_CreateWithNameAndPayload(name, NULL); + ccnxContentObject_SetPayload(contentObject, type, payload); + + assertTrue(ccnxContentObject_GetPayloadType(contentObject) == type, "Unexpected PayloadType"); + ccnxContentObject_Release(&contentObject); + } + + parcBuffer_Release(&payload); + ccnxName_Release(&name); +} + + +LONGBOW_TEST_CASE(Global, ccnxContentObject_SetSignature) +{ + CCNxName *name = ccnxName_CreateFromCString("ccnx:/hello/dolly"); + PARCBuffer *payload = parcBuffer_WrapCString("hello"); + + CCNxContentObject *contentObject = ccnxContentObject_CreateWithNameAndPayload(name, payload); + + PARCBuffer *keyId = parcBuffer_WrapCString("keyhash"); + PARCBuffer *sigbits = parcBuffer_WrapCString("siggybits"); + PARCSignature *signature = parcSignature_Create(PARCSigningAlgorithm_RSA, PARCCryptoHashType_SHA256, parcBuffer_Flip(sigbits)); + + ccnxContentObject_SetSignature(contentObject, keyId, signature, NULL); + + parcBuffer_Release(&payload); + parcBuffer_Release(&sigbits); + parcBuffer_Release(&keyId); + parcSignature_Release(&signature); + ccnxName_Release(&name); + ccnxContentObject_Release(&contentObject); +} + +LONGBOW_TEST_CASE(Global, ccnxContentObject_GetKeyId) +{ + CCNxName *name = ccnxName_CreateFromCString("ccnx:/hello/dolly"); + PARCBuffer *payload = parcBuffer_WrapCString("hello"); + + CCNxContentObject *contentObject = ccnxContentObject_CreateWithNameAndPayload(name, payload); + + assertNull(ccnxContentObject_GetKeyId(contentObject), "Expect NULL for KeyId here"); + + PARCBuffer *testKeyId = parcBuffer_WrapCString("keyhash"); + PARCBuffer *sigbits = parcBuffer_WrapCString("siggybits"); + PARCSignature *signature = parcSignature_Create(PARCSigningAlgorithm_RSA, PARCCryptoHashType_SHA256, parcBuffer_Flip(sigbits)); + + ccnxContentObject_SetSignature(contentObject, testKeyId, signature, NULL); + + PARCBuffer *keyId = ccnxContentObject_GetKeyId(contentObject); + + assertTrue(parcBuffer_Equals(keyId, testKeyId), "Expect key ids to match"); + + parcBuffer_Release(&payload); + parcBuffer_Release(&sigbits); + parcBuffer_Release(&keyId); + parcSignature_Release(&signature); + ccnxName_Release(&name); + ccnxContentObject_Release(&contentObject); +} + +LONGBOW_TEST_CASE(Global, ccnxContentObject_HasExpiryTime) +{ + CCNxName *name = ccnxName_CreateFromCString("ccnx:/hello/dolly"); + PARCBuffer *payload = parcBuffer_WrapCString("hello"); + + // Use a V1 ContentObject, as V0 doesn't support ExpiryTime + CCNxContentObject *contentObject = + ccnxContentObject_CreateWithImplAndPayload(&CCNxContentObjectFacadeV1_Implementation, + name, CCNxPayloadType_DATA, payload); + + + assertFalse(ccnxContentObject_HasExpiryTime(contentObject), "Expected no expiration time by default"); + + parcBuffer_Release(&payload); + ccnxName_Release(&name); + ccnxContentObject_Release(&contentObject); +} + +LONGBOW_TEST_CASE(Global, ccnxContentObject_SetGetExpiryTime) +{ + CCNxName *name = ccnxName_CreateFromCString("ccnx:/hello/dolly"); + PARCBuffer *payload = parcBuffer_WrapCString("hello"); + + // Use a V1 ContentObject, as V0 doesn't support ExpiryTime + CCNxContentObject *contentObject = + ccnxContentObject_CreateWithImplAndPayload(&CCNxContentObjectFacadeV1_Implementation, + name, CCNxPayloadType_DATA, payload); + + assertFalse(ccnxContentObject_HasExpiryTime(contentObject), "Expected no expiration time by default"); + + uint64_t expiryTime = 1010101ULL; + ccnxContentObject_SetExpiryTime(contentObject, expiryTime); + + assertTrue(ccnxContentObject_HasExpiryTime(contentObject), "Expected the expiryTime to be set"); + uint64_t retrievedTime = ccnxContentObject_GetExpiryTime(contentObject); + assertTrue(expiryTime == retrievedTime, "Did not retrieve expected expiryTime from ContentObject"); + + parcBuffer_Release(&payload); + ccnxName_Release(&name); + ccnxContentObject_Release(&contentObject); +} + +LONGBOW_TEST_CASE_EXPECTS(Global, ccnxContentObject_GetExpiryTimeWithNoExpiryTime, .event = &LongBowTrapUnexpectedStateEvent) +{ + CCNxName *name = ccnxName_CreateFromCString("ccnx:/hello/dolly"); + PARCBuffer *payload = parcBuffer_WrapCString("hello"); + + // Use a V1 ContentObject, as V0 doesn't support ExpiryTime + CCNxContentObject *contentObject = + ccnxContentObject_CreateWithImplAndPayload(&CCNxContentObjectFacadeV1_Implementation, + name, CCNxPayloadType_DATA, payload); + + // This should throw. + uint64_t retrievedTime = ccnxContentObject_GetExpiryTime(contentObject); + trapUnexpectedState("Expected to have thrown an exception when calling GetExpiryTime(), which returned %" PRIu64, retrievedTime); + parcBuffer_Release(&payload); + ccnxName_Release(&name); + ccnxContentObject_Release(&contentObject); +} + +LONGBOW_TEST_CASE(Global, ccnxContentObject_Display) +{ + CCNxName *name = ccnxName_CreateFromCString("ccnx:/hello/dolly"); + PARCBuffer *payload = parcBuffer_WrapCString("hello"); + + CCNxContentObject *contentObject = ccnxContentObject_CreateWithNameAndPayload(name, payload); + + ccnxContentObject_Display(contentObject, 0); + + parcBuffer_Release(&payload); + ccnxName_Release(&name); + ccnxContentObject_Release(&contentObject); +} + +/////////////////////////////////////////////////////////////////////////// +// Empty Implementation Tests +/////////////////////////////////////////////////////////////////////////// + +LONGBOW_TEST_CASE_EXPECTS(EmptyImpl, empty_GetPayloadType, .event = &LongBowTrapNotImplemented) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + data->impl.getPayloadType = NULL; + + CCNxPayloadType type = ccnxContentObject_GetPayloadType(data->contentObject); + printf("We shouldn't get here. Payload = %d", type); +} + +LONGBOW_TEST_CASE_EXPECTS(EmptyImpl, empty_GetPayload, .event = &LongBowTrapNotImplemented) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + data->impl.getPayload = NULL; + + PARCBuffer *payload = ccnxContentObject_GetPayload(data->contentObject); + printf("We shouldn't get here. Payload = %p", (void *) payload); +} + +LONGBOW_TEST_CASE_EXPECTS(EmptyImpl, empty_SetPayload, .event = &LongBowTrapNotImplemented) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + data->impl.setPayload = NULL; + + ccnxContentObject_SetPayload(data->contentObject, CCNxPayloadType_DATA, NULL); +} + +LONGBOW_TEST_CASE_EXPECTS(EmptyImpl, empty_GetName, .event = &LongBowTrapNotImplemented) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + data->impl.getName = NULL; + + CCNxName *name = ccnxContentObject_GetName(data->contentObject); + printf("We shouldn't get here. Name = %p", (void *) name); +} + +LONGBOW_TEST_CASE_EXPECTS(EmptyImpl, empty_SetFinalChunkNumber, .event = &LongBowTrapNotImplemented) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + data->impl.setFinalChunkNumber = NULL; + + ccnxContentObject_SetFinalChunkNumber(data->contentObject, 100); +} + +LONGBOW_TEST_CASE_EXPECTS(EmptyImpl, empty_GetFinalChunkNumber, .event = &LongBowTrapNotImplemented) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + data->impl.getFinalChunkNumber = NULL; + + ccnxContentObject_SetFinalChunkNumber(data->contentObject, 100); + ccnxContentObject_GetFinalChunkNumber(data->contentObject); +} + +LONGBOW_TEST_CASE_EXPECTS(EmptyImpl, empty_GetFinalChunkNumberNoHas, .event = &LongBowTrapUnexpectedStateEvent) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + data->impl.getFinalChunkNumber = NULL; + + ccnxContentObject_GetFinalChunkNumber(data->contentObject); +} + +LONGBOW_TEST_CASE_EXPECTS(EmptyImpl, empty_HasFinalChunkNumber, .event = &LongBowTrapNotImplemented) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + data->impl.hasFinalChunkNumber = NULL; + + if (ccnxContentObject_HasFinalChunkNumber(data->contentObject)) { + printf("Shouldn't get here"); + } +} + +LONGBOW_TEST_CASE(EmptyImpl, empty_HasExpiryTime) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + data->impl.hasExpiryTime = NULL; + + assertFalse(ccnxContentObject_HasExpiryTime(data->contentObject), "If no expiry time implementation, return false."); +} + +LONGBOW_TEST_CASE_EXPECTS(EmptyImpl, empty_SetExpiryTime, .event = &LongBowTrapNotImplemented) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + data->impl.setExpiryTime = NULL; + + ccnxContentObject_SetExpiryTime(data->contentObject, 100); +} + +LONGBOW_TEST_CASE_EXPECTS(EmptyImpl, empty_GetExpiryTime, .event = &LongBowTrapNotImplemented) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + data->impl.getExpiryTime = NULL; + + ccnxContentObject_SetExpiryTime(data->contentObject, 100); + uint64_t expiryTime = ccnxContentObject_GetExpiryTime(data->contentObject); + printf("We shouldn't get here, with expiryTime = %" PRIu64, expiryTime); +} + +LONGBOW_TEST_CASE_EXPECTS(EmptyImpl, empty_GetExpiryTimeNoHas, .event = &LongBowTrapUnexpectedStateEvent) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + data->impl.getExpiryTime = NULL; + + uint64_t expiryTime = ccnxContentObject_GetExpiryTime(data->contentObject); + printf("We shouldn't get here, with expiryTime = %" PRIu64, expiryTime); +} + +LONGBOW_TEST_CASE(EmptyImpl, empty_Display) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + data->impl.display = NULL; + + ccnxContentObject_Display(data->contentObject, 2); +} + +LONGBOW_TEST_CASE_EXPECTS(EmptyImpl, empty_ToString, .event = &LongBowTrapNotImplemented) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + data->impl.toString = NULL; + + const char *expectedString = ccnxContentObject_ToString(data->contentObject); + if (expectedString != NULL) { + parcMemory_Deallocate((void **) &expectedString); + } +} + +LONGBOW_TEST_CASE_EXPECTS(EmptyImpl, empty_Equals, .event = &LongBowTrapNotImplemented) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + data->impl.equals = NULL; + + if (ccnxContentObject_Equals(data->contentObject, data->contentObject)) { + printf("Shouldn't get here"); + } +} + +LONGBOW_TEST_FIXTURE(EmptyImpl) +{ + LONGBOW_RUN_TEST_CASE(EmptyImpl, empty_Display); + LONGBOW_RUN_TEST_CASE(EmptyImpl, empty_HasExpiryTime); + LONGBOW_RUN_TEST_CASE(EmptyImpl, empty_GetExpiryTime); + LONGBOW_RUN_TEST_CASE(EmptyImpl, empty_GetExpiryTimeNoHas); + LONGBOW_RUN_TEST_CASE(EmptyImpl, empty_SetExpiryTime); + LONGBOW_RUN_TEST_CASE(EmptyImpl, empty_SetFinalChunkNumber); + LONGBOW_RUN_TEST_CASE(EmptyImpl, empty_GetFinalChunkNumber); + LONGBOW_RUN_TEST_CASE(EmptyImpl, empty_GetFinalChunkNumberNoHas); + LONGBOW_RUN_TEST_CASE(EmptyImpl, empty_HasFinalChunkNumber); + LONGBOW_RUN_TEST_CASE(EmptyImpl, empty_GetPayload); + LONGBOW_RUN_TEST_CASE(EmptyImpl, empty_GetPayloadType); + LONGBOW_RUN_TEST_CASE(EmptyImpl, empty_SetPayload); + LONGBOW_RUN_TEST_CASE(EmptyImpl, empty_GetName); + LONGBOW_RUN_TEST_CASE(EmptyImpl, empty_ToString); + LONGBOW_RUN_TEST_CASE(EmptyImpl, empty_Equals); +} + +LONGBOW_TEST_FIXTURE_SETUP(EmptyImpl) +{ + longBowTestCase_SetClipBoardData(testCase, _commonSetup()); + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(EmptyImpl) +{ + _commonTeardown(longBowTestCase_GetClipBoardData(testCase)); + + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDOUT_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(ccnx_ContentObject); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-common/ccnx/common/test/test_ccnx_Interest.c b/libccnx-common/ccnx/common/test/test_ccnx_Interest.c new file mode 100755 index 00000000..29debe9c --- /dev/null +++ b/libccnx-common/ccnx/common/test/test_ccnx_Interest.c @@ -0,0 +1,725 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ +#include <config.h> + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include <ccnx/common/ccnx_Interest.c> + +#include <LongBow/unit-test.h> +#include <parc/algol/parc_SafeMemory.h> + +LONGBOW_TEST_RUNNER(ccnx_Interest) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(EmptyImpl); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(ccnx_Interest) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(ccnx_Interest) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, ccnxInterest_Create); + LONGBOW_RUN_TEST_CASE(Global, ccnxInterest_CreateSimple); + LONGBOW_RUN_TEST_CASE(Global, ccnxInterest_Release); + + LONGBOW_RUN_TEST_CASE(Global, ccnxInterest_AssertValid); + + LONGBOW_RUN_TEST_CASE(Global, ccnxInterest_Equals_Same); + LONGBOW_RUN_TEST_CASE(Global, ccnxInterest_Equals); + + LONGBOW_RUN_TEST_CASE(Global, ccnxInterest_SetLifetime); + LONGBOW_RUN_TEST_CASE(Global, ccnxInterest_GetLifetime); + + LONGBOW_RUN_TEST_CASE(Global, ccnxInterest_GetName); + + LONGBOW_RUN_TEST_CASE(Global, ccnxInterest_SetGetPayload); + LONGBOW_RUN_TEST_CASE(Global, ccnxInterest_SetPayloadWithId); + LONGBOW_RUN_TEST_CASE(Global, ccnxInterest_SetPayloadAndId); + + LONGBOW_RUN_TEST_CASE(Global, ccnxInterest_SetGetHopLimit); + + LONGBOW_RUN_TEST_CASE(Global, ccnxInterest_SetKeyIdRestriction); + LONGBOW_RUN_TEST_CASE(Global, ccnxInterest_GetKeyIdRestriction); + + LONGBOW_RUN_TEST_CASE(Global, ccnxInterest_GetContentObjectHashRestriction); + LONGBOW_RUN_TEST_CASE(Global, ccnxInterest_SetContentObjectHashRestriction); + + LONGBOW_RUN_TEST_CASE(Global, ccnxInterest_ToString); + + LONGBOW_RUN_TEST_CASE(Global, ccnxInterest_Display); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, ccnxInterest_Create) +{ + CCNxName *name = ccnxName_CreateFromCString("lci:/name"); + PARCBuffer *key = parcBuffer_Allocate(8); + parcBuffer_PutUint64(key, 1234L); + + CCNxInterest *interest = ccnxInterest_Create(name, + 15 * 1000, /* lifetime, 15 seconds in milliseconds */ + key, /* KeyId */ + NULL /* ContentObjectHash */ + ); + ccnxName_Release(&name); + parcBuffer_Release(&key); + ccnxInterest_Release(&interest); +} + +LONGBOW_TEST_CASE(Global, ccnxInterest_CreateSimple) +{ + CCNxName *name = ccnxName_CreateFromCString("lci:/name"); + CCNxInterest *interest = ccnxInterest_CreateSimple(name); + + ccnxName_Release(&name); + ccnxInterest_Release(&interest); +} + +LONGBOW_TEST_CASE(Global, ccnxInterest_Release) +{ + CCNxName *name = ccnxName_CreateFromCString("lci:/name"); + CCNxInterest *interest = ccnxInterest_CreateSimple(name); + + CCNxInterest *reference = ccnxInterest_Acquire(interest); + + ccnxName_Release(&name); + ccnxInterest_Release(&interest); + ccnxInterest_Release(&reference); + + assertNull(interest, "Expected ccnxInterest_Release to null the pointer."); + assertNull(reference, "Expected ccnxInterest_Release to null the pointer."); +} + +LONGBOW_TEST_CASE(Global, ccnxInterest_AssertValid) +{ + CCNxName *name = ccnxName_CreateFromCString("lci:/boose/roo/pie"); + CCNxInterest *interest = ccnxInterest_CreateSimple(name); + + ccnxInterest_AssertValid(interest); + + ccnxName_Release(&name); + ccnxInterest_Release(&interest); +} + +LONGBOW_TEST_CASE(Global, ccnxInterest_Equals_Same) +{ + CCNxName *nameA = ccnxName_CreateFromCString("lci:/name"); + PARCBuffer *key = parcBuffer_Allocate(8); + parcBuffer_PutUint64(key, 1234L); + + CCNxInterest *interestA = ccnxInterest_Create(nameA, + CCNxInterestDefault_LifetimeMilliseconds, /* lifetime */ + key, /* KeyId */ + NULL /* ContentObjectHash */ + ); + + assertTrue(ccnxInterest_Equals(interestA, interestA), "Expected the same interest to be equal."); + + assertFalse(ccnxInterest_Equals(interestA, NULL), "Did not expect NULL to equal an Interest"); + + + ccnxName_Release(&nameA); + parcBuffer_Release(&key); + ccnxInterest_Release(&interestA); +} + +LONGBOW_TEST_CASE(Global, ccnxInterest_Equals) +{ + CCNxName *nameA = ccnxName_CreateFromCString("lci:/name"); + PARCBuffer *keyA = parcBuffer_Allocate(8); + parcBuffer_PutUint64(keyA, 1234L); + + CCNxInterest *interestA = ccnxInterest_Create(nameA, + 1000, /* lifetime */ + keyA, /* KeyId */ + NULL /* ContentObjectHash */ + ); + + CCNxName *nameB = ccnxName_CreateFromCString("lci:/name"); + PARCBuffer *keyB = parcBuffer_Allocate(8); + parcBuffer_PutUint64(keyB, 1234L); + CCNxInterest *interestB = ccnxInterest_Create(nameB, + 1000, /* lifetime */ + keyB, /* KeyId */ + NULL /* ContentObjectHash */ + ); + + assertTrue(ccnxInterest_Equals(interestA, interestB), "Expected equivalent interests to be equal."); + + ccnxName_Release(&nameA); + ccnxName_Release(&nameB); + parcBuffer_Release(&keyA); + parcBuffer_Release(&keyB); + ccnxInterest_Release(&interestA); + ccnxInterest_Release(&interestB); +} + +LONGBOW_TEST_CASE(Global, ccnxInterest_SetLifetime) +{ + CCNxName *name = ccnxName_CreateFromCString("lci:/name"); + PARCBuffer *key = parcBuffer_Allocate(8); + parcBuffer_PutUint64(key, 1234L); + + uint32_t lifetime = 5000; // 5 seconds, in milliseconds + + CCNxInterest *interest = ccnxInterest_Create(name, + lifetime, /* lifetime */ + key, /* KeyId */ + NULL /* ContentObjectHash */ + ); + + uint32_t actual = ccnxInterest_GetLifetime(interest); + + assertTrue(actual == lifetime, "Expected the retrieved lifetime to be equal to the assigned one."); + + lifetime = 2000; + ccnxInterest_SetLifetime(interest, lifetime); + actual = ccnxInterest_GetLifetime(interest); + + assertTrue(actual == lifetime, "Expected the retrieved lifetime to be equal to the assigned one."); + + ccnxName_Release(&name); + parcBuffer_Release(&key); + ccnxInterest_Release(&interest); +} + + +LONGBOW_TEST_CASE(Global, ccnxInterest_GetLifetime) +{ + CCNxName *name = ccnxName_CreateFromCString("lci:/name"); + PARCBuffer *key = parcBuffer_Allocate(8); + parcBuffer_PutUint64(key, 1234L); + + uint32_t lifetime = 5000; // 5 seconds, in milliseconds + + CCNxInterest *interest = ccnxInterest_Create(name, + lifetime, /* lifetime */ + key, /* KeyId */ + NULL /* ContentObjectHash */ + ); + + uint32_t actual = ccnxInterest_GetLifetime(interest); + + assertTrue(actual == lifetime, "Expected the retrieved lifetime to be equal to the assigned one."); + + ccnxName_Release(&name); + parcBuffer_Release(&key); + ccnxInterest_Release(&interest); +} + +LONGBOW_TEST_CASE(Global, ccnxInterest_GetName) +{ + CCNxName *name = ccnxName_CreateFromCString("lci:/name"); + CCNxInterest *interest = ccnxInterest_CreateSimple(name); + + CCNxName *actual = ccnxInterest_GetName(interest); + assertTrue(ccnxName_Equals(name, actual), "Expected the same name."); + + ccnxName_Release(&name); + ccnxInterest_Release(&interest); +} + +LONGBOW_TEST_CASE(Global, ccnxInterest_SetKeyIdRestriction) +{ + CCNxName *name = ccnxName_CreateFromCString("lci:/name"); + PARCBuffer *key = parcBuffer_Allocate(8); + parcBuffer_PutUint64(key, 1234L); + + CCNxInterest *interest = ccnxInterest_Create(name, 3000, NULL, NULL); + ccnxInterest_SetKeyIdRestriction(interest, key); + PARCBuffer *actual = ccnxInterest_GetKeyIdRestriction(interest); + + actual = ccnxInterest_GetKeyIdRestriction(interest); + assertTrue(actual == key, "Expected retrieved key to be the same as assigned"); + + ccnxName_Release(&name); + ccnxInterest_Release(&interest); + parcBuffer_Release(&key); +} + +LONGBOW_TEST_CASE(Global, ccnxInterest_GetKeyIdRestriction) +{ + CCNxName *name = ccnxName_CreateFromCString("lci:/name"); + PARCBuffer *key = parcBuffer_Allocate(8); + parcBuffer_PutUint64(key, 1234L); + + CCNxInterest *interest = ccnxInterest_Create(name, 3000, key, NULL); + + PARCBuffer *actual = ccnxInterest_GetKeyIdRestriction(interest); + + actual = ccnxInterest_GetKeyIdRestriction(interest); + assertTrue(actual == key, "Expected retrieved key to be the same as assigned"); + + ccnxName_Release(&name); + ccnxInterest_Release(&interest); + parcBuffer_Release(&key); +} + +LONGBOW_TEST_CASE(Global, ccnxInterest_SetContentObjectHashRestriction) +{ + CCNxName *name = ccnxName_CreateFromCString("lci:/name"); + PARCBuffer *coh = parcBuffer_Allocate(8); + parcBuffer_PutUint64(coh, 77573L); + + CCNxInterest *interest = ccnxInterest_Create(name, CCNxInterestDefault_LifetimeMilliseconds, NULL, NULL); + + PARCBuffer *actual = ccnxInterest_GetContentObjectHashRestriction(interest); + assertNull(actual, "Expected retrieved ContentObjectHash to be initially NULL"); + + ccnxInterest_SetContentObjectHashRestriction(interest, coh); + actual = ccnxInterest_GetContentObjectHashRestriction(interest); + + assertTrue(actual == coh, "Expected retrieved ContentObjectHash to be the same as assigned"); + + // Re-setting is not yet supported. At the moment, you can only put the COHR once. + // Now change it, and validate. + //PARCBuffer *coh2 = parcBuffer_Allocate(8); + //parcBuffer_PutUint64(coh2, 3262L); + //ccnxInterest_SetContentObjectHashRestriction(interest, coh2); + //actual = ccnxInterest_GetContentObjectHashRestriction(interest); + //assertTrue(actual == coh2, "Expected retrieved ContentObjectHash to be the same as assigned"); + + ccnxName_Release(&name); + ccnxInterest_Release(&interest); + parcBuffer_Release(&coh); + //parcBuffer_Release(&coh2); +} + +LONGBOW_TEST_CASE(Global, ccnxInterest_GetContentObjectHashRestriction) +{ + CCNxName *name = ccnxName_CreateFromCString("lci:/name"); + PARCBuffer *coh = parcBuffer_Allocate(8); + parcBuffer_PutUint64(coh, 1234L); + + CCNxInterest *interest = ccnxInterest_Create(name, CCNxInterestDefault_LifetimeMilliseconds, NULL, coh); + + PARCBuffer *actual = ccnxInterest_GetContentObjectHashRestriction(interest); + + assertTrue(actual == coh, "Expected retrieved ContentObjectHash to be the same as assigned"); + + ccnxName_Release(&name); + ccnxInterest_Release(&interest); + parcBuffer_Release(&coh); +} + +LONGBOW_TEST_CASE(Global, ccnxInterest_SetGetPayload) +{ + CCNxName *name = ccnxName_CreateFromCString("lci:/name"); + CCNxInterest *interest = ccnxInterest_CreateSimple(name); + CCNxName *origNameCopy = ccnxName_Copy(name); + + CCNxInterestInterface *impl = ccnxInterestInterface_GetInterface(interest); + + if (impl->getPayload) { + assertNull(ccnxInterest_GetPayload(interest), "Expected a NULL payload"); + } + + if (impl->getPayload && impl->setPayload) { + PARCBuffer *payload = parcBuffer_WrapCString("impls have pimples"); + ccnxInterest_SetPayload(interest, payload); + + PARCBuffer *payloadOut = ccnxInterest_GetPayload(interest); + + assertTrue(parcBuffer_Equals(payload, payloadOut), "Expected an equal buffer"); + + CCNxName *nameAfterPayload = ccnxInterest_GetName(interest); + assertTrue(ccnxName_Equals(nameAfterPayload, origNameCopy), "Expected an unmodified name"); + + parcBuffer_Release(&payload); + } + ccnxName_Release(&name); + ccnxName_Release(&origNameCopy); + ccnxInterest_Release(&interest); +} + +LONGBOW_TEST_CASE(Global, ccnxInterest_SetPayloadAndId) +{ + CCNxName *name = ccnxName_CreateFromCString("lci:/name"); + CCNxInterest *interest = ccnxInterest_CreateSimple(name); + + CCNxInterestInterface *impl = ccnxInterestInterface_GetInterface(interest); + + if (impl->getPayload) { + assertNull(ccnxInterest_GetPayload(interest), "Expected a NULL payload"); + } + + if (impl->getPayload && impl->setPayload) { + PARCBuffer *payload = parcBuffer_WrapCString("impls have pimples"); + + ccnxInterest_SetPayloadAndId(interest, payload); + + PARCBuffer *payloadOut = ccnxInterest_GetPayload(interest); + + assertTrue(parcBuffer_Equals(payload, payloadOut), "Expected an equal buffer"); + + CCNxName *nameAfterPayload = ccnxInterest_GetName(interest); + CCNxNameSegment *segment = ccnxName_GetSegment(nameAfterPayload, ccnxName_GetSegmentCount(nameAfterPayload) - 1); + + assertTrue(ccnxNameSegment_GetType(segment) == CCNxNameLabelType_PAYLOADID, "Expected to find a payload ID appended to the name"); + + parcBuffer_Release(&payload); + } + ccnxName_Release(&name); + ccnxInterest_Release(&interest); +} + +LONGBOW_TEST_CASE(Global, ccnxInterest_SetPayloadWithId) +{ + CCNxName *name = ccnxName_CreateFromCString("lci:/name"); + CCNxInterest *interest = ccnxInterest_CreateSimple(name); + CCNxName *origNameCopy = ccnxName_Copy(name); + + CCNxInterestInterface *impl = ccnxInterestInterface_GetInterface(interest); + + if (impl->getPayload) { + assertNull(ccnxInterest_GetPayload(interest), "Expected a NULL payload"); + } + + if (impl->getPayload && impl->setPayload) { + PARCBuffer *payload = parcBuffer_WrapCString("impls have pimples"); + PARCBuffer *payloadIdBuffer = parcBuffer_WrapCString("payload Id buffer"); + + CCNxInterestPayloadId *payloadId = ccnxInterestPayloadId_Create(payloadIdBuffer, + CCNxInterestPayloadId_TypeCode_App + 2); + + ccnxInterest_SetPayloadWithId(interest, payload, payloadId); + + PARCBuffer *payloadOut = ccnxInterest_GetPayload(interest); + + assertTrue(parcBuffer_Equals(payload, payloadOut), "Expected an equal buffer"); + + CCNxName *nameAfterPayload = ccnxInterest_GetName(interest); + CCNxNameSegment *segment = ccnxName_GetSegment(nameAfterPayload, ccnxName_GetSegmentCount(nameAfterPayload) - 1); + + assertTrue(ccnxNameSegment_GetType(segment) == CCNxNameLabelType_PAYLOADID, "Expected to find a payload ID appended to the name"); + + CCNxInterestPayloadId *outId = ccnxInterestPayloadId_CreateFromSegmentInName(nameAfterPayload); + assertTrue(ccnxInterestPayloadId_Equals(outId, payloadId), "expected to see the same payload Id after setting the payload"); + + ccnxInterestPayloadId_Release(&payloadId); + ccnxInterestPayloadId_Release(&outId); + + parcBuffer_Release(&payload); + parcBuffer_Release(&payloadIdBuffer); + } + ccnxName_Release(&name); + ccnxName_Release(&origNameCopy); + ccnxInterest_Release(&interest); +} + + +LONGBOW_TEST_CASE(Global, ccnxInterest_SetGetHopLimit) +{ + CCNxName *name = ccnxName_CreateFromCString("lci:/name"); + CCNxInterest *interest = ccnxInterest_CreateSimple(name); + + CCNxInterestInterface *impl = ccnxInterestInterface_GetInterface(interest); + + if (impl->getHopLimit) { + assertTrue(ccnxInterest_GetHopLimit(interest) == CCNxInterestDefault_HopLimit, "Expected the default hop limit"); + } + + if (impl->setHopLimit && impl->getHopLimit) { + ccnxInterest_SetHopLimit(interest, 10); + assertTrue(ccnxInterest_GetHopLimit(interest) == 10, "Expected a hopLimit of 10"); + ccnxInterest_SetHopLimit(interest, 20); + assertTrue(ccnxInterest_GetHopLimit(interest) == 20, "Expected a hopLimit of 20"); + } + + ccnxName_Release(&name); + ccnxInterest_Release(&interest); +} + +LONGBOW_TEST_CASE(Global, ccnxInterest_ToString) +{ + CCNxName *name = ccnxName_CreateFromCString("lci:/name"); + CCNxInterest *interest = ccnxInterest_Create(name, + CCNxInterestDefault_LifetimeMilliseconds, /* lifetime */ + NULL, /* KeyId */ + NULL /* ContentObjectHash */ + ); + + const char *expectedString = ccnxInterest_ToString(interest); + assertNotNull(expectedString, "Expected non-null result from ccnxInterest_ToString."); + + parcMemory_Deallocate((void **) &expectedString); + ccnxName_Release(&name); + ccnxInterest_Release(&interest); +} + +LONGBOW_TEST_CASE(Global, ccnxInterest_Display) +{ + PARCBuffer *coh = parcBuffer_Allocate(8); + parcBuffer_PutUint64(coh, 7778L); + + CCNxName *name = ccnxName_CreateFromCString("lci:/name"); + CCNxInterest *interest = ccnxInterest_Create(name, + 100, /* lifetime */ + NULL, /* KeyId */ + coh); /* ContentObjectHash */ + + ccnxInterest_Display(interest, 2); + ccnxName_Release(&name); + ccnxInterest_Release(&interest); + parcBuffer_Release(&coh); +} + +/////////////////////////////////////////////////////////////////////////////// +// Empty Implementation tests +/////////////////////////////////////////////////////////////////////////////// + + +typedef struct test_data { + CCNxInterestInterface impl; + CCNxName *name; + CCNxInterest *interest; +} TestData; + + +static TestData * +_commonSetup(void) +{ + TestData *data = parcMemory_AllocateAndClear(sizeof(TestData)); + + CCNxName *name = ccnxName_CreateFromCString("lci:/default/testData/content"); + + data->impl = CCNxInterestFacadeV1_Implementation; // This copies the struct each time. + data->name = name; + data->interest = ccnxInterest_CreateWithImpl(&data->impl, name, 100, NULL, NULL, 10); + + return data; +} + +static void +_commonTeardown(TestData *data) +{ + if (data->interest) { + ccnxInterest_Release(&data->interest); + } + if (data->name) { + ccnxName_Release(&data->name); + } + + parcMemory_Deallocate((void **) &data); +} + +LONGBOW_TEST_FIXTURE_SETUP(EmptyImpl) +{ + longBowTestCase_SetClipBoardData(testCase, _commonSetup()); + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(EmptyImpl) +{ + _commonTeardown(longBowTestCase_GetClipBoardData(testCase)); + + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDOUT_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + + +LONGBOW_TEST_CASE(EmptyImpl, empty_Display) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + data->impl.display = NULL; + + ccnxInterest_Display(data->interest, 2); +} + +LONGBOW_TEST_CASE(EmptyImpl, empty_ToString) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + data->impl.toString = NULL; + + const char *expectedString = ccnxInterest_ToString(data->interest); + if (expectedString != NULL) { + parcMemory_Deallocate((void **) &expectedString); + } +} + +LONGBOW_TEST_CASE_EXPECTS(EmptyImpl, empty_GetName, .event = &LongBowTrapNotImplemented) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + data->impl.getName = NULL; + + CCNxName *name = ccnxInterest_GetName(data->interest); + printf("Shouldn't get here. name = %p\n", (void *) name); +} + +LONGBOW_TEST_CASE_EXPECTS(EmptyImpl, empty_SetHopLimit, .event = &LongBowTrapNotImplemented) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + data->impl.setHopLimit = NULL; + + if (ccnxInterest_SetHopLimit(data->interest, 10)) { + printf("Shouldn't get here"); + } +} + +LONGBOW_TEST_CASE_EXPECTS(EmptyImpl, empty_GetHopLimit, .event = &LongBowTrapNotImplemented) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + data->impl.getHopLimit = NULL; + + uint32_t hopLimit = ccnxInterest_GetHopLimit(data->interest); + printf("Shouldn't get here. hopLimit = %u\n", hopLimit); +} + +LONGBOW_TEST_CASE_EXPECTS(EmptyImpl, empty_SetLifetime, .event = &LongBowTrapNotImplemented) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + data->impl.setLifetime = NULL; + + if (ccnxInterest_SetLifetime(data->interest, 10)) { + printf("Shouldn't get here"); + } +} + +LONGBOW_TEST_CASE_EXPECTS(EmptyImpl, empty_GetLifetime, .event = &LongBowTrapNotImplemented) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + data->impl.getLifetime = NULL; + + uint32_t lifetime = ccnxInterest_GetLifetime(data->interest); + printf("Shouldn't get here. lifetime = %u\n", lifetime); +} + +LONGBOW_TEST_CASE_EXPECTS(EmptyImpl, empty_SetKeyIdRestriction, .event = &LongBowTrapNotImplemented) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + data->impl.setKeyIdRestriction = NULL; + + if (ccnxInterest_SetKeyIdRestriction(data->interest, NULL)) { + printf("Shouldn't get here"); + } +} + +LONGBOW_TEST_CASE_EXPECTS(EmptyImpl, empty_GetKeyIdRestriction, .event = &LongBowTrapNotImplemented) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + data->impl.getKeyIdRestriction = NULL; + + PARCBuffer *keyIdRestriction = ccnxInterest_GetKeyIdRestriction(data->interest); + printf("Shouldn't get here. getKeyIdRestriction = %p\n", (void *) keyIdRestriction); +} + +LONGBOW_TEST_CASE_EXPECTS(EmptyImpl, empty_SetContentObjectHashRestriction, .event = &LongBowTrapNotImplemented) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + data->impl.setContentObjectHashRestriction = NULL; + + if (ccnxInterest_SetContentObjectHashRestriction(data->interest, NULL)) { + printf("Shouldn't get here"); + } +} + +LONGBOW_TEST_CASE_EXPECTS(EmptyImpl, empty_GetContentObjectHashRestriction, .event = &LongBowTrapNotImplemented) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + data->impl.getContentObjectHashRestriction = NULL; + + PARCBuffer *restriction = ccnxInterest_GetContentObjectHashRestriction(data->interest); + printf("Shouldn't get here. restriction = %p\n", (void *) restriction); +} + +LONGBOW_TEST_CASE_EXPECTS(EmptyImpl, empty_SetPayload, .event = &LongBowTrapNotImplemented) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + data->impl.setPayload = NULL; + + if (ccnxInterest_SetPayload(data->interest, NULL)) { + printf("Shouldn't get here"); + } +} + +LONGBOW_TEST_CASE_EXPECTS(EmptyImpl, empty_GetPayload, .event = &LongBowTrapNotImplemented) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + data->impl.getPayload = NULL; + + PARCBuffer *payload = ccnxInterest_GetPayload(data->interest); + printf("Shouldn't get here. payload = %p\n", (void *) payload); +} + +LONGBOW_TEST_FIXTURE(EmptyImpl) +{ + LONGBOW_RUN_TEST_CASE(EmptyImpl, empty_Display); + LONGBOW_RUN_TEST_CASE(EmptyImpl, empty_ToString); + + LONGBOW_RUN_TEST_CASE(EmptyImpl, empty_GetName); + + LONGBOW_RUN_TEST_CASE(EmptyImpl, empty_GetHopLimit); + LONGBOW_RUN_TEST_CASE(EmptyImpl, empty_SetHopLimit); + + LONGBOW_RUN_TEST_CASE(EmptyImpl, empty_GetLifetime); + LONGBOW_RUN_TEST_CASE(EmptyImpl, empty_SetLifetime); + + LONGBOW_RUN_TEST_CASE(EmptyImpl, empty_GetKeyIdRestriction); + LONGBOW_RUN_TEST_CASE(EmptyImpl, empty_SetKeyIdRestriction); + + LONGBOW_RUN_TEST_CASE(EmptyImpl, empty_GetContentObjectHashRestriction); + LONGBOW_RUN_TEST_CASE(EmptyImpl, empty_SetContentObjectHashRestriction); + + LONGBOW_RUN_TEST_CASE(EmptyImpl, empty_GetPayload); + LONGBOW_RUN_TEST_CASE(EmptyImpl, empty_SetPayload); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(ccnx_Interest); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-common/ccnx/common/test/test_ccnx_InterestPayloadId.c b/libccnx-common/ccnx/common/test/test_ccnx_InterestPayloadId.c new file mode 100755 index 00000000..28e07643 --- /dev/null +++ b/libccnx-common/ccnx/common/test/test_ccnx_InterestPayloadId.c @@ -0,0 +1,469 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include <config.h> + +#include <LongBow/unit-test.h> +#include <parc/algol/parc_SafeMemory.h> +#include <stdio.h> + +#include "../ccnx_InterestPayloadId.c" + +static const PARCMemoryInterface *_originalMemoryProvider; + +LONGBOW_TEST_RUNNER(ccnx_InterestPayloadId) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Error); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(ccnx_InterestPayloadId) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(ccnx_InterestPayloadId) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, ccnxInterestPayloadId_CreateWithAppDefinedType); + LONGBOW_RUN_TEST_CASE(Global, ccnxInterestPayloadId_CreateAsCryptoHash); + LONGBOW_RUN_TEST_CASE(Global, ccnxInterestPayloadId_CreateFromSegmentInName); + LONGBOW_RUN_TEST_CASE(Global, ccnxInterestPayloadId_CreateFromSegmentInName_NotFound); + LONGBOW_RUN_TEST_CASE(Global, ccnxInterestPayloadId_Acquire); + LONGBOW_RUN_TEST_CASE(Global, ccnxInterestPayloadId_GetNameSegment); + LONGBOW_RUN_TEST_CASE(Global, ccnxInterestPayloadId_GetValue); + LONGBOW_RUN_TEST_CASE(Global, ccnxInterestPayloadId_GetType); + LONGBOW_RUN_TEST_CASE(Global, ccnxInterestPayloadId_IsValid); + LONGBOW_RUN_TEST_CASE(Global, ccnxInterestPayloadId_ToString); + LONGBOW_RUN_TEST_CASE(Global, ccnxInterestPayloadId_Copy); + LONGBOW_RUN_TEST_CASE(Global, ccnxInterestPayloadId_Equals); + LONGBOW_RUN_TEST_CASE(Global, ccnxInterestPayloadId_NotEquals); + LONGBOW_RUN_TEST_CASE(Global, ccnxInterestPayloadId_Compare); + LONGBOW_RUN_TEST_CASE(Global, ccnxInterestPayloadId_HashCode); +} + +typedef struct { + uint8_t type; + PARCBuffer *value; +} TestData; + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + _originalMemoryProvider = parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + + TestData *data = parcMemory_AllocateAndClear(sizeof(TestData)); + assertNotNull(data, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(TestData)); + data->value = parcBuffer_WrapCString("123456789abcdef"); + data->type = 42 + CCNxInterestPayloadId_TypeCode_App; + longBowTestCase_SetClipBoardData(testCase, data); + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + parcBuffer_Release(&data->value); + parcMemory_Deallocate((void **) &data); + + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDOUT_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestRunner_GetName(testRunner), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + parcMemory_SetInterface(_originalMemoryProvider); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, ccnxInterestPayloadId_CreateWithAppDefinedType) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + uint8_t type = data->type; + PARCBuffer *value = parcBuffer_Acquire(data->value); + CCNxInterestPayloadId *ipId = ccnxInterestPayloadId_Create(value, type); + assertNotNull(ipId, "Expect a non-NULL result from Create"); + ccnxInterestPayloadId_AssertValid(ipId); + + parcBuffer_Release(&value); + + assertTrue(ccnxInterestPayloadId_IsValid(ipId), "Expected a valid CCNxInteresPayloadId."); + ccnxInterestPayloadId_Release(&ipId); +} + + +LONGBOW_TEST_CASE(Global, ccnxInterestPayloadId_CreateAsCryptoHash) +{ + PARCBuffer *value = parcBuffer_WrapCString("123456789abcdef"); + CCNxInterestPayloadId *ipId = ccnxInterestPayloadId_CreateAsSHA256Hash(value); + assertNotNull(ipId, "Expect a non-NULL result from CreateByHash"); + ccnxInterestPayloadId_AssertValid(ipId); + + parcBuffer_Release(&value); + + assertTrue(ccnxInterestPayloadId_IsValid(ipId), "Expected a valid CCNxInteresPayloadId."); + ccnxInterestPayloadId_Release(&ipId); +} + +LONGBOW_TEST_CASE(Global, ccnxInterestPayloadId_CreateFromSegmentInName) +{ + PARCBuffer *value = parcBuffer_WrapCString("123456789abcdef"); + CCNxInterestPayloadId *ipId = ccnxInterestPayloadId_CreateAsSHA256Hash(value); + parcBuffer_Release(&value); + assertNotNull(ipId, "Expect a non-NULL result from CreateByHash"); + ccnxInterestPayloadId_AssertValid(ipId); + assertTrue(ccnxInterestPayloadId_IsValid(ipId), "Expected a valid CCNxInteresPayloadId."); + + CCNxName *name = ccnxName_CreateFromCString("lci:/segment1/segment2/segment3"); + ccnxName_Append(name, ccnxInterestPayloadId_GetNameSegment(ipId)); + + CCNxInterestPayloadId *result = ccnxInterestPayloadId_CreateFromSegmentInName(name); + ccnxName_Release(&name); + + assertNotNull(result, "Should have found a payload ID"); + ccnxInterestPayloadId_AssertValid(result); + + assertTrue(ccnxInterestPayloadId_Equals(result, ipId), + "Expected source and result Interest Payload IDs to be equal"); + + ccnxInterestPayloadId_Release(&ipId); + ccnxInterestPayloadId_Release(&result); +} + +LONGBOW_TEST_CASE(Global, ccnxInterestPayloadId_CreateFromSegmentInName_NotFound) +{ + CCNxName *name = ccnxName_CreateFromCString("lci:/segment1/segment2/segment3"); + CCNxInterestPayloadId *result = ccnxInterestPayloadId_CreateFromSegmentInName(name); + ccnxName_Release(&name); + + assertNull(result, "Should have not found a payload ID"); +} + +LONGBOW_TEST_CASE(Global, ccnxInterestPayloadId_Acquire) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + uint8_t type = data->type; + PARCBuffer *value = parcBuffer_Acquire(data->value); + CCNxInterestPayloadId *ipId = ccnxInterestPayloadId_Create(value, type); + parcBuffer_Release(&value); + ccnxInterestPayloadId_AssertValid(ipId); + + CCNxInterestPayloadId *ipIdAcq = ccnxInterestPayloadId_Acquire(ipId); + ccnxInterestPayloadId_AssertValid(ipIdAcq); + + ccnxInterestPayloadId_Release(&ipId); + ccnxInterestPayloadId_Release(&ipIdAcq); +} + +LONGBOW_TEST_CASE(Global, ccnxInterestPayloadId_GetValue) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + uint8_t type = data->type; + PARCBuffer *value = parcBuffer_Acquire(data->value); + CCNxInterestPayloadId *ipId = ccnxInterestPayloadId_Create(value, type); + ccnxInterestPayloadId_AssertValid(ipId); + + assertTrue(parcBuffer_Equals(ccnxInterestPayloadId_GetValue(ipId), value), + "Expect GetId to produce the correct result"); + + parcBuffer_Release(&value); + ccnxInterestPayloadId_Release(&ipId); +} + +LONGBOW_TEST_CASE(Global, ccnxInterestPayloadId_GetType) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + uint8_t type = data->type; + PARCBuffer *value = parcBuffer_Acquire(data->value); + CCNxInterestPayloadId *ipId = ccnxInterestPayloadId_Create(value, type); + ccnxInterestPayloadId_AssertValid(ipId); + + assertTrue(ccnxInterestPayloadId_GetType(ipId) == type, + "Expect GetId to produce the correct result"); + + parcBuffer_Release(&value); + ccnxInterestPayloadId_Release(&ipId); +} + +LONGBOW_TEST_CASE(Global, ccnxInterestPayloadId_GetType_App) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + uint8_t type = data->type; + PARCBuffer *value = parcBuffer_Acquire(data->value); + CCNxInterestPayloadId *ipId = ccnxInterestPayloadId_Create(value, type); + ccnxInterestPayloadId_AssertValid(ipId); + + assertTrue(parcBuffer_Equals(ccnxInterestPayloadId_GetValue(ipId), value), + "Expect GetId to produce the correct result"); + + parcBuffer_Release(&value); + ccnxInterestPayloadId_Release(&ipId); +} + +LONGBOW_TEST_CASE(Global, ccnxInterestPayloadId_HashCode) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + uint8_t type = data->type; + PARCBuffer *value = parcBuffer_Acquire(data->value); + CCNxInterestPayloadId *ipId1 = ccnxInterestPayloadId_Create(value, type); + uint32_t hashCode1 = ccnxInterestPayloadId_HashCode(ipId1); + CCNxInterestPayloadId *ipId2 = ccnxInterestPayloadId_Create(value, type); + uint32_t hashCode2 = ccnxInterestPayloadId_HashCode(ipId2); + assertTrue(hashCode1 == hashCode2, "Expect hash codes of equal objects to be equal"); + parcBuffer_Release(&value); + ccnxInterestPayloadId_Release(&ipId1); + ccnxInterestPayloadId_Release(&ipId2); +} + + +LONGBOW_TEST_CASE(Global, ccnxInterestPayloadId_Equals) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + uint8_t type = data->type; + PARCBuffer *value = parcBuffer_WrapCString("123456789abcdef"); + CCNxInterestPayloadId *ipId = ccnxInterestPayloadId_Create(value, type); + ccnxInterestPayloadId_AssertValid(ipId); + + PARCBuffer *value2 = parcBuffer_WrapCString("123456789abcdef"); + CCNxInterestPayloadId *ipId2 = ccnxInterestPayloadId_Create(value2, type); + ccnxInterestPayloadId_AssertValid(ipId2); + + assertTrue(ccnxInterestPayloadId_Equals(ipId, ipId2), + "Expect InterestPayloadIds to be equal"); + + CCNxInterestPayloadId *ipId3 = ccnxInterestPayloadId_Create(value, type); + ccnxInterestPayloadId_AssertValid(ipId3); + + assertTrue(ccnxInterestPayloadId_Equals(ipId, ipId3), + "Expect InterestPayloadIds to be equal"); + + parcBuffer_Release(&value); + parcBuffer_Release(&value2); + ccnxInterestPayloadId_Release(&ipId); + ccnxInterestPayloadId_Release(&ipId2); + ccnxInterestPayloadId_Release(&ipId3); +} + +LONGBOW_TEST_CASE(Global, ccnxInterestPayloadId_NotEquals) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + uint8_t type = data->type; + PARCBuffer *value = parcBuffer_WrapCString("123456789abcdef"); + CCNxInterestPayloadId *ipId = ccnxInterestPayloadId_Create(value, type); + ccnxInterestPayloadId_AssertValid(ipId); + + PARCBuffer *value2 = parcBuffer_WrapCString("123456789abcdex"); + CCNxInterestPayloadId *ipId2 = ccnxInterestPayloadId_Create(value2, type); + ccnxInterestPayloadId_AssertValid(ipId2); + + assertTrue(!ccnxInterestPayloadId_Equals(ipId, ipId2), + "Expect InterestPayloadIds to be equal"); + + parcBuffer_Release(&value); + parcBuffer_Release(&value2); + ccnxInterestPayloadId_Release(&ipId); + ccnxInterestPayloadId_Release(&ipId2); +} + + +LONGBOW_TEST_CASE(Global, ccnxInterestPayloadId_Compare) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + uint8_t type = data->type; + PARCBuffer *value1 = parcBuffer_WrapCString("123456789abcdef"); + CCNxInterestPayloadId *ipId1 = ccnxInterestPayloadId_Create(value1, type); + ccnxInterestPayloadId_AssertValid(ipId1); + + assertTrue(ccnxInterestPayloadId_Compare(ipId1, ipId1) == 0, + "Expect compare result of 0 when comparing InterestPayloadId to itself"); + + PARCBuffer *value1p = parcBuffer_WrapCString("123456789abcdef"); + CCNxInterestPayloadId *ipId1p = ccnxInterestPayloadId_Create(value1p, type); + ccnxInterestPayloadId_AssertValid(ipId1p); + + assertTrue(ccnxInterestPayloadId_Compare(ipId1, ipId1p) == 0, + "Expect compare result of 0 when comparing InterestPayloadIds with the same content"); + + PARCBuffer *value2 = parcBuffer_WrapCString("123456789abcdex"); + CCNxInterestPayloadId *ipId2 = ccnxInterestPayloadId_Create(value2, type); + ccnxInterestPayloadId_AssertValid(ipId2); + + assertTrue(ccnxInterestPayloadId_Compare(ipId2, ipId1) > 0, + "Expect compare result > 0 when comparing InterestPayloadId2 to InterestPayloadId1"); + + parcBuffer_Release(&value1); + parcBuffer_Release(&value1p); + parcBuffer_Release(&value2); + + ccnxInterestPayloadId_Release(&ipId1); + ccnxInterestPayloadId_Release(&ipId1p); + ccnxInterestPayloadId_Release(&ipId2); +} + +LONGBOW_TEST_CASE(Global, ccnxInterestPayloadId_Copy) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + uint8_t type = data->type; + PARCBuffer *value1 = parcBuffer_Acquire(data->value); + CCNxInterestPayloadId *ipId1 = ccnxInterestPayloadId_Create(value1, type); + parcBuffer_Release(&value1); + ccnxInterestPayloadId_AssertValid(ipId1); + + CCNxInterestPayloadId *ipIdCopy = ccnxInterestPayloadId_Copy(ipId1); + ccnxInterestPayloadId_AssertValid(ipIdCopy); + assertTrue(ccnxInterestPayloadId_Equals(ipId1, ipIdCopy), "Expect original and copy InterestPayloadId to be equal"); + + ccnxInterestPayloadId_Release(&ipId1); + ccnxInterestPayloadId_Release(&ipIdCopy); +} + +LONGBOW_TEST_CASE(Global, ccnxInterestPayloadId_IsValid) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + uint8_t type = data->type; + PARCBuffer *value = parcBuffer_Acquire(data->value); + CCNxInterestPayloadId *ipId = ccnxInterestPayloadId_Create(value, type); + parcBuffer_Release(&value); + + assertTrue(ccnxInterestPayloadId_IsValid(ipId), "Expected a valid CCNxInteresPayloadId."); + ccnxInterestPayloadId_Release(&ipId); +} + +LONGBOW_TEST_CASE(Global, ccnxInterestPayloadId_ToString) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + uint8_t type = data->type; + char *test = "123456789abcdef"; + PARCBuffer *value = parcBuffer_WrapCString(test); + CCNxInterestPayloadId *ipId = ccnxInterestPayloadId_Create(value, type); + parcBuffer_Release(&value); + + char *result = ccnxInterestPayloadId_ToString(ipId); + assertNotNull(result, "Expect a non-NULL result from ToString"); + ccnxInterestPayloadId_Release(&ipId); + + PARCBufferComposer *composer = parcBufferComposer_Allocate(10); + parcBufferComposer_PutString(composer, CCNxNameLabel_InterestPayloadId); + parcBufferComposer_PutChar(composer, '='); + + PARCBufferComposer *uriComposer = parcBufferComposer_Allocate(10); + parcBufferComposer_PutChar(uriComposer, type); + parcBufferComposer_PutString(uriComposer, test); + PARCBuffer *producedBuffer = parcBufferComposer_ProduceBuffer(uriComposer); + PARCURISegment *uriSegment = parcURISegment_CreateFromBuffer(producedBuffer); + parcBuffer_Release(&producedBuffer); + parcBufferComposer_Release(&uriComposer); + parcURISegment_BuildString(uriSegment, composer); + parcURISegment_Release(&uriSegment); + char *expect = parcBufferComposer_ToString(composer); + parcBufferComposer_Release(&composer); + + assertTrue(strcmp(expect, result) == 0, "Expect test and result strings to be the same."); + + parcMemory_Deallocate((void **) &result); + parcMemory_Deallocate((void **) &expect); +} + +LONGBOW_TEST_CASE(Global, ccnxInterestPayloadId_GetNameSegment) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + uint8_t type = data->type; + PARCBuffer *value = parcBuffer_Acquire(data->value); + CCNxInterestPayloadId *ipId = ccnxInterestPayloadId_Create(value, type); + + CCNxNameSegment *segment = ccnxNameSegment_Acquire(ccnxInterestPayloadId_GetNameSegment(ipId)); + ccnxInterestPayloadId_Release(&ipId); + + PARCBufferComposer *composer = parcBufferComposer_Allocate(parcBuffer_Capacity(value) + 1); + parcBufferComposer_PutUint8(composer, type); + parcBufferComposer_PutBuffer(composer, value); + PARCBuffer *testValue = parcBufferComposer_ProduceBuffer(composer); + parcBufferComposer_Release(&composer); + parcBuffer_Release(&value); + CCNxNameSegment *testSegment = ccnxNameSegment_CreateTypeValue(CCNxNameLabelType_PAYLOADID, testValue); + parcBuffer_Release(&testValue); + assertTrue(ccnxNameSegment_Equals(segment, testSegment), "Expect GetAsNameSegment result to match test NameSegment"); + + ccnxNameSegment_Release(&segment); + ccnxNameSegment_Release(&testSegment); +} + +LONGBOW_TEST_FIXTURE(Error) +{ + LONGBOW_RUN_TEST_CASE(Error, ccnxInterestPayloadId__CreateFromNameSegment_NotFound); +} + +typedef struct { + CCNxName *name; +} TestDataError; + +LONGBOW_TEST_FIXTURE_SETUP(Error) +{ + _originalMemoryProvider = parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + + + TestDataError *data = parcMemory_AllocateAndClear(sizeof(TestDataError)); + assertNotNull(data, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(TestDataError)); + data->name = ccnxName_CreateFromCString("lci:/segment1/segment2/segment3"); + longBowTestCase_SetClipBoardData(testCase, data); + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Error) +{ + TestDataError *data = longBowTestCase_GetClipBoardData(testCase); + ccnxName_Release(&data->name); + parcMemory_Deallocate((void **) &data); + + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDOUT_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestRunner_GetName(testRunner), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + parcMemory_SetInterface(_originalMemoryProvider); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE_EXPECTS(Error, ccnxInterestPayloadId__CreateFromNameSegment_NotFound, .event = &LongBowAssertEvent) +{ + TestDataError *data = longBowTestCase_GetClipBoardData(testCase); + CCNxNameSegment *nameSegment = ccnxName_GetSegment(data->name, 0); + CCNxInterestPayloadId *result = + _ccnxInterestPayloadId_CreateFromNameSegment(nameSegment); + assertNull(result, "Expect an assert event or NULL"); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(ccnx_InterestPayloadId); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-common/ccnx/common/test/test_ccnx_InterestReturn.c b/libccnx-common/ccnx/common/test/test_ccnx_InterestReturn.c new file mode 100755 index 00000000..79e27994 --- /dev/null +++ b/libccnx-common/ccnx/common/test/test_ccnx_InterestReturn.c @@ -0,0 +1,285 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../ccnx_InterestReturn.c" +#include "../ccnx_Interest.h" +#include <parc/algol/parc_SafeMemory.h> +#include <LongBow/unit-test.h> + +#include <ccnx/common/ccnx_Interest.h> +#include <ccnx/common/ccnx_InterestReturn.h> +#include <ccnx/common/ccnx_PayloadType.h> + +typedef struct test_data { + CCNxTlvDictionary *interest; + CCNxInterestInterface *interestImpl; + + CCNxName *name; + PARCBuffer *keyid; + PARCBuffer *contentObjectHash; + PARCBuffer *payload; + + // allocated data + uint8_t keyidArray[32]; + uint8_t contentObjectHashArray[32]; + uint8_t payloadArray[128]; + + uint32_t lifetime; + uint32_t hoplimit; + CCNxPayloadType payloadType; +} TestData; + + +LONGBOW_TEST_RUNNER(ccnx_InterestReturnV1) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(ccnx_InterestReturnV1) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(ccnx_InterestReturnV1) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// ======================================================================================== + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, ccnxInterestReturnV1_Create); + LONGBOW_RUN_TEST_CASE(Global, ccnxInterestReturnV1_GetReturnCode); + LONGBOW_RUN_TEST_CASE(Global, ccnxInterestReturnV1_AcquireRelease); + LONGBOW_RUN_TEST_CASE(Global, ccnxInterestReturnV1_Equals); + LONGBOW_RUN_TEST_CASE(Global, ccnxInterestReturnV1_NotEquals); + LONGBOW_RUN_TEST_CASE(Global, ccnxInterestReturnV1_ToString); + LONGBOW_RUN_TEST_CASE(Global, ccnxInterestReturnV1_GetInterestFields); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + TestData *data = parcMemory_AllocateAndClear(sizeof(TestData)); + assertNotNull(data, "parcMemory_AllocateAndClear(%lu) returned NULL", sizeof(TestData)); + data->name = ccnxName_CreateFromCString("lci:/once/upon/a/time"); + + for (int i = 0; i < 32; i++) { + data->keyidArray[i] = i * 7; + data->contentObjectHashArray[i] = i * 11; + } + + for (int i = 0; i < 128; i++) { + data->payloadArray[i] = i * 13; + } + + data->interestImpl = &CCNxInterestFacadeV1_Implementation; + data->keyid = parcBuffer_Wrap(data->keyidArray, 32, 0, 32); + data->contentObjectHash = parcBuffer_Wrap(data->contentObjectHashArray, 32, 0, 32); + data->payloadType = CCNxPayloadType_DATA; + data->payload = parcBuffer_Wrap(data->payloadArray, 128, 0, 128); + + data->lifetime = 900; + data->hoplimit = 77; + + data->interest = ccnxInterest_CreateWithImpl(data->interestImpl, + data->name, + data->lifetime, + data->keyid, + data->contentObjectHash, + data->hoplimit); + + ccnxInterest_SetPayload(data->interest, data->payload); + + longBowTestCase_SetClipBoardData(testCase, data); + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + ccnxName_Release(&data->name); + parcBuffer_Release(&data->keyid); + parcBuffer_Release(&data->contentObjectHash); + parcBuffer_Release(&data->payload); + ccnxTlvDictionary_Release(&data->interest); + + parcMemory_Deallocate((void **) &data); + + if (parcSafeMemory_ReportAllocation(STDOUT_FILENO) != 0) { + printf("('%s' leaks memory by %d (allocs - frees)) ", longBowTestCase_GetName(testCase), parcMemory_Outstanding()); + return LONGBOW_STATUS_TEARDOWN_FAILED; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, ccnxInterestReturnV1_Create) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + CCNxInterestReturn *interestReturn = + ccnxInterestReturn_Create(data->interest, CCNxInterestReturn_ReturnCode_NoRoute); + + ccnxInterestReturn_AssertValid(interestReturn); + + //assertTrue(ccnxInterestReturn_IsValid(interestReturn), "InterestReturn is not valid"); + ccnxTlvDictionary_Release(&interestReturn); +} + + +LONGBOW_TEST_CASE(Global, ccnxInterestReturnV1_AcquireRelease) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + CCNxInterestReturn *interestReturn = + ccnxInterestReturn_Create(data->interest, CCNxInterestReturn_ReturnCode_NoRoute); + + assertNotNull(interestReturn, "Expect non-NULL interestReturn"); + + CCNxInterestReturn *testInterestReturn = ccnxInterestReturn_Acquire(interestReturn); + assertNotNull(testInterestReturn, "Expected a non-NULL testInterestReturn"); + + ccnxInterestReturn_Release(&testInterestReturn); + assertNull(testInterestReturn, "Expected a NULL testInterestReturn"); + + ccnxInterestReturn_Release(&interestReturn); + assertNull(interestReturn, "Expected a NULL testInterestReturn"); +} + +LONGBOW_TEST_CASE(Global, ccnxInterestReturnV1_Equals) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + CCNxInterestReturn *interestReturn = + ccnxInterestReturn_Create(data->interest, CCNxInterestReturn_ReturnCode_NoRoute); + assertTrue(ccnxInterestReturn_Equals(interestReturn, interestReturn), "Expect same interestReturn pointers to be equal"); + + CCNxInterestReturn *acquiredIR = ccnxInterestReturn_Acquire(interestReturn); + assertTrue(ccnxInterestReturn_Equals(interestReturn, acquiredIR), "Expect acquired interestReturn to be equal to original"); + ccnxInterestReturn_Release(&acquiredIR); + + CCNxInterestReturn *identicalIR = + ccnxInterestReturn_Create(data->interest, CCNxInterestReturn_ReturnCode_NoRoute); + assertTrue(ccnxInterestReturn_Equals(interestReturn, identicalIR), "Expect identical interestReturn to be equal to original"); + + ccnxInterestReturn_Release(&identicalIR); + ccnxInterestReturn_Release(&interestReturn); +} + +LONGBOW_TEST_CASE(Global, ccnxInterestReturnV1_NotEquals) +{ + assertFalse(ccnxInterestReturn_Equals(NULL, NULL), "Expect two NULL interests to not be equal"); + + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + CCNxInterestReturn *interestReturn = + ccnxInterestReturn_Create(data->interest, CCNxInterestReturn_ReturnCode_NoRoute); + assertFalse(ccnxInterestReturn_Equals(interestReturn, NULL), "Expect a NULL interest to not be equal"); + + CCNxInterestReturn *testIR = + ccnxInterestReturn_Create(data->interest, CCNxInterestReturn_ReturnCode_MTUTooLarge); + assertFalse(ccnxInterestReturn_Equals(interestReturn, testIR), "Expect interestReturn's with different return codes to be !="); + + ccnxInterestReturn_Release(&testIR); + ccnxInterestReturn_Release(&interestReturn); +} + +LONGBOW_TEST_CASE(Global, ccnxInterestReturnV1_ToString) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + CCNxInterestReturn *interestReturn = + ccnxInterestReturn_Create(data->interest, CCNxInterestReturn_ReturnCode_NoRoute); + + const char *string = ccnxInterestReturn_ToString(interestReturn); + assertNotNull(string, "Expected non-null result from ccnxInterestReturn_ToString."); + + parcMemory_Deallocate((void **) &string); + + ccnxInterestReturn_Release(&interestReturn); +} + +LONGBOW_TEST_CASE(Global, ccnxInterestReturnV1_GetReturnCode) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + CCNxInterestReturn *interestReturn = + ccnxInterestReturn_Create(data->interest, CCNxInterestReturn_ReturnCode_NoRoute); + + CCNxInterestReturn_ReturnCode code = + ccnxInterestReturn_GetReturnCode(interestReturn); + + assertTrue((CCNxInterestReturn_ReturnCode_NoRoute == code), "InterestReturn wrong Return Code"); + ccnxInterestReturn_Release(&interestReturn); +} + +LONGBOW_TEST_CASE(Global, ccnxInterestReturnV1_GetInterestFields) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + CCNxInterestReturn *interestReturn = + ccnxInterestReturn_Create(data->interest, CCNxInterestReturn_ReturnCode_NoRoute); + + CCNxName *name = ccnxInterest_GetName(interestReturn); + assertTrue(ccnxName_Equals(name, data->name), "Names do not match") + { + printf("\ngot : \n"); ccnxName_Display(name, 3); + printf("\nexpected: \n"); ccnxName_Display(data->name, 3); + } + + uint32_t hopLimit = ccnxInterest_GetHopLimit(interestReturn); + assertTrue(hopLimit == data->hoplimit, "Wrong hoplimit: got %u expected %u", hopLimit, data->hoplimit); + + uint32_t lifetime = (uint32_t) ccnxInterest_GetLifetime(interestReturn); + assertTrue(lifetime == data->lifetime, "Wrong lifetime: got %u expected %u", lifetime, data->lifetime); + + PARCBuffer *buff = ccnxInterest_GetKeyIdRestriction(interestReturn); + assertTrue(parcBuffer_Equals(buff, data->keyid), "KeyIDs do not match") + { + printf("\ngot : \n"); parcBuffer_Display(buff, 3); + printf("\nexpected: \n"); parcBuffer_Display(data->keyid, 3); + } + + buff = ccnxInterest_GetPayload(interestReturn); + assertTrue(parcBuffer_Equals(buff, data->payload), "Payloads do not match") + { + printf("\ngot : \n"); parcBuffer_Display(buff, 3); + printf("\nexpected: \n"); parcBuffer_Display(data->payload, 3); + } + + ccnxInterestReturn_Release(&interestReturn); +} + + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(ccnx_InterestReturnV1); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-common/ccnx/common/test/test_ccnx_Key.c b/libccnx-common/ccnx/common/test/test_ccnx_Key.c new file mode 100755 index 00000000..c1bd5ed2 --- /dev/null +++ b/libccnx-common/ccnx/common/test/test_ccnx_Key.c @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ +#include <config.h> +#include "../ccnx_Key.c" +#include <LongBow/unit-test.h> + +#include <parc/algol/parc_SafeMemory.h> + +LONGBOW_TEST_RUNNER(ccnx_Key) +{ + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +LONGBOW_TEST_RUNNER_SETUP(ccnx_Key) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_RUNNER_TEARDOWN(ccnx_Key) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, ccnxKey_CreateRelease); + LONGBOW_RUN_TEST_CASE(Global, ccnxKey_CreateFromHexString); + LONGBOW_RUN_TEST_CASE(Global, ccnxKey_FromByteBuffer); + LONGBOW_RUN_TEST_CASE(Global, ccnxKey_ToString); +} + +typedef struct { + char *hexString; +} TestData; + +static TestData* +commonSetup() +{ + char *hexString = "30819F300D06092A864886F70D010101050003818D0030818902818100A826C09E01FF4970428213C96312B46050514FD5F87E670A4784C75D8B23CD073B1CBEF328E538584E442A769DF77299192BCF3603F50F14C5664994250E5C24DF47B86EA5C7CA99B3584E9A63BC5993569FF3612C71AD46A088CDC7346B9BE021D4CA1764CF5434F993E6120363C551E2979BDB3F0345B4994BCED9CB260EEB0203010001"; + + TestData *data = parcMemory_AllocateAndClear(sizeof(TestData)); + assertNotNull(data, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(TestData)); + data->hexString = parcMemory_StringDuplicate(hexString, strlen(hexString)); + return data; +} + +static void +commonTearDown(TestData *data) +{ + parcMemory_Deallocate((void **) &(data->hexString)); + parcMemory_Deallocate((void **) &data); +} + + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + TestData *data = commonSetup(); + longBowTestCase_SetClipBoardData(testCase, data); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + commonTearDown(data); + + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + + +LONGBOW_TEST_CASE(Global, ccnxKey_FromByteBuffer) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + PARCBuffer *hexBuf = parcBuffer_ParseHexString(data->hexString); + CCNxKey *key = ccnxKey_Create(hexBuf); + parcBuffer_Release(&hexBuf); + ccnxKey_Release(&key); +} + +LONGBOW_TEST_CASE(Global, ccnxKey_CreateRelease) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + CCNxKey *key = ccnxKey_CreateFromHexString(data->hexString); + ccnxKey_AssertValid(key); + + char *string = ccnxKey_ToString(key); + + ccnxKey_Release(&key); + assertNull(key, "Key was not nulled out after Release()"); + + parcMemory_Deallocate((void **) &string); +} + +LONGBOW_TEST_CASE(Global, ccnxKey_CreateFromHexString) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + CCNxKey *key = ccnxKey_CreateFromHexString(data->hexString); + ccnxKey_AssertValid(key); + + char *string = ccnxKey_ToHexString(key); + + assertTrue(strcasecmp(data->hexString, string) == 0, + "Expected '%s' actual '%s'", data->hexString, string); + + ccnxKey_Release(&key); + assertNull(key, "Key was not nulled out after Release()"); + + parcMemory_Deallocate((void **) &string); +} + +LONGBOW_TEST_CASE(Global, ccnxKey_ToString) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + CCNxKey *key = ccnxKey_CreateFromHexString(data->hexString); + ccnxKey_AssertValid(key); + + char *string = ccnxKey_ToString(key); + + ccnxKey_Release(&key); + assertNull(key, "Key was not nulled out after Release()"); + + parcMemory_Deallocate((void **) &string); +} + +LONGBOW_TEST_FIXTURE(Local) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(ccnx_Key); + exit(longBowMain(argc, argv, testRunner, NULL)); +} diff --git a/libccnx-common/ccnx/common/test/test_ccnx_KeyLocator.c b/libccnx-common/ccnx/common/test/test_ccnx_KeyLocator.c new file mode 100755 index 00000000..3e877179 --- /dev/null +++ b/libccnx-common/ccnx/common/test/test_ccnx_KeyLocator.c @@ -0,0 +1,358 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../ccnx_KeyLocator.c" + +#include <parc/algol/parc_SafeMemory.h> +#include <LongBow/unit-test.h> + +#include <parc/algol/parc_SafeMemory.h> + +LONGBOW_TEST_RUNNER(ccnx_KeyLocator) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(ccnx_KeyLocator) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(ccnx_KeyLocator) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, ccnxKeyLocator_Copy); + LONGBOW_RUN_TEST_CASE(Global, ccnxKeyLocator_Destroy); + LONGBOW_RUN_TEST_CASE(Global, ccnxKeyLocator_Equals); + LONGBOW_RUN_TEST_CASE(Global, ccnxKeyLocator_FromKey); + LONGBOW_RUN_TEST_CASE(Global, ccnxKeyLocator_FromKeyLink); + LONGBOW_RUN_TEST_CASE(Global, ccnxKeyLocator_GetKey); + LONGBOW_RUN_TEST_CASE(Global, ccnxKeyLocator_GetKeyName); + LONGBOW_RUN_TEST_CASE(Global, ccnxKeyLocator_GetType); + LONGBOW_RUN_TEST_CASE(Global, ccnxKeyLocator_IsKey); + LONGBOW_RUN_TEST_CASE(Global, ccnxKeyLocator_IsKeyName); + LONGBOW_RUN_TEST_CASE(Global, ccnxKeyLocator_ToString); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, ccnxKeyLocator_Copy) +{ + CCNxName *keyURIName = ccnxName_CreateFromCString("lci://name"); + CCNxLink *keyLink = ccnxLink_Create(keyURIName, NULL, NULL); + CCNxKeyLocator *keyLocator = ccnxKeyLocator_CreateFromKeyLink(keyLink); + + ccnxKeyLocator_AssertValid(keyLocator); + + CCNxKeyLocator *copy = ccnxKeyLocator_Copy(keyLocator); + assertTrue(ccnxKeyLocator_Equals(copy, keyLocator), "Expected orig and copy to be the same"); + + ccnxKeyLocator_Release(&keyLocator); + ccnxKeyLocator_Release(©); + + ccnxName_Release(&keyURIName); + ccnxLink_Release(&keyLink); + + // Try FromHexString, too, for yucks. + PARCBuffer *keyBuffer = parcBuffer_WrapCString("hello world"); + PARCKeyId *keyId = parcKeyId_Create(keyBuffer); + PARCKey *key = parcKey_CreateFromDerEncodedPublicKey(keyId, PARCSigningAlgorithm_RSA, keyBuffer); + parcKeyId_Release(&keyId); + parcBuffer_Release(&keyBuffer); + CCNxKeyLocator *keyLocator2 = ccnxKeyLocator_CreateFromKey(key); + + CCNxKeyLocator *copy2 = ccnxKeyLocator_Copy(keyLocator2); + assertTrue(ccnxKeyLocator_Equals(copy, keyLocator), "Expected orig and copy to be the same"); + + parcKey_Release(&key); + ccnxKeyLocator_Release(&keyLocator2); + ccnxKeyLocator_Release(©2); +} + +LONGBOW_TEST_CASE(Global, ccnxKeyLocator_Destroy) +{ + CCNxName *keyURIName = ccnxName_CreateFromCString("lci://name"); + CCNxLink *keyLink = ccnxLink_Create(keyURIName, NULL, NULL); + CCNxKeyLocator *keyLocator = ccnxKeyLocator_CreateFromKeyLink(keyLink); + + ccnxKeyLocator_AssertValid(keyLocator); + + ccnxKeyLocator_Release(&keyLocator); + assertNull(keyLocator, "keyLocator was not nulled out by Release()"); + + ccnxLink_Release(&keyLink); + ccnxName_Release(&keyURIName); +} + +LONGBOW_TEST_CASE(Global, ccnxKeyLocator_Equals) +{ + char *hexString = "ABCDEF1234"; + PARCBuffer *keyBuffer = parcBuffer_WrapCString(hexString); + PARCKeyId *keyId = parcKeyId_Create(keyBuffer); + PARCKey *key1 = parcKey_CreateFromDerEncodedPublicKey(keyId, PARCSigningAlgorithm_RSA, keyBuffer); + CCNxKeyLocator *keyLocator1 = ccnxKeyLocator_CreateFromKey(key1); + parcKeyId_Release(&keyId); + parcBuffer_Release(&keyBuffer); + + CCNxKeyLocator *keyLocator1Copy = ccnxKeyLocator_Copy(keyLocator1); + + PARCBuffer *keyBuffer2 = parcBuffer_WrapCString(hexString); + PARCKeyId *keyId2 = parcKeyId_Create(keyBuffer2); + PARCKey *key2 = parcKey_CreateFromDerEncodedPublicKey(keyId2, PARCSigningAlgorithm_RSA, keyBuffer2); + CCNxKeyLocator *keyLocator2 = ccnxKeyLocator_CreateFromKey(key2); + parcBuffer_Release(&keyBuffer2); + parcKeyId_Release(&keyId2); + + CCNxName *keyURIName = ccnxName_CreateFromCString("lci://name"); + CCNxLink *keyLink = ccnxLink_Create(keyURIName, NULL, NULL); + CCNxKeyLocator *keyLocatorDiff = ccnxKeyLocator_CreateFromKeyLink(keyLink); + + ccnxKeyLocator_AssertValid(keyLocator1); + ccnxKeyLocator_AssertValid(keyLocator1Copy); + ccnxKeyLocator_AssertValid(keyLocator2); + ccnxKeyLocator_AssertValid(keyLocatorDiff); + + assertEqualsContract(ccnxKeyLocator_Equals, keyLocator1, keyLocator1Copy, keyLocator2, keyLocatorDiff); + + parcKey_Release(&key1); + ccnxKeyLocator_Release(&keyLocator1); + ccnxKeyLocator_Release(&keyLocator1Copy); + + parcKey_Release(&key2); + ccnxKeyLocator_Release(&keyLocator2); + + ccnxName_Release(&keyURIName); + ccnxLink_Release(&keyLink); + ccnxKeyLocator_Release(&keyLocatorDiff); +} + +LONGBOW_TEST_CASE(Global, ccnxKeyLocator_FromKey) +{ + char *hexString = "ABCDEF1234"; + PARCBuffer *keyBuffer = parcBuffer_WrapCString(hexString); + PARCKeyId *keyId = parcKeyId_Create(keyBuffer); + + PARCKey *key = parcKey_CreateFromDerEncodedPublicKey(keyId, PARCSigningAlgorithm_RSA, keyBuffer); + CCNxKeyLocator *keyLocator = ccnxKeyLocator_CreateFromKey(key); + parcKeyId_Release(&keyId); + parcBuffer_Release(&keyBuffer); + + ccnxKeyLocator_AssertValid(keyLocator); + + ccnxKeyLocator_Release(&keyLocator); + parcKey_Release(&key); +} + +LONGBOW_TEST_CASE(Global, ccnxKeyLocator_FromKeyLink) +{ + CCNxName *keyURIName = ccnxName_CreateFromCString("lci://name"); + CCNxLink *keyLink = ccnxLink_Create(keyURIName, NULL, NULL); + CCNxKeyLocator *keyLocator = ccnxKeyLocator_CreateFromKeyLink(keyLink); + + ccnxKeyLocator_AssertValid(keyLocator); + + ccnxKeyLocator_Release(&keyLocator); + ccnxName_Release(&keyURIName); + ccnxLink_Release(&keyLink); +} + +LONGBOW_TEST_CASE(Global, ccnxKeyLocator_GetKey) +{ + char *hexString = "ABCDEF1234"; + PARCBuffer *keyBuffer = parcBuffer_WrapCString(hexString); + PARCKeyId *keyId = parcKeyId_Create(keyBuffer); + + PARCKey *key = parcKey_CreateFromDerEncodedPublicKey(keyId, PARCSigningAlgorithm_RSA, keyBuffer); + CCNxKeyLocator *keyLocator = ccnxKeyLocator_CreateFromKey(key); + + parcKeyId_Release(&keyId); + parcBuffer_Release(&keyBuffer); + + ccnxKeyLocator_AssertValid(keyLocator); + PARCKey *actual = ccnxKeyLocator_GetKey(keyLocator); + assertTrue(actual == key, "Actual certificate didn't match expected"); + + ccnxKeyLocator_Release(&keyLocator); + parcKey_Release(&key); +} + +LONGBOW_TEST_CASE(Global, ccnxKeyLocator_GetKeyName) +{ + CCNxName *keyURIName = ccnxName_CreateFromCString("lci://name/test"); + CCNxLink *keyLink = ccnxLink_Create(keyURIName, NULL, NULL); + CCNxKeyLocator *keyLocator = ccnxKeyLocator_CreateFromKeyLink(keyLink); + + ccnxKeyLocator_AssertValid(keyLocator); + const CCNxName *actual = ccnxLink_GetName(ccnxKeyLocator_GetKeyLink(keyLocator)); + assertTrue(ccnxName_Equals(actual, keyURIName), "Actual keyName did not match the one returned by GetKeyName()"); + + ccnxKeyLocator_Release(&keyLocator); + ccnxName_Release(&keyURIName); + ccnxLink_Release(&keyLink); +} + +LONGBOW_TEST_CASE(Global, ccnxKeyLocator_GetType) +{ + // Try FromKey + char *hexString = "ABCDEF1234"; + PARCBuffer *keyBuffer = parcBuffer_WrapCString(hexString); + PARCKeyId *keyId = parcKeyId_Create(keyBuffer); + + PARCKey *key = parcKey_CreateFromDerEncodedPublicKey(keyId, PARCSigningAlgorithm_RSA, keyBuffer); + CCNxKeyLocator *keyLocator = ccnxKeyLocator_CreateFromKey(key); + parcKeyId_Release(&keyId); + parcBuffer_Release(&keyBuffer); + + ccnxKeyLocator_AssertValid(keyLocator); + assertTrue(ccnxKeyLocator_GetType(keyLocator) == CCNxKeyLocatorType_Key, "Actual certificate type didn't match expected type"); + + ccnxKeyLocator_Release(&keyLocator); + parcKey_Release(&key); + + // Try KeyName + CCNxName *keyURIName = ccnxName_CreateFromCString("lci://name/test"); + CCNxLink *keyLink = ccnxLink_Create(keyURIName, NULL, NULL); + keyLocator = ccnxKeyLocator_CreateFromKeyLink(keyLink); + + ccnxKeyLocator_AssertValid(keyLocator); + assertTrue(ccnxKeyLocator_GetType(keyLocator) == CCNxKeyLocatorType_Link, "Actual certificate type didn't match expected type"); + assertFalse(ccnxKeyLocator_GetType(keyLocator) == CCNxKeyLocatorType_Key, "Actual certificate type didn't match expected type"); + + ccnxKeyLocator_Release(&keyLocator); + ccnxName_Release(&keyURIName); + ccnxLink_Release(&keyLink); +} + +LONGBOW_TEST_CASE(Global, ccnxKeyLocator_IsKey) +{ + char *hexString = "ABCDEF1234"; + PARCBuffer *keyBuffer = parcBuffer_WrapCString(hexString); + PARCKeyId *keyId = parcKeyId_Create(keyBuffer); + + PARCKey *key = parcKey_CreateFromDerEncodedPublicKey(keyId, PARCSigningAlgorithm_RSA, keyBuffer); + CCNxKeyLocator *keyLocator = ccnxKeyLocator_CreateFromKey(key); + parcKeyId_Release(&keyId); + parcBuffer_Release(&keyBuffer); + + ccnxKeyLocator_AssertValid(keyLocator); + assertTrue(ccnxKeyLocator_IsKey(keyLocator), "Expected Iskey to be true"); + assertFalse(ccnxKeyLocator_IsKeyLink(keyLocator), "Expected IsKeyLink to be false"); + + ccnxKeyLocator_Release(&keyLocator); + parcKey_Release(&key); +} + +LONGBOW_TEST_CASE(Global, ccnxKeyLocator_IsKeyName) +{ + CCNxName *keyURIName = ccnxName_CreateFromCString("lci://name/test"); + CCNxLink *keyLink = ccnxLink_Create(keyURIName, NULL, NULL); + CCNxKeyLocator *keyLocator = ccnxKeyLocator_CreateFromKeyLink(keyLink); + + ccnxKeyLocator_AssertValid(keyLocator); + assertFalse(ccnxKeyLocator_IsKey(keyLocator), "Expected Iskey to be false"); + assertTrue(ccnxKeyLocator_IsKeyLink(keyLocator), "Expected IsKeyLink to be true"); + + ccnxKeyLocator_Release(&keyLocator); + ccnxName_Release(&keyURIName); + ccnxLink_Release(&keyLink); +} + +LONGBOW_TEST_CASE(Global, ccnxKeyLocator_ToString) +{ + CCNxName *keyURIName = ccnxName_CreateFromCString("lci://name/test"); + CCNxLink *keyLink = ccnxLink_Create(keyURIName, NULL, NULL); + CCNxKeyLocator *keyLocator = ccnxKeyLocator_CreateFromKeyLink(keyLink); + + ccnxKeyLocator_AssertValid(keyLocator); + + char *actualString = ccnxKeyLocator_ToString(keyLocator); + + assertTrue(0 == strncmp("KeyLocator", actualString, strlen("KeyLocator")), "ToString() did not return the expected prefix"); + + parcMemory_Deallocate((void **) &actualString); + + ccnxLink_Release(&keyLink); + ccnxName_Release(&keyURIName); + ccnxKeyLocator_Release(&keyLocator); +} + +LONGBOW_TEST_FIXTURE(Local) +{ + LONGBOW_RUN_TEST_CASE(Local, ccnxKeyLocator_Create); +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Local, ccnxKeyLocator_Create) +{ + CCNxName *keyURIName = ccnxName_CreateFromCString("lci://name"); + CCNxLink *keyLink = ccnxLink_Create(keyURIName, NULL, NULL); + CCNxKeyLocator *keyLocator = ccnxKeyLocator_CreateFromKeyLink(keyLink); + + ccnxKeyLocator_AssertValid(keyLocator); + + ccnxKeyLocator_Release(&keyLocator); + ccnxName_Release(&keyURIName); + ccnxLink_Release(&keyLink); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(ccnx_KeyLocator); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-common/ccnx/common/test/test_ccnx_KeystoreUtilities.c b/libccnx-common/ccnx/common/test/test_ccnx_KeystoreUtilities.c new file mode 100644 index 00000000..0ab770a5 --- /dev/null +++ b/libccnx-common/ccnx/common/test/test_ccnx_KeystoreUtilities.c @@ -0,0 +1,255 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../ccnx_KeystoreUtilities.c" + +#include <LongBow/unit-test.h> +#include <parc/algol/parc_SafeMemory.h> +#include <parc/security/parc_Security.h> + +#include <errno.h> +#include <ftw.h> + +typedef struct test_data { + char dirname[1024]; +} TestData; + +static int +deleteEntry(const char *fpath, const struct stat *sb, + int tflag, struct FTW *ftwbuf) +{ + if (tflag == FTW_DP) { + // directory in post-order + rmdir(fpath); + } else { + unlink(fpath); + } + return 0; /* To tell nftw() to continue */ +} + +__attribute__((unused)) +static void +recursiveDelete(const char *path) +{ + // only allow under tmp + assertTrue(strncmp(path, "/tmp/", 5) == 0, "Path must begin with /tmp/: %s", path); + // dont allow ".." + assertNull(strstr(path, ".."), "Path cannot have .. in it: %s", path); + + // depth first, dont't follow symlinks + int flags = FTW_DEPTH | FTW_PHYS; + + // maximum 20 fds open at a time + int max_fd = 20; + + int failure = nftw(path, deleteEntry, max_fd, flags); + assertFalse(failure, "Error on recursive delete: (%d) %s", errno, strerror(errno)); +} + +static TestData * +commonSetup(const char *testCaseName) +{ + TestData *data = parcMemory_AllocateAndClear(sizeof(TestData)); + assertNotNull(data, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(TestData)); + sprintf(data->dirname, "/tmp/%s.%d", testCaseName, getpid()); + mkdir(data->dirname, S_IRWXU | S_IRWXG); + setenv("HOME", data->dirname, 1); + + return data; +} + +static void +commonTeardown(TestData **dataPtr) +{ + TestData *data = *dataPtr; + + recursiveDelete(data->dirname); + parcMemory_Deallocate((void **) &data); + *dataPtr = NULL; +} + +LONGBOW_TEST_RUNNER(ccnx_KeystoreUtilities) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(ccnx_KeystoreUtilities) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(ccnx_KeystoreUtilities) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + TestData *data = commonSetup(longBowTestCase_GetName(testCase)); + longBowTestCase_SetClipBoardData(testCase, data); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + commonTeardown(&data); + if (parcSafeMemory_ReportAllocation(STDOUT_FILENO) != 0) { + printf("('%s' leaks memory by %d (allocs - frees)) ", longBowTestCase_GetName(testCase), parcMemory_Outstanding()); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Local) +{ + LONGBOW_RUN_TEST_CASE(Local, ccnxKeystoreUtilities_ConstructPath); + LONGBOW_RUN_TEST_CASE(Local, ccnxKeystoreUtilities_HomeDirectoryFromEnv); + LONGBOW_RUN_TEST_CASE(Local, ccnxKeystoreUtilities_HomeDirectoryFromPasswd); + + LONGBOW_RUN_TEST_CASE(Local, ccnxKeystoreUtilities_OpenFromHomeDirectory_Missing); + + LONGBOW_RUN_TEST_CASE(Local, ccnxKeystoreUtilities_OpenFromHomeDirectory_Newfile); + LONGBOW_RUN_TEST_CASE(Local, ccnxKeystoreUtilities_OpenFromHomeDirectory_Oldfile); +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + parcSecurity_Init(); + + TestData *data = commonSetup(longBowTestCase_GetName(testCase)); + longBowTestCase_SetClipBoardData(testCase, data); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + commonTeardown(&data); + + parcSecurity_Fini(); + + if (parcSafeMemory_ReportAllocation(STDOUT_FILENO) != 0) { + printf("('%s' leaks memory by %d (allocs - frees)) ", longBowTestCase_GetName(testCase), parcMemory_Outstanding()); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Local, ccnxKeystoreUtilities_ConstructPath) +{ + const char *dir = "/some/where"; + const char *file = "else"; + const char *truth = "/some/where/else"; + + char *test = ccnxKeystoreUtilities_ConstructPath(dir, file); + assertTrue(strcmp(truth, test) == 0, "Wrong path, expected %s got %s", truth, test); + parcMemory_Deallocate((void **) &test); +} + +LONGBOW_TEST_CASE(Local, ccnxKeystoreUtilities_HomeDirectoryFromEnv) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + char *dir = ccnxKeystoreUtilities_HomeDirectoryFromEnv(); + assertNotNull(dir, "Did not get HOME variable from environment"); + assertTrue(strcmp(dir, data->dirname) == 0, "HOME directory not correct, expecting %s got %s\n", data->dirname, dir); + parcMemory_Deallocate((void **) &dir); +} + +LONGBOW_TEST_CASE(Local, ccnxKeystoreUtilities_HomeDirectoryFromPasswd) +{ + char *dir = ccnxKeystoreUtilities_HomeDirectoryFromPasswd(); + assertNotNull(dir, "Did not get HOME variable from environment"); + parcMemory_Deallocate((void **) &dir); +} + +/** + * Create a temporary directory, set HOME to it, then try to open, but do + * not create the keystore. should return NULL. + */ +LONGBOW_TEST_CASE(Local, ccnxKeystoreUtilities_OpenFromHomeDirectory_Missing) +{ + KeystoreParams *params = ccnxKeystoreUtilities_OpenFromHomeDirectory("abcd"); + assertNull(params, "Signer should have been null opening from non-existent keystore"); +} + + +/** + * Create a keystore with the old default name in the old location + */ +LONGBOW_TEST_CASE(Local, ccnxKeystoreUtilities_OpenFromHomeDirectory_Oldfile) +{ + char *homedir = ccnxKeystoreUtilities_GetHomeDirectory(); + char *ccnxdir = ccnxKeystoreUtilities_ConstructPath(homedir, ".ccnx"); + mkdir(ccnxdir, 0700); + char *path = ccnxKeystoreUtilities_ConstructPath(ccnxdir, ".ccnx_keystore"); + + bool success = parcPkcs12KeyStore_CreateFile(path, "1234", "ccnxuser", 1024, 365); + assertTrue(success, "parcPkcs12KeyStore_CreateFile() failed."); + + KeystoreParams *signer = ccnxKeystoreUtilities_OpenFromHomeDirectory("1234"); + assertNotNull(signer, "Signer should be non-null opening from a file we just created"); + keystoreParams_Destroy(&signer); + + parcMemory_Deallocate((void **) &path); + parcMemory_Deallocate((void **) &ccnxdir); + parcMemory_Deallocate((void **) &homedir); +} + +/** + * Create a keystore with the new default name in the old location + */ +LONGBOW_TEST_CASE(Local, ccnxKeystoreUtilities_OpenFromHomeDirectory_Newfile) +{ + char *homedir = ccnxKeystoreUtilities_GetHomeDirectory(); + char *ccnxdir = ccnxKeystoreUtilities_ConstructPath(homedir, ".ccnx"); + mkdir(ccnxdir, 0700); + char *path = ccnxKeystoreUtilities_ConstructPath(ccnxdir, ".ccnx_keystore.p12"); + + bool success = parcPkcs12KeyStore_CreateFile(path, "1234", "ccnxuser", 1024, 365); + assertTrue(success, "parcPkcs12KeyStore_CreateFile() failed."); + + KeystoreParams *signer = ccnxKeystoreUtilities_OpenFromHomeDirectory("1234"); + assertNotNull(signer, "Signer should be non-null opening from a file we just created"); + keystoreParams_Destroy(&signer); + + parcMemory_Deallocate((void **) &path); + parcMemory_Deallocate((void **) &ccnxdir); + parcMemory_Deallocate((void **) &homedir); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(ccnx_KeystoreUtilities); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-common/ccnx/common/test/test_ccnx_Link.c b/libccnx-common/ccnx/common/test/test_ccnx_Link.c new file mode 100755 index 00000000..87105468 --- /dev/null +++ b/libccnx-common/ccnx/common/test/test_ccnx_Link.c @@ -0,0 +1,296 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. + +#include <ccnx/common/ccnx_Link.c> + +#include <LongBow/unit-test.h> + +#include <parc/algol/parc_Object.h> +#include <parc/algol/parc_SafeMemory.h> +#include <parc/algol/parc_ArrayList.h> +#include <parc/testing/parc_ObjectTesting.h> + +LONGBOW_TEST_RUNNER(ccnx_Link) +{ + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(ccnx_Link) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(ccnx_Link) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, ccnxLink_Create_Full); + LONGBOW_RUN_TEST_CASE(Global, ccnxLink_Create_EmptyKeyID); + LONGBOW_RUN_TEST_CASE(Global, ccnxLink_Create_EmptyContentObjectHash); + LONGBOW_RUN_TEST_CASE(Global, ccnxLink_Create_EmptyBoth); + LONGBOW_RUN_TEST_CASE(Global, ccnxLink_AcquireRelease); + + LONGBOW_RUN_TEST_CASE(Global, ccnxLink_GetName); + LONGBOW_RUN_TEST_CASE(Global, ccnxLink_GetKeyID); + LONGBOW_RUN_TEST_CASE(Global, ccnxLink_GetContentObjectHash); + + LONGBOW_RUN_TEST_CASE(Global, ccnxLink_Create_ToString); + + LONGBOW_RUN_TEST_CASE(Global, ccnxLink_Equals); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, ccnxLink_Create_Full) +{ + CCNxName *name = ccnxName_CreateFromCString("lci:/foo/bar/name"); + PARCBuffer *keyId = parcBuffer_Allocate(10); + PARCBuffer *contentObjectHash = parcBuffer_Allocate(10); + + CCNxLink *object = ccnxLink_Create(name, keyId, contentObjectHash); + + assertNotNull(object, "Expected non-null return value."); + + ccnxLink_Release(&object); + + ccnxName_Release(&name); + parcBuffer_Release(&keyId); + parcBuffer_Release(&contentObjectHash); +} + +LONGBOW_TEST_CASE(Global, ccnxLink_Create_EmptyKeyID) +{ + CCNxName *name = ccnxName_CreateFromCString("lci:/foo/bar/name"); + PARCBuffer *keyId = NULL; + PARCBuffer *contentObjectHash = parcBuffer_Allocate(10); + + CCNxLink *object = ccnxLink_Create(name, keyId, contentObjectHash); + + assertNotNull(object, "Expected non-null return value."); + + ccnxLink_Release(&object); + + ccnxName_Release(&name); + parcBuffer_Release(&contentObjectHash); +} + +LONGBOW_TEST_CASE(Global, ccnxLink_Create_EmptyContentObjectHash) +{ + CCNxName *name = ccnxName_CreateFromCString("lci:/foo/bar/name"); + PARCBuffer *keyId = parcBuffer_Allocate(10); + PARCBuffer *contentObjectHash = NULL; + + CCNxLink *object = ccnxLink_Create(name, keyId, contentObjectHash); + + assertNotNull(object, "Expected non-null return value."); + + ccnxLink_Release(&object); + + ccnxName_Release(&name); + parcBuffer_Release(&keyId); +} + +LONGBOW_TEST_CASE(Global, ccnxLink_Create_EmptyBoth) +{ + CCNxName *name = ccnxName_CreateFromCString("lci:/foo/bar/name"); + PARCBuffer *keyId = NULL; + PARCBuffer *contentObjectHash = NULL; + + CCNxLink *object = ccnxLink_Create(name, keyId, contentObjectHash); + + assertNotNull(object, "Expected non-null return value."); + + ccnxLink_Release(&object); + + ccnxName_Release(&name); +} + +LONGBOW_TEST_CASE(Global, ccnxLink_AcquireRelease) +{ + CCNxName *name = ccnxName_CreateFromCString("lci:/foo/bar/name"); + PARCBuffer *keyId = parcBuffer_Allocate(10); + PARCBuffer *contentObjectHash = parcBuffer_Allocate(10); + + CCNxLink *object = ccnxLink_Create(name, keyId, contentObjectHash); + parcObjectTesting_AssertAcquireReleaseContract(ccnxLink_Acquire, object); + + ccnxLink_Release(&object); + + ccnxName_Release(&name); + parcBuffer_Release(&keyId); + parcBuffer_Release(&contentObjectHash); +} + +LONGBOW_TEST_CASE(Global, ccnxLink_GetName) +{ + CCNxName *name = ccnxName_CreateFromCString("lci:/foo/bar/name"); + PARCBuffer *keyId = parcBuffer_Allocate(10); + PARCBuffer *contentObjectHash = parcBuffer_Allocate(10); + + CCNxLink *object = ccnxLink_Create(name, keyId, contentObjectHash); + + const CCNxName *actualName = ccnxLink_GetName(object); + assertNotNull(actualName, "Expected non-null return value."); + assertTrue(ccnxName_Equals(name, actualName), "Expected the same name back"); + + ccnxLink_Release(&object); + + ccnxName_Release(&name); + parcBuffer_Release(&keyId); + parcBuffer_Release(&contentObjectHash); +} + +LONGBOW_TEST_CASE(Global, ccnxLink_GetKeyID) +{ + CCNxName *name = ccnxName_CreateFromCString("lci:/foo/bar/name"); + PARCBuffer *keyId = parcBuffer_Allocate(10); + PARCBuffer *contentObjectHash = parcBuffer_Allocate(20); + + CCNxLink *object = ccnxLink_Create(name, keyId, contentObjectHash); + + PARCBuffer *buffer = ccnxLink_GetKeyID(object); + assertNotNull(buffer, "Expected non-null return value."); + assertTrue(parcBuffer_Capacity(buffer) == 10, "Expected the same buffer size back"); + + ccnxLink_Release(&object); + + ccnxName_Release(&name); + parcBuffer_Release(&keyId); + parcBuffer_Release(&contentObjectHash); +} + +LONGBOW_TEST_CASE(Global, ccnxLink_GetContentObjectHash) +{ + CCNxName *name = ccnxName_CreateFromCString("lci:/foo/bar/name"); + PARCBuffer *keyId = parcBuffer_Allocate(10); + PARCBuffer *contentObjectHash = parcBuffer_Allocate(20); + + CCNxLink *object = ccnxLink_Create(name, keyId, contentObjectHash); + + PARCBuffer *buffer = ccnxLink_GetContentObjectHash(object); + assertNotNull(buffer, "Expected non-null return value."); + assertTrue(parcBuffer_Capacity(buffer) == 20, "Expected the same buffer size back"); + + ccnxLink_Release(&object); + + ccnxName_Release(&name); + parcBuffer_Release(&keyId); + parcBuffer_Release(&contentObjectHash); +} + +LONGBOW_TEST_CASE(Global, ccnxLink_Equals) +{ + CCNxName *name = ccnxName_CreateFromCString("lci:/foo/bar/name"); + PARCBuffer *keyId = parcBuffer_Allocate(10); + PARCBuffer *contentObjectHash = parcBuffer_Allocate(20); + CCNxLink *x = ccnxLink_Create(name, keyId, contentObjectHash); + ccnxName_Release(&name); + parcBuffer_Release(&keyId); + parcBuffer_Release(&contentObjectHash); + + name = ccnxName_CreateFromCString("lci:/foo/bar/name"); + keyId = parcBuffer_Allocate(10); + contentObjectHash = parcBuffer_Allocate(20); + CCNxLink *y = ccnxLink_Create(name, keyId, contentObjectHash); + ccnxName_Release(&name); + parcBuffer_Release(&keyId); + parcBuffer_Release(&contentObjectHash); + + name = ccnxName_CreateFromCString("lci:/foo/bar/name"); + keyId = parcBuffer_Allocate(10); + contentObjectHash = parcBuffer_Allocate(20); + CCNxLink *z = ccnxLink_Create(name, keyId, contentObjectHash); + ccnxName_Release(&name); + parcBuffer_Release(&keyId); + parcBuffer_Release(&contentObjectHash); + + name = ccnxName_CreateFromCString("lci:/foo/bar/othername"); + keyId = parcBuffer_Allocate(10); + contentObjectHash = parcBuffer_Allocate(20); + CCNxLink *unequal1 = ccnxLink_Create(name, keyId, contentObjectHash); + ccnxName_Release(&name); + parcBuffer_Release(&keyId); + parcBuffer_Release(&contentObjectHash); + + name = ccnxName_CreateFromCString("lci:/foo/bar/name"); + keyId = NULL; + contentObjectHash = parcBuffer_Allocate(20); + CCNxLink *unequal2 = ccnxLink_Create(name, keyId, contentObjectHash); + ccnxName_Release(&name); + parcBuffer_Release(&contentObjectHash); + + name = ccnxName_CreateFromCString("lci:/foo/bar/name"); + keyId = parcBuffer_Allocate(10); + contentObjectHash = NULL; + CCNxLink *unequal3 = ccnxLink_Create(name, keyId, contentObjectHash); + ccnxName_Release(&name); + parcBuffer_Release(&keyId); + + assertEqualsContract(ccnxLink_Equals, x, y, z, unequal1, unequal2, unequal3); + + ccnxLink_Release(&x); + ccnxLink_Release(&y); + ccnxLink_Release(&z); + ccnxLink_Release(&unequal1); + ccnxLink_Release(&unequal2); + ccnxLink_Release(&unequal3); +} + +LONGBOW_TEST_CASE(Global, ccnxLink_Create_ToString) +{ + CCNxName *name = ccnxName_CreateFromCString("lci:/foo/bar/name"); + PARCBuffer *keyId = parcBuffer_Allocate(10); + PARCBuffer *contentObjectHash = parcBuffer_Allocate(20); + CCNxLink *object = ccnxLink_Create(name, keyId, contentObjectHash); + + char *string = ccnxLink_ToString(object); + assertNotNull(string, "Expected non-null string."); + parcMemory_Deallocate((void **) &string); + + ccnxLink_Release(&object); + + ccnxName_Release(&name); + parcBuffer_Release(&keyId); + parcBuffer_Release(&contentObjectHash); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(ccnx_Link); + int exitStatus = LONGBOW_TEST_MAIN(argc, argv, testRunner); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-common/ccnx/common/test/test_ccnx_Manifest.c b/libccnx-common/ccnx/common/test/test_ccnx_Manifest.c new file mode 100755 index 00000000..583c9a79 --- /dev/null +++ b/libccnx-common/ccnx/common/test/test_ccnx_Manifest.c @@ -0,0 +1,359 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../ccnx_Manifest.c" + +#include <LongBow/unit-test.h> +#include <parc/algol/parc_SafeMemory.h> + +#include <parc/testing/parc_ObjectTesting.h> + +#include <ccnx/common/ccnx_Interest.h> + +typedef struct test_data { + CCNxManifest *object; + CCNxManifest *manifestWithNamelessGroup; + CCNxManifest *nameless; + PARCLinkedList *interestListFromGroupLocator; + PARCLinkedList *interestListFromManifestLocator; + PARCLinkedList *interestListFromOverrideLocator; + CCNxName *overrideLocator; +} ManifestTestData; + +static ManifestTestData * +_commonSetup(void) +{ + ManifestTestData *data = parcMemory_AllocateAndClear(sizeof(ManifestTestData)); + + data->overrideLocator = ccnxName_CreateFromCString("ccnx:/override"); + + CCNxName *name = ccnxName_CreateFromCString("ccnx:/my/manifest"); + CCNxManifest *manifest = ccnxManifest_Create(name); + CCNxManifest *nameless = ccnxManifest_CreateNameless(); + CCNxManifest *manifestWithNamelessGroup = ccnxManifest_Create(name); + + data->interestListFromGroupLocator = parcLinkedList_Create(); + data->interestListFromManifestLocator = parcLinkedList_Create(); + data->interestListFromOverrideLocator = parcLinkedList_Create(); + data->object = manifest; + data->nameless = nameless; + data->manifestWithNamelessGroup = manifestWithNamelessGroup; + + CCNxManifestHashGroup *group = ccnxManifestHashGroup_Create(); + CCNxManifestHashGroup *namelessGroup = ccnxManifestHashGroup_Create(); + + CCNxName *locator = ccnxName_CreateFromCString("ccnx:/locator"); + ccnxManifestHashGroup_SetLocator(group, locator); + + // Create pointers for the pieces of data + PARCBuffer *digest1 = parcBuffer_Allocate(32); + PARCBuffer *digest2 = parcBuffer_Allocate(32); + ccnxManifestHashGroup_AppendPointer(group, CCNxManifestHashGroupPointerType_Data, digest1); + ccnxManifestHashGroup_AppendPointer(namelessGroup, CCNxManifestHashGroupPointerType_Data, digest1); + ccnxManifestHashGroup_AppendPointer(group, CCNxManifestHashGroupPointerType_Manifest, digest2); + ccnxManifestHashGroup_AppendPointer(namelessGroup, CCNxManifestHashGroupPointerType_Data, digest2); + + // Create the corresponding interests based on the three locator cases + // 1. The locator is inherited from the hash group + // 2. The locator is inherited from the manifest + // 3. The locator is overridden + CCNxInterest *interest1 = ccnxInterest_CreateSimple(locator); + ccnxInterest_SetContentObjectHashRestriction(interest1, digest1); + parcLinkedList_Append(data->interestListFromGroupLocator, interest1); + ccnxInterest_Release(&interest1); + + interest1 = ccnxInterest_CreateSimple(name); + ccnxInterest_SetContentObjectHashRestriction(interest1, digest1); + parcLinkedList_Append(data->interestListFromManifestLocator, interest1); + ccnxInterest_Release(&interest1); + + interest1 = ccnxInterest_CreateSimple(data->overrideLocator); + ccnxInterest_SetContentObjectHashRestriction(interest1, digest1); + parcLinkedList_Append(data->interestListFromOverrideLocator, interest1); + ccnxInterest_Release(&interest1); + + CCNxInterest *interest2 = ccnxInterest_CreateSimple(locator); + ccnxInterest_SetContentObjectHashRestriction(interest2, digest2); + parcLinkedList_Append(data->interestListFromGroupLocator, interest2); + ccnxInterest_Release(&interest2); + + interest2 = ccnxInterest_CreateSimple(name); + ccnxInterest_SetContentObjectHashRestriction(interest2, digest2); + parcLinkedList_Append(data->interestListFromManifestLocator, interest2); + ccnxInterest_Release(&interest2); + + interest2 = ccnxInterest_CreateSimple(data->overrideLocator); + ccnxInterest_SetContentObjectHashRestriction(interest2, digest2); + parcLinkedList_Append(data->interestListFromOverrideLocator, interest2); + ccnxInterest_Release(&interest2); + + ccnxName_Release(&name); + ccnxName_Release(&locator); + parcBuffer_Release(&digest1); + parcBuffer_Release(&digest2); + + ccnxManifest_AddHashGroup(manifest, group); + ccnxManifest_AddHashGroup(manifestWithNamelessGroup, namelessGroup); + ccnxManifest_AddHashGroup(nameless, namelessGroup); + + ccnxManifestHashGroup_Release(&group); + ccnxManifestHashGroup_Release(&namelessGroup); + + return data; +} + +static void +_commonTeardown(ManifestTestData *data) +{ + ccnxManifest_Release(&data->object); + ccnxManifest_Release(&data->nameless); + ccnxManifest_Release(&data->manifestWithNamelessGroup); + + parcLinkedList_Release(&data->interestListFromGroupLocator); + parcLinkedList_Release(&data->interestListFromManifestLocator); + parcLinkedList_Release(&data->interestListFromOverrideLocator); + + ccnxName_Release(&data->overrideLocator); + parcMemory_Deallocate((void **) &data); +} + +LONGBOW_TEST_RUNNER(ccnx_Manifest) +{ + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(ccnx_Manifest) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(ccnx_Manifest) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, ccnxManifest_AcquireRelease); + LONGBOW_RUN_TEST_CASE(Global, ccnxManifest_Create); + LONGBOW_RUN_TEST_CASE(Global, ccnxManifest_AddHashGroup); + LONGBOW_RUN_TEST_CASE(Global, ccnxManifest_GetHashGroup); + LONGBOW_RUN_TEST_CASE(Global, ccnxManifest_GetNumberOfHashGroups); + LONGBOW_RUN_TEST_CASE(Global, ccnxManifest_GetName); + LONGBOW_RUN_TEST_CASE(Global, ccnxManifest_Equals); + LONGBOW_RUN_TEST_CASE(Global, ccnxManifest_ToString); + + LONGBOW_RUN_TEST_CASE(Global, ccnxManifest_CreateInterestList_OverrideLocator); + // LONGBOW_RUN_TEST_CASE(Global, ccnxManifest_CreateInterestList_ManifestLocator); + // LONGBOW_RUN_TEST_CASE(Global, ccnxManifest_CreateInterestList_GroupLocator); + // LONGBOW_RUN_TEST_CASE(Global, ccnxManifest_CreateInterestList_NoLocator); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + longBowTestCase_SetClipBoardData(testCase, _commonSetup()); + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + _commonTeardown(longBowTestCase_GetClipBoardData(testCase)); + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, ccnxManifest_AcquireRelease) +{ + CCNxName *name = ccnxName_CreateFromCString("ccnx:/my/manifest"); + CCNxManifest *manifest = ccnxManifest_Create(name); + ccnxName_Release(&name); + + parcObjectTesting_AssertAcquireReleaseContract(ccnxManifest_Acquire, manifest); + + ccnxManifest_Release(&manifest); +} + +LONGBOW_TEST_CASE(Global, ccnxManifest_Create) +{ + CCNxName *name = ccnxName_CreateFromCString("ccnx:/my/manifest"); + CCNxManifest *manifest = ccnxManifest_Create(name); + + assertNotNull(manifest, "Expected the Manifest to be created without errors."); + const CCNxName *copy = ccnxManifest_GetName(manifest); + + assertTrue(ccnxName_Equals(name, copy), "Expected name to equal %s, got %s", ccnxName_ToString(name), ccnxName_ToString(copy)); + + ccnxName_Release(&name); + ccnxManifest_Release(&manifest); +} + +LONGBOW_TEST_CASE(Global, ccnxManifest_AddHashGroup) +{ + ManifestTestData *data = longBowTestCase_GetClipBoardData(testCase); + CCNxManifest *manifest = data->object; + + size_t numGroups = ccnxManifest_GetNumberOfHashGroups(manifest); + + CCNxManifestHashGroup *group = ccnxManifestHashGroup_Create(); + ccnxManifest_AddHashGroup(manifest, group); + + size_t expected = numGroups + 1; + size_t actual = ccnxManifest_GetNumberOfHashGroups(manifest); + assertTrue(actual == expected, "Expected %zu, got %zu", expected, actual); + + ccnxManifestHashGroup_Release(&group); +} + +LONGBOW_TEST_CASE(Global, ccnxManifest_GetHashGroup) +{ + ManifestTestData *data = longBowTestCase_GetClipBoardData(testCase); + CCNxManifest *manifest = data->object; + + CCNxManifestHashGroup *group = ccnxManifest_GetHashGroupByIndex(manifest, 0); + CCNxName *expected = ccnxName_CreateFromCString("ccnx:/locator"); + const CCNxName *actual = ccnxManifestHashGroup_GetLocator(group); + assertTrue(ccnxName_Equals(expected, actual), "Expected %s, got %s", ccnxName_ToString(expected), ccnxName_ToString(actual)); + + ccnxName_Release(&expected); + ccnxManifestHashGroup_Release(&group); +} + +LONGBOW_TEST_CASE(Global, ccnxManifest_GetNumberOfHashGroups) +{ + ManifestTestData *data = longBowTestCase_GetClipBoardData(testCase); + CCNxManifest *manifest = data->object; + + size_t before = ccnxManifest_GetNumberOfHashGroups(manifest); + CCNxManifestHashGroup *group = ccnxManifestHashGroup_Create(); + ccnxManifest_AddHashGroup(manifest, group); + + size_t actual = ccnxManifest_GetNumberOfHashGroups(manifest); + size_t expected = before + 1; + + assertTrue(expected == actual, "Expected %zu, got %zu", expected, actual); + + ccnxManifestHashGroup_Release(&group); +} + +LONGBOW_TEST_CASE(Global, ccnxManifest_CreateInterestList_NoLocator) +{ + ManifestTestData *data = longBowTestCase_GetClipBoardData(testCase); + CCNxManifest *nameless = data->nameless; + + PARCLinkedList *interestList = ccnxManifest_CreateInterestList(nameless, NULL); + assertTrue(parcLinkedList_Size(interestList) == 0, "Expected the interest list to be empty since there was no valid locator"); + parcLinkedList_Release(&interestList); +} + +LONGBOW_TEST_CASE(Global, ccnxManifest_CreateInterestList_GroupLocator) +{ + ManifestTestData *data = longBowTestCase_GetClipBoardData(testCase); + CCNxManifest *manifest = data->object; + + PARCLinkedList *interestList = ccnxManifest_CreateInterestList(manifest, NULL); + assertTrue(parcLinkedList_Equals(interestList, data->interestListFromGroupLocator), "Expected the interest lists to be equal"); + parcLinkedList_Release(&interestList); +} + +LONGBOW_TEST_CASE(Global, ccnxManifest_CreateInterestList_ManifestLocator) +{ + ManifestTestData *data = longBowTestCase_GetClipBoardData(testCase); + CCNxManifest *manifestWithNamelessGroup = data->manifestWithNamelessGroup; + + PARCLinkedList *interestList = ccnxManifest_CreateInterestList(manifestWithNamelessGroup, NULL); + assertTrue(parcLinkedList_Equals(interestList, data->interestListFromManifestLocator), "Expected the interest lists to be equal"); + parcLinkedList_Release(&interestList); +} + +LONGBOW_TEST_CASE(Global, ccnxManifest_CreateInterestList_OverrideLocator) +{ + ManifestTestData *data = longBowTestCase_GetClipBoardData(testCase); + CCNxManifest *nameless = data->nameless; + CCNxName *overrideLocator = data->overrideLocator; + + PARCLinkedList *interestList = ccnxManifest_CreateInterestList(nameless, overrideLocator); + assertTrue(parcLinkedList_Equals(interestList, data->interestListFromOverrideLocator), "Expected the interest lists to be equal"); + parcLinkedList_Release(&interestList); +} + +LONGBOW_TEST_CASE(Global, ccnxManifest_GetName) +{ + CCNxName *name = ccnxName_CreateFromCString("ccnx:/my/manifest"); + CCNxManifest *manifest = ccnxManifest_Create(name); + + assertNotNull(manifest, "Expected the Manifest to be created without errors."); + const CCNxName *copy = ccnxManifest_GetName(manifest); + + assertTrue(ccnxName_Equals(name, copy), "Expected name to equal %s, got %s", ccnxName_ToString(name), ccnxName_ToString(copy)); + + ccnxName_Release(&name); + ccnxManifest_Release(&manifest); +} + +LONGBOW_TEST_CASE(Global, ccnxManifest_Equals) +{ + CCNxName *name = ccnxName_CreateFromCString("ccnx:/my/manifest"); + CCNxManifest *x = ccnxManifest_Create(name); + CCNxManifest *y = ccnxManifest_Create(name); + CCNxManifest *z = ccnxManifest_Create(name); + + CCNxName *name1 = ccnxName_CreateFromCString("ccnx:/not/my/manifest"); + CCNxManifest *u1 = ccnxManifest_Create(name1); + + parcObjectTesting_AssertEqualsFunction(ccnxManifest_Equals, x, y, z, u1, NULL); + + ccnxName_Release(&name); + ccnxName_Release(&name1); + ccnxManifest_Release(&x); + ccnxManifest_Release(&y); + ccnxManifest_Release(&z); + ccnxManifest_Release(&u1); +} + +LONGBOW_TEST_CASE(Global, ccnxManifest_ToString) +{ + CCNxName *name = ccnxName_CreateFromCString("ccnx:/my/manifest"); + CCNxManifest *manifest = ccnxManifest_Create(name); + + assertNotNull(manifest, "Expected the Manifest to be created without errors."); + const CCNxName *copy = ccnxManifest_GetName(manifest); + + assertTrue(ccnxName_Equals(name, copy), "Expected name to equal %s, got %s", ccnxName_ToString(name), ccnxName_ToString(copy)); + + ccnxName_Release(&name); + ccnxManifest_Release(&manifest); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(ccnx_Manifest); + int exitStatus = LONGBOW_TEST_MAIN(argc, argv, testRunner); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-common/ccnx/common/test/test_ccnx_ManifestHashGroup.c b/libccnx-common/ccnx/common/test/test_ccnx_ManifestHashGroup.c new file mode 100644 index 00000000..c09fa63d --- /dev/null +++ b/libccnx-common/ccnx/common/test/test_ccnx_ManifestHashGroup.c @@ -0,0 +1,515 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../ccnx_ManifestHashGroup.c" + +#include <inttypes.h> +#include <ccnx/common/ccnx_Manifest.h> + +#include <ccnx/common/ccnx_Name.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_PacketEncoder.h> +#include <ccnx/common/codec/ccnxCodec_TlvPacket.h> +#include <ccnx/common/ccnx_WireFormatMessage.h> + +#include <LongBow/unit-test.h> +#include <parc/algol/parc_SafeMemory.h> +#include <parc/algol/parc_Object.h> +#include <parc/algol/parc_ArrayList.h> + +#include <parc/testing/parc_ObjectTesting.h> + +LONGBOW_TEST_RUNNER(ccnx_ManifestHashGroup) +{ + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(ccnx_ManifestHashGroup) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(ccnx_ManifestHashGroup) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, ccnxManifestHashGroup_AcquireRelease); + LONGBOW_RUN_TEST_CASE(Global, ccnxManifestHashGroup_Create); + LONGBOW_RUN_TEST_CASE(Global, ccnxManifestHashGroup_CreateFromJson); + LONGBOW_RUN_TEST_CASE(Global, ccnxManifestHashGroup_AppendGetPointer); + LONGBOW_RUN_TEST_CASE(Global, ccnxManifestHashGroup_PrependGetPointer); + LONGBOW_RUN_TEST_CASE(Global, ccnxManifestHashGroup_ToString); + LONGBOW_RUN_TEST_CASE(Global, ccnxManifestHashGroup_ToJson); + LONGBOW_RUN_TEST_CASE(Global, ccnxManifestHashGroup_IsFull); + + LONGBOW_RUN_TEST_CASE(Global, ccnxManifestHashGroup_CreateInterestList_GroupLocator); + LONGBOW_RUN_TEST_CASE(Global, ccnxManifestHashGroup_CreateInterestList_OverrideLocator); + LONGBOW_RUN_TEST_CASE(Global, ccnxManifestHashGroup_CreateInterestList_NoLocator); + + LONGBOW_RUN_TEST_CASE(Global, ccnxManifestHashGroup_Equals); + LONGBOW_RUN_TEST_CASE(Global, ccnxManifestHashGroup_Iterator); + + // Metadata + LONGBOW_RUN_TEST_CASE(Global, ccnxManifestHashGroup_BlockSize); + LONGBOW_RUN_TEST_CASE(Global, ccnxManifestHashGroup_DataSize); + LONGBOW_RUN_TEST_CASE(Global, ccnxManifestHashGroup_EntrySize); + LONGBOW_RUN_TEST_CASE(Global, ccnxManifestHashGroup_TreeHeight); + LONGBOW_RUN_TEST_CASE(Global, ccnxManifestHashGroup_Locator); + LONGBOW_RUN_TEST_CASE(Global, ccnxManifestHashGroup_OverallDataDigest); + LONGBOW_RUN_TEST_CASE(Global, ccnxManifestHashGroup_HasMetadata); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, ccnxManifestHashGroup_AcquireRelease) +{ + CCNxManifestHashGroup *group = ccnxManifestHashGroup_Create(); + assertNotNull(group, "Expected non-null CCNxManifestHashGroup"); + + parcObjectTesting_AssertAcquireReleaseContract(ccnxManifestHashGroup_Acquire, group); + + ccnxManifestHashGroup_Release(&group); +} + +LONGBOW_TEST_CASE(Global, ccnxManifestHashGroup_Create) +{ + CCNxManifestHashGroup *group = ccnxManifestHashGroup_Create(); + assertNotNull(group, "Expected non-null CCNxManifestHashGroup"); + ccnxManifestHashGroup_Release(&group); +} + +LONGBOW_TEST_CASE(Global, ccnxManifestHashGroup_CreateFromJson) +{ + char *jsonString = "{ \"HashGroup\" : [ { \"type\" : 0, \"digest\" : \"FFFF\" } ] }"; + PARCJSON *json = parcJSON_ParseString(jsonString); + + CCNxManifestHashGroup *group = ccnxManifestHashGroup_CreateFromJson(json); + assertNotNull(group, "Expected non-null CCNxManifestHashGroup"); + + size_t actual = ccnxManifestHashGroup_GetNumberOfPointers(group); + size_t expected = 1; + + assertTrue(actual == expected, "Expected %zu pointers, got %zu", expected, actual); + + parcJSON_Release(&json); + ccnxManifestHashGroup_Release(&group); +} + +LONGBOW_TEST_CASE(Global, ccnxManifestHashGroup_AppendGetPointer) +{ + CCNxManifestHashGroup *group = ccnxManifestHashGroup_Create(); + assertNotNull(group, "Expected non-null CCNxManifestHashGroup"); + + PARCBuffer *buffer1 = parcBuffer_Allocate(32); + PARCBuffer *buffer2 = parcBuffer_Allocate(32); + + ccnxManifestHashGroup_AppendPointer(group, CCNxManifestHashGroupPointerType_Data, buffer1); + ccnxManifestHashGroup_AppendPointer(group, CCNxManifestHashGroupPointerType_Manifest, buffer2); + + size_t expected = 2; + size_t actual = ccnxManifestHashGroup_GetNumberOfPointers(group); + + assertTrue(expected == actual, "Expected %zu, got %zu", expected, actual); + assertTrue(ccnxManifestHashGroupPointer_GetType(ccnxManifestHashGroup_GetPointerAtIndex(group, 0)) == CCNxManifestHashGroupPointerType_Data, "Expected data in the first slot"); + assertTrue(ccnxManifestHashGroupPointer_GetType(ccnxManifestHashGroup_GetPointerAtIndex(group, 1)) == CCNxManifestHashGroupPointerType_Manifest, "Expected data in the first slot"); + + parcBuffer_Release(&buffer1); + parcBuffer_Release(&buffer2); + + ccnxManifestHashGroup_Release(&group); +} + +LONGBOW_TEST_CASE(Global, ccnxManifestHashGroup_PrependGetPointer) +{ + CCNxManifestHashGroup *group = ccnxManifestHashGroup_Create(); + assertNotNull(group, "Expected non-null CCNxManifestHashGroup"); + + PARCBuffer *buffer1 = parcBuffer_Allocate(32); + PARCBuffer *buffer2 = parcBuffer_Allocate(32); + + ccnxManifestHashGroup_PrependPointer(group, CCNxManifestHashGroupPointerType_Data, buffer1); + ccnxManifestHashGroup_PrependPointer(group, CCNxManifestHashGroupPointerType_Manifest, buffer2); + + size_t expected = 2; + size_t actual = ccnxManifestHashGroup_GetNumberOfPointers(group); + + assertTrue(expected == actual, "Expected %zu, got %zu", expected, actual); + assertTrue(ccnxManifestHashGroupPointer_GetType(ccnxManifestHashGroup_GetPointerAtIndex(group, 1)) == CCNxManifestHashGroupPointerType_Data, "Expected data in the first slot"); + assertTrue(ccnxManifestHashGroupPointer_GetType(ccnxManifestHashGroup_GetPointerAtIndex(group, 0)) == CCNxManifestHashGroupPointerType_Manifest, "Expected data in the first slot"); + + parcBuffer_Release(&buffer1); + parcBuffer_Release(&buffer2); + + ccnxManifestHashGroup_Release(&group); +} + +static CCNxManifestHashGroup * +_createHashGroup(CCNxName *locator, size_t n, size_t blockSize, size_t dataSize, size_t entrySize, size_t treeHeight) +{ + CCNxManifestHashGroup *group = ccnxManifestHashGroup_Create(); + + if (locator != NULL) { + ccnxManifestHashGroup_SetLocator(group, locator); + } + + for (size_t i = 0; i < n; i++) { + PARCBuffer *buffer = parcBuffer_Allocate(32); + ccnxManifestHashGroup_AppendPointer(group, CCNxManifestHashGroupPointerType_Data, buffer); + parcBuffer_Release(&buffer); + } + + if (blockSize != 0) { + ccnxManifestHashGroup_SetBlockSize(group, blockSize); + } + + if (dataSize != 0) { + ccnxManifestHashGroup_SetDataSize(group, dataSize); + } + + if (entrySize != 0) { + ccnxManifestHashGroup_SetEntrySize(group, entrySize); + } + + if (treeHeight != 0) { + ccnxManifestHashGroup_SetTreeHeight(group, treeHeight); + } + + return group; +} + + +LONGBOW_TEST_CASE(Global, ccnxManifestHashGroup_Equals) +{ + CCNxName *locator = ccnxName_CreateFromCString("ccnx:/my/manifest"); + + CCNxManifestHashGroup *x = _createHashGroup(locator, 10, 0, 0, 0, 0); + CCNxManifestHashGroup *y = _createHashGroup(locator, 10, 0, 0, 0, 0); + CCNxManifestHashGroup *z = _createHashGroup(locator, 10, 0, 0, 0, 0); + + CCNxManifestHashGroup *u1 = _createHashGroup(locator, 5, 0, 0, 0, 0); + CCNxManifestHashGroup *u2 = _createHashGroup(NULL, 10, 0, 0, 0, 0); + CCNxManifestHashGroup *u3 = _createHashGroup(locator, 10, 1, 0, 0, 0); + CCNxManifestHashGroup *u4 = _createHashGroup(locator, 10, 0, 1, 0, 0); + CCNxManifestHashGroup *u5 = _createHashGroup(locator, 10, 0, 0, 1, 0); + CCNxManifestHashGroup *u6 = _createHashGroup(locator, 10, 0, 0, 0, 1); + + parcObjectTesting_AssertEqualsFunction(ccnxManifestHashGroup_Equals, x, y, z, u1, u2, u3, u4, u5, u6, NULL); + + ccnxManifestHashGroup_Release(&x); + ccnxManifestHashGroup_Release(&y); + ccnxManifestHashGroup_Release(&z); + + ccnxManifestHashGroup_Release(&u1); + ccnxManifestHashGroup_Release(&u2); + ccnxManifestHashGroup_Release(&u3); + ccnxManifestHashGroup_Release(&u4); + ccnxManifestHashGroup_Release(&u5); + ccnxManifestHashGroup_Release(&u6); + + ccnxName_Release(&locator); +} + +LONGBOW_TEST_CASE(Global, ccnxManifestHashGroup_ToString) +{ + char *jsonString = "{ \"HashGroup\" : [ { \"type\" : 0, \"digest\" : \"617364617364617364\" } ] }"; + PARCJSON *json = parcJSON_ParseString(jsonString); + + CCNxManifestHashGroup *group = ccnxManifestHashGroup_CreateFromJson(json); + + char *stringForm = ccnxManifestHashGroup_ToString(group); + assertTrue(strcmp(jsonString, stringForm) == 0, "Expected %s and actual %s should be equal.", jsonString, stringForm); + + parcMemory_Deallocate(&stringForm); + parcJSON_Release(&json); + ccnxManifestHashGroup_Release(&group); +} + +LONGBOW_TEST_CASE(Global, ccnxManifestHashGroup_ToJson) +{ + char *jsonString = "{ \"HashGroup\" : [ { \"type\" : 0, \"digest\" : \"617364617364617364\" } ] }"; + PARCJSON *json = parcJSON_ParseString(jsonString); + + CCNxManifestHashGroup *group = ccnxManifestHashGroup_CreateFromJson(json); + PARCJSON *expected = ccnxManifestHashGroup_ToJson(group); + + assertTrue(parcJSON_Equals(json, expected), "Expected the input and output JSON to be identical"); + + parcJSON_Release(&expected); + parcJSON_Release(&json); + ccnxManifestHashGroup_Release(&group); +} + +LONGBOW_TEST_CASE(Global, ccnxManifestHashGroup_IsFull) +{ + CCNxManifestHashGroup *group = ccnxManifestHashGroup_Create(); + assertNotNull(group, "Expected non-null CCNxManifestHashGroup"); + + for (size_t i = 0; i < MAX_NUMBER_OF_POINTERS; i++) { + PARCBuffer *buffer = parcBuffer_Allocate(32); + assertTrue(ccnxManifestHashGroup_AppendPointer(group, CCNxManifestHashGroupPointerType_Data, buffer), "Expected the insertion to succeed"); + parcBuffer_Release(&buffer); + } + + PARCBuffer *buffer = parcBuffer_Allocate(32); + assertFalse(ccnxManifestHashGroup_AppendPointer(group, CCNxManifestHashGroupPointerType_Data, buffer), "Expected the insertion to fail since the HashGroup is full."); + parcBuffer_Release(&buffer); + + bool isFull = ccnxManifestHashGroup_IsFull(group); + assertTrue(isFull, "Expected the group to be full after %ul pointers", MAX_NUMBER_OF_POINTERS); + + ccnxManifestHashGroup_Release(&group); +} + +LONGBOW_TEST_CASE(Global, ccnxManifestHashGroup_CreateInterestList_OverrideLocator) +{ + CCNxManifestHashGroup *group = ccnxManifestHashGroup_Create(); + assertNotNull(group, "Expected non-null CCNxManifestHashGroup"); + + PARCLinkedList *interestList = parcLinkedList_Create(); + CCNxName *locator = ccnxName_CreateFromCString("ccnx:/locator"); + for (size_t i = 0; i < MAX_NUMBER_OF_POINTERS; i++) { + PARCBuffer *buffer = parcBuffer_Allocate(32); + assertTrue(ccnxManifestHashGroup_AppendPointer(group, CCNxManifestHashGroupPointerType_Data, buffer), "Expected the insertion to succeed"); + + CCNxInterest *interest = ccnxInterest_CreateSimple(locator); + ccnxInterest_SetContentObjectHashRestriction(interest, buffer); + parcLinkedList_Append(interestList, interest); + + ccnxInterest_Release(&interest); + parcBuffer_Release(&buffer); + } + + PARCLinkedList *extractedList = ccnxManifestHashGroup_CreateInterestList(group, locator); + assertTrue(parcLinkedList_Equals(interestList, extractedList), "Expected the interest lists to be equal"); + + parcLinkedList_Release(&interestList); + parcLinkedList_Release(&extractedList); + ccnxName_Release(&locator); + ccnxManifestHashGroup_Release(&group); +} + +LONGBOW_TEST_CASE(Global, ccnxManifestHashGroup_CreateInterestList_GroupLocator) +{ + CCNxManifestHashGroup *group = ccnxManifestHashGroup_Create(); + assertNotNull(group, "Expected non-null CCNxManifestHashGroup"); + + CCNxName *locator = ccnxName_CreateFromCString("ccnx:/group/locator"); + ccnxManifestHashGroup_SetLocator(group, locator); + + PARCLinkedList *interestList = parcLinkedList_Create(); + for (size_t i = 0; i < MAX_NUMBER_OF_POINTERS; i++) { + PARCBuffer *buffer = parcBuffer_Allocate(32); + assertTrue(ccnxManifestHashGroup_AppendPointer(group, CCNxManifestHashGroupPointerType_Data, buffer), "Expected the insertion to succeed"); + + CCNxInterest *interest = ccnxInterest_CreateSimple(locator); + ccnxInterest_SetContentObjectHashRestriction(interest, buffer); + parcLinkedList_Append(interestList, interest); + + ccnxInterest_Release(&interest); + parcBuffer_Release(&buffer); + } + + CCNxName *differentLocator = ccnxName_CreateFromCString("ccnx:/different/locator"); + PARCLinkedList *extractedList = ccnxManifestHashGroup_CreateInterestList(group, differentLocator); + ccnxName_Release(&differentLocator); + assertTrue(parcLinkedList_Equals(interestList, extractedList), "Expected the interest lists to be equal"); + + parcLinkedList_Release(&interestList); + parcLinkedList_Release(&extractedList); + ccnxName_Release(&locator); + ccnxManifestHashGroup_Release(&group); +} + +LONGBOW_TEST_CASE(Global, ccnxManifestHashGroup_CreateInterestList_NoLocator) +{ + CCNxManifestHashGroup *group = ccnxManifestHashGroup_Create(); + assertNotNull(group, "Expected non-null CCNxManifestHashGroup"); + + for (size_t i = 0; i < MAX_NUMBER_OF_POINTERS; i++) { + PARCBuffer *buffer = parcBuffer_Allocate(32); + assertTrue(ccnxManifestHashGroup_AppendPointer(group, CCNxManifestHashGroupPointerType_Data, buffer), "Expected the insertion to succeed"); + parcBuffer_Release(&buffer); + } + + PARCLinkedList *extractedList = ccnxManifestHashGroup_CreateInterestList(group, NULL); + assertTrue(parcLinkedList_Size(extractedList) == 0, "Expected the interest list to be empty since there was no valid locator"); + + parcLinkedList_Release(&extractedList); + ccnxManifestHashGroup_Release(&group); +} + +LONGBOW_TEST_CASE(Global, ccnxManifestHashGroup_BlockSize) +{ + CCNxManifestHashGroup *group = ccnxManifestHashGroup_Create(); + assertNotNull(group, "Expected non-null CCNxManifestHashGroup"); + + size_t blockSize = 10; + ccnxManifestHashGroup_SetBlockSize(group, blockSize); + size_t actual = ccnxManifestHashGroup_GetBlockSize(group); + + assertTrue(blockSize == actual, "Expected %zu, got %zu", blockSize, actual); + + ccnxManifestHashGroup_Release(&group); +} + +LONGBOW_TEST_CASE(Global, ccnxManifestHashGroup_DataSize) +{ + CCNxManifestHashGroup *group = ccnxManifestHashGroup_Create(); + assertNotNull(group, "Expected non-null CCNxManifestHashGroup"); + + size_t dataSize = 10; + ccnxManifestHashGroup_SetDataSize(group, dataSize); + size_t actual = ccnxManifestHashGroup_GetDataSize(group); + + assertTrue(dataSize == actual, "Expected %zu, got %zu", dataSize, actual); + + ccnxManifestHashGroup_Release(&group); +} + +LONGBOW_TEST_CASE(Global, ccnxManifestHashGroup_EntrySize) +{ + CCNxManifestHashGroup *group = ccnxManifestHashGroup_Create(); + assertNotNull(group, "Expected non-null CCNxManifestHashGroup"); + + size_t entrySize = 10; + ccnxManifestHashGroup_SetEntrySize(group, entrySize); + size_t actual = ccnxManifestHashGroup_GetEntrySize(group); + + assertTrue(entrySize == actual, "Expected %zu, got %zu", entrySize, actual); + + ccnxManifestHashGroup_Release(&group); +} + +LONGBOW_TEST_CASE(Global, ccnxManifestHashGroup_TreeHeight) +{ + CCNxManifestHashGroup *group = ccnxManifestHashGroup_Create(); + assertNotNull(group, "Expected non-null CCNxManifestHashGroup"); + + size_t treeHeight = 10; + ccnxManifestHashGroup_SetTreeHeight(group, treeHeight); + size_t actual = ccnxManifestHashGroup_GetTreeHeight(group); + + assertTrue(treeHeight == actual, "Expected %zu, got %zu", treeHeight, actual); + + ccnxManifestHashGroup_Release(&group); +} + +LONGBOW_TEST_CASE(Global, ccnxManifestHashGroup_OverallDataDigest) +{ + CCNxManifestHashGroup *group = ccnxManifestHashGroup_Create(); + assertNotNull(group, "Expected non-null CCNxManifestHashGroup"); + + PARCBuffer *digest = parcBuffer_Allocate(10); + ccnxManifestHashGroup_SetOverallDataDigest(group, digest); + const PARCBuffer *actual = ccnxManifestHashGroup_GetOverallDataDigest(group); + + assertTrue(parcBuffer_Equals(digest, actual) == true, "Expected %s, got %s", parcBuffer_ToHexString(digest), parcBuffer_ToHexString(actual)); + + parcBuffer_Release(&digest); + ccnxManifestHashGroup_Release(&group); +} + +LONGBOW_TEST_CASE(Global, ccnxManifestHashGroup_Locator) +{ + CCNxManifestHashGroup *group = ccnxManifestHashGroup_Create(); + assertNotNull(group, "Expected non-null CCNxManifestHashGroup"); + + CCNxName *expected = ccnxName_CreateFromCString("ccnx:/flic/manifest"); + ccnxManifestHashGroup_SetLocator(group, expected); + const CCNxName *actual = ccnxManifestHashGroup_GetLocator(group); + + assertTrue(ccnxName_Equals(expected, actual) == true, "Expected %s, got %s", ccnxName_ToString(expected), ccnxName_ToString(actual)); + + ccnxName_Release(&expected); + ccnxManifestHashGroup_Release(&group); +} + +LONGBOW_TEST_CASE(Global, ccnxManifestHashGroup_HasMetadata) +{ + CCNxManifestHashGroup *group = ccnxManifestHashGroup_Create(); + assertNotNull(group, "Expected non-null CCNxManifestHashGroup"); + + assertFalse(ccnxManifestHashGroup_HasMetadata(group), "Expected an empty HashGroup to have no metadata"); + + CCNxName *expected = ccnxName_CreateFromCString("ccnx:/flic/manifest"); + ccnxManifestHashGroup_SetLocator(group, expected); + + assertTrue(ccnxManifestHashGroup_HasMetadata(group), "Expected a HashGroup with a locator to have metadata"); + + ccnxName_Release(&expected); + ccnxManifestHashGroup_Release(&group); +} + +LONGBOW_TEST_CASE(Global, ccnxManifestHashGroup_Iterator) +{ + CCNxManifestHashGroup *group = ccnxManifestHashGroup_Create(); + assertNotNull(group, "Expected non-null CCNxManifestHashGroup"); + + for (size_t i = 0; i < MAX_NUMBER_OF_POINTERS; i++) { + PARCBuffer *buffer = parcBuffer_Allocate(10); + parcBuffer_Flip(parcBuffer_PutUint32(buffer, i)); + assertTrue(ccnxManifestHashGroup_AppendPointer(group, CCNxManifestHashGroupPointerType_Data, buffer), "Expected the insertion to succeed"); + parcBuffer_Release(&buffer); + } + + PARCIterator *itr = ccnxManifestHashGroup_Iterator(group); + + size_t i = 0; + while (parcIterator_HasNext(itr)) { + CCNxManifestHashGroupPointer *ptr = (CCNxManifestHashGroupPointer *) parcIterator_Next(itr); + const PARCBuffer *digest = ccnxManifestHashGroupPointer_GetDigest(ptr); + size_t index = parcBuffer_GetUint32((PARCBuffer *) digest); + assertTrue(index == i, "Expected the right digest pointer to be extracted, got %zu, expected %zu", index, i); + i++; + } + + parcIterator_Release(&itr); + + bool isFull = ccnxManifestHashGroup_IsFull(group); + assertTrue(isFull, "Expected the group to be full after %ul pointers", MAX_NUMBER_OF_POINTERS); + + ccnxManifestHashGroup_Release(&group); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(ccnx_ManifestHashGroup); + int exitStatus = LONGBOW_TEST_MAIN(argc, argv, testRunner); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-common/ccnx/common/test/test_ccnx_Name.c b/libccnx-common/ccnx/common/test/test_ccnx_Name.c new file mode 100644 index 00000000..2adec939 --- /dev/null +++ b/libccnx-common/ccnx/common/test/test_ccnx_Name.c @@ -0,0 +1,735 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +#include "../ccnx_Name.c" + +#include <LongBow/unit-test.h> + +#include <stdio.h> +#include <limits.h> + +#include <parc/algol/parc_SafeMemory.h> + +LONGBOW_TEST_RUNNER(ccnx_Name) +{ + LONGBOW_RUN_TEST_FIXTURE(Local); + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Specialization); + LONGBOW_RUN_TEST_FIXTURE(Performance); +} + +LONGBOW_TEST_RUNNER_SETUP(ccnx_Name) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_RUNNER_TEARDOWN(ccnx_Name) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, ccnxName_CreateFromCString); + LONGBOW_RUN_TEST_CASE(Global, ccnxName_CreateFromCString_Root); + LONGBOW_RUN_TEST_CASE(Global, ccnxName_CreateFromCString_BadScheme); + LONGBOW_RUN_TEST_CASE(Global, ccnxName_CreateFromCString_NoScheme); + LONGBOW_RUN_TEST_CASE(Global, ccnxName_CreateFromCString_ZeroComponents); + LONGBOW_RUN_TEST_CASE(Global, ccnxName_CreateFromBuffer); + + LONGBOW_RUN_TEST_CASE(Global, ccnxName_IsValid_True); + LONGBOW_RUN_TEST_CASE(Global, ccnxName_IsValid_False); + + LONGBOW_RUN_TEST_CASE(Global, ccnxName_Equals); + LONGBOW_RUN_TEST_CASE(Global, ccnxName_HashCode); + LONGBOW_RUN_TEST_CASE(Global, ccnxName_LeftMostHashCode); + LONGBOW_RUN_TEST_CASE(Global, ccnxName_HashCode_LeftMostHashCode); + + LONGBOW_RUN_TEST_CASE(Global, ccnxName_ToString); + LONGBOW_RUN_TEST_CASE(Global, ccnxName_ToString_Root); + LONGBOW_RUN_TEST_CASE(Global, ccnxName_ToString_NoPath); + LONGBOW_RUN_TEST_CASE(Global, ccnxName_ToString_LCI); + LONGBOW_RUN_TEST_CASE(Global, ccnxName_Copy_Zero); + LONGBOW_RUN_TEST_CASE(Global, ccnxName_Copy_NonZero); + LONGBOW_RUN_TEST_CASE(Global, ccnxName_CreateAndDestroy); + + LONGBOW_RUN_TEST_CASE(Global, ccnxName_Trim); + LONGBOW_RUN_TEST_CASE(Global, ccnxName_Trim_MAXINT); + LONGBOW_RUN_TEST_CASE(Global, ccnxName_StartsWith_True); + LONGBOW_RUN_TEST_CASE(Global, ccnxName_StartsWith_FalseShorterPrefix); + LONGBOW_RUN_TEST_CASE(Global, ccnxName_StartsWith_FalseLongerPrefix); + + LONGBOW_RUN_TEST_CASE(Global, ccnxName_Compare); + LONGBOW_RUN_TEST_CASE(Global, ccnxName_ComposeNAME); + + LONGBOW_RUN_TEST_CASE(Global, ParseTest1); + LONGBOW_RUN_TEST_CASE(Global, ParseTest2); + + LONGBOW_RUN_TEST_CASE(Global, MemoryProblem); +} + +static size_t _longBowGlobal_Global_outstanding; + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + _longBowGlobal_Global_outstanding = parcMemory_Outstanding(); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + LongBowStatus result = LONGBOW_STATUS_SUCCEEDED; + + size_t allocationsLeaked = parcMemory_Outstanding() - _longBowGlobal_Global_outstanding; + + if (allocationsLeaked > 0) { + printf("%s leaks memory by %zd allocations\n", longBowTestCase_GetName(testCase), allocationsLeaked); + parcSafeMemory_ReportAllocation(STDERR_FILENO); + result = LONGBOW_STATUS_MEMORYLEAK; + } + return result; +} + +LONGBOW_TEST_CASE(Global, ccnxName_ComposeNAME) +{ + char *string = "lci:/a/b/c"; + + CCNxName *basename = ccnxName_CreateFromCString("lci:/a/b"); + CCNxName *expected = ccnxName_CreateFromCString(string); + + CCNxName *actual = ccnxName_ComposeNAME(basename, "c"); + assertTrue(ccnxName_Equals(expected, actual), "Failed."); + + ccnxName_Release(&basename); + ccnxName_Release(&expected); + ccnxName_Release(&actual); +} + +LONGBOW_TEST_CASE(Global, ccnxName_IsValid_True) +{ + char *string = "lci:/a/b/c"; + CCNxName *name = ccnxName_CreateFromCString(string); + assertTrue(ccnxName_IsValid(name), "Expected %s to be a valid CCNxName.", string); + ccnxName_Release(&name); +} + +LONGBOW_TEST_CASE(Global, ccnxName_IsValid_False) +{ + assertFalse(ccnxName_IsValid(NULL), "Expected NULL to be an invalid CCNxName."); +} + +LONGBOW_TEST_CASE(Global, ccnxName_Equals) +{ + CCNxName *x = ccnxName_CreateFromCString("lci:/a/b/c"); + CCNxName *y = ccnxName_CreateFromCString("lci:/a/b/c"); + CCNxName *z = ccnxName_CreateFromCString("lci:/a/b/c"); + CCNxName *u1 = ccnxName_CreateFromCString("lci:/a/b"); + CCNxName *u2 = ccnxName_CreateFromCString("lci:/a/b/d"); + + assertEqualsContract(ccnxName_Equals, x, y, z, u1, u2); + + ccnxName_Release(&x); + ccnxName_Release(&y); + ccnxName_Release(&z); + ccnxName_Release(&u1); + ccnxName_Release(&u2); +} + +LONGBOW_TEST_CASE(Global, ccnxName_ToString_Root) +{ + const char *expected = "ccnx:/"; + + CCNxName *name = ccnxName_CreateFromCString(expected); + + char *actual = ccnxName_ToString(name); + + assertTrue(strcmp(expected, actual) == 0, "Expected '%s' actual '%s'", expected, actual); + parcMemory_Deallocate((void **) &actual); + + ccnxName_Release(&name); + + name = ccnxName_CreateFromCString("ccnx:"); + actual = ccnxName_ToString(name); + + assertTrue(strcmp(expected, actual) == 0, "Expected '%s' actual '%s'", expected, actual); + parcMemory_Deallocate((void **) &actual); + + ccnxName_Release(&name); +} + +LONGBOW_TEST_CASE(Global, ccnxName_ToString_NoPath) +{ + const char *expected = "ccnx:/"; + + CCNxName *name = ccnxName_CreateFromCString("ccnx:"); + + char *actual = ccnxName_ToString(name); + + assertTrue(strcmp(expected, actual) == 0, "Expected '%s' actual '%s'", expected, actual); + parcMemory_Deallocate((void **) &actual); + + ccnxName_Release(&name); +} + +LONGBOW_TEST_CASE(Global, ccnxName_Trim) +{ + CCNxName *name = ccnxName_CreateFromCString("ccnx:/a/b/c"); + + ccnxName_Trim(name, 1); + + const char *expected = "ccnx:/a/b"; + char *actual = ccnxName_ToString(name); + + assertTrue(strcmp(expected, actual) == 0, "Expected '%s' actual '%s'", expected, actual); + + parcMemory_Deallocate((void **) &actual); + + ccnxName_Release(&name); +} + +LONGBOW_TEST_CASE(Global, ccnxName_Trim_MAXINT) +{ + CCNxName *name = ccnxName_CreateFromCString("ccnx:/a/b/c"); + + ccnxName_Trim(name, INT_MAX); + + const char *expected = "ccnx:/"; + char *actual = ccnxName_ToString(name); + + assertTrue(strcmp(expected, actual) == 0, "Expected '%s' actual '%s'", expected, actual); + + parcMemory_Deallocate((void **) &actual); + + ccnxName_Release(&name); +} + +LONGBOW_TEST_CASE(Global, ccnxName_Copy_Zero) +{ + const char *uri = "ccnx:/"; // A Name with 1 zero-length segment + + CCNxName *name = ccnxName_CreateFromCString(uri); + + CCNxName *copy = ccnxName_Copy(name); + assertNotNull(copy, "Expect non-null result."); + + char *expected = ccnxName_ToString(name); + char *actual = ccnxName_ToString(copy); + + assertTrue(strcmp(expected, actual) == 0, "Expected '%s', actual '%s'", expected, actual); + + ccnxName_Release(&name); + ccnxName_Release(©); + parcMemory_Deallocate((void **) &expected); + parcMemory_Deallocate((void **) &actual); +} + +LONGBOW_TEST_CASE(Global, ccnxName_Copy_NonZero) +{ + const char *uri = "ccnx:/a/b/c/d/e"; + + CCNxName *name = ccnxName_CreateFromCString(uri); + + CCNxName *copy = ccnxName_Copy(name); + assertNotNull(copy, "Expect non-null result."); + + char *expected = ccnxName_ToString(name); + char *actual = ccnxName_ToString(copy); + + assertTrue(strcmp(expected, actual) == 0, "Expected '%s', actual '%s'", expected, actual); + + ccnxName_Release(&name); + ccnxName_Release(©); + parcMemory_Deallocate((void **) &expected); + parcMemory_Deallocate((void **) &actual); +} + +LONGBOW_TEST_CASE(Global, ccnxName_HashCode) +{ + const char *uriA = "lci:/a/b/c/d/e/"; + const char *uriB = "lci:/a/b/c/d/e/"; + + CCNxName *nameA = ccnxName_CreateFromCString(uriA); + CCNxName *nameB = ccnxName_CreateFromCString(uriB); + + PARCHashCode codeA = ccnxName_HashCode(nameA); + PARCHashCode codeB = ccnxName_HashCode(nameB); + + // We know the hashcode of uriA is not zero + assertTrue(codeA != 0, "Expected a non-zero hash code"); + + assertTrue(codeA == codeB, "Expected %" PRIPARCHashCode " == %" PRIPARCHashCode, codeA, codeB); + + ccnxName_Release(&nameA); + ccnxName_Release(&nameB); +} + +LONGBOW_TEST_CASE(Global, ccnxName_HashCode_LeftMostHashCode) +{ + const char *uriA = "lci:/a/b/c/d/e/"; + const char *uriB = "lci:/a/b/c/d/e/"; + + CCNxName *nameA = ccnxName_CreateFromCString(uriA); + CCNxName *nameB = ccnxName_CreateFromCString(uriB); + + PARCHashCode codeA = ccnxName_HashCode(nameA); + PARCHashCode codeB = ccnxName_HashCode(nameB); + PARCHashCode leftMostCodeA = ccnxName_LeftMostHashCode(nameA, INT_MAX); + PARCHashCode leftMostCodeB = ccnxName_LeftMostHashCode(nameB, INT_MAX); + + // We know the hashcode of uriA is not zero + assertTrue(codeA != 0, "Expected a non-zero hash code"); + + assertTrue(codeA == codeB, "Expected %" PRIPARCHashCode " == %" PRIPARCHashCode, codeA, codeB); + assertTrue(codeA == leftMostCodeA, "Expected %" PRIPARCHashCode " == %" PRIPARCHashCode, codeA, leftMostCodeA); + assertTrue(codeA == leftMostCodeB, "Expected %" PRIPARCHashCode " == %" PRIPARCHashCode, codeA, leftMostCodeB); + + ccnxName_Release(&nameA); + ccnxName_Release(&nameB); +} + +LONGBOW_TEST_CASE(Global, ccnxName_LeftMostHashCode) +{ + const char *uriA = "lci:/a/b/c/d/e/"; + const char *uriB = "lci:/a/b/c/d/e/"; + + CCNxName *nameA = ccnxName_CreateFromCString(uriA); + CCNxName *nameB = ccnxName_CreateFromCString(uriB); + + PARCHashCode codeA = ccnxName_LeftMostHashCode(nameA, 2); + PARCHashCode codeB = ccnxName_LeftMostHashCode(nameB, 2); + + assertTrue(codeA == codeB, "Expected %" PRIPARCHashCode " == %" PRIPARCHashCode, codeA, codeB); + + ccnxName_Release(&nameA); + ccnxName_Release(&nameB); +} + +LONGBOW_TEST_CASE(Global, ccnxName_CreateAndDestroy) +{ + CCNxName *name = ccnxName_Create(); + assertNotNull(name, "Expected non-null"); + ccnxName_Release(&name); + assertNull(name, "Expected null"); +} + +LONGBOW_TEST_CASE(Global, ccnxName_CreateFromCString) +{ + const char *uri = "lci:/CCN-Python-Test"; + + CCNxName *name = ccnxName_CreateFromCString(uri); + ccnxName_Display(name, 0); + assertNotNull(name, "Expected non-null"); + + size_t expected = 1; + size_t actual = ccnxName_GetSegmentCount(name); + + assertTrue(expected == actual, "Expected %zd segments, actual %zd", expected, actual); + + ccnxName_Release(&name); +} + +LONGBOW_TEST_CASE(Global, ccnxName_CreateFromCString_BadScheme) +{ + const char *uri = "abcd:/CCN-Python-Test/Echo"; + + CCNxName *name = ccnxName_CreateFromCString(uri); + assertNull(name, "Expected null"); +} + +LONGBOW_TEST_CASE(Global, ccnxName_CreateFromCString_NoScheme) +{ + const char *uri = "/paravion"; + + CCNxName *name = ccnxName_CreateFromCString(uri); + assertNull(name, "Expected null"); +} + +LONGBOW_TEST_CASE(Global, ccnxName_CreateFromCString_ZeroComponents) +{ + const char *uri = "lci:"; + + CCNxName *name = ccnxName_CreateFromCString(uri); + assertNotNull(name, "Expected non-null result from ccnxName_CreateFromCString"); + + size_t expected = 0; + size_t actual = ccnxName_GetSegmentCount(name); + + assertTrue(expected == actual, "Expected %zd segments, actual %zd", expected, actual); + + ccnxName_Release(&name); +} + +LONGBOW_TEST_CASE(Global, ccnxName_CreateFromCString_Root) +{ + const char *uri = "lci:/"; + + CCNxName *name = ccnxName_CreateFromCString(uri); + assertNotNull(name, "Expected non-null"); + + size_t expected = 1; + size_t actual = ccnxName_GetSegmentCount(name); + + assertTrue(expected == actual, "Expected %zd segments, actual %zd", expected, actual); + + CCNxNameSegment *segment = ccnxName_GetSegment(name, 0); + + size_t segmentLength = ccnxNameSegment_Length(segment); + assertTrue(segmentLength == 0, "Expected a zero length segment, actual %zd", segmentLength); + + ccnxName_Release(&name); +} + +LONGBOW_TEST_CASE(Global, ccnxName_CreateFromBuffer) +{ + PARCBuffer *buffer = parcBuffer_WrapCString("lci:/CCN-Python-Test"); + CCNxName *name = ccnxName_CreateFromBuffer(buffer); + assertNotNull(name, "Expected non-null"); + + size_t expected = 1; + size_t actual = ccnxName_GetSegmentCount(name); + + assertTrue(expected == actual, "Expected %zd segments, actual %zd", expected, actual); + + ccnxName_Release(&name); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, ccnxName_ToString_LCI) +{ + const char *lci = "lci:/a/b"; + const char *expectedURI = "ccnx:/a/b"; + + CCNxName *name = ccnxName_CreateFromCString(lci); + + size_t expected = 2; + size_t actual = ccnxName_GetSegmentCount(name); + + assertTrue(expected == actual, + "Expected %zd segments, actual %zd", expected, actual); + + char *string = ccnxName_ToString(name); + assertTrue(strcmp(expectedURI, string) == 0, + "Expected '%s' actual '%s'", expectedURI, string); + parcMemory_Deallocate((void **) &string); + + ccnxName_Release(&name); +} + +LONGBOW_TEST_CASE(Global, ccnxName_ToString) +{ + const char *uri = "ccnx:/a/b"; + + CCNxName *name = ccnxName_CreateFromCString(uri); + + size_t expected = 2; + size_t actual = ccnxName_GetSegmentCount(name); + + assertTrue(expected == actual, + "Expected %zd segments, actual %zd", expected, actual); + + char *string = ccnxName_ToString(name); + assertTrue(strcmp(uri, string) == 0, + "Expected '%s' actual '%s'", uri, string); + parcMemory_Deallocate((void **) &string); + + ccnxName_Release(&name); +} + +LONGBOW_TEST_CASE(Global, ccnxName_Compare) +{ + CCNxName *value = ccnxName_CreateFromCString("lci:/a/b/c"); + CCNxName *equal1 = ccnxName_CreateFromCString("lci:/a/b/c"); + + CCNxName **equivalents = (CCNxName *[]) { + equal1, + NULL + }; + + CCNxName *lesser1 = ccnxName_CreateFromCString("lci:/a/b"); + CCNxName *lesser2 = ccnxName_CreateFromCString("lci:/a/b/b"); + CCNxName **lesser = (CCNxName *[]) { + lesser1, + lesser2, + NULL + }; + + CCNxName *greater1 = ccnxName_CreateFromCString("lci:/a/b/d"); + CCNxName *greater2 = ccnxName_CreateFromCString("lci:/a/b/c/d"); + CCNxName **greater = (CCNxName *[]) { + greater1, + greater2, + NULL + }; + + assertCompareToContract(ccnxName_Compare, value, equivalents, lesser, greater); + + for (int i = 0; lesser[i] != NULL; i++) { + ccnxName_Release(&lesser[i]); + } + for (int i = 0; greater[i] != NULL; i++) { + ccnxName_Release(&greater[i]); + } + for (int i = 0; equivalents[i] != NULL; i++) { + ccnxName_Release(&equivalents[i]); + } + ccnxName_Release(&value); +} + +LONGBOW_TEST_CASE(Global, ccnxName_StartsWith_True) +{ + const char *uriA = "lci:/a/b/c/d/e/"; + const char *uriB = "lci:/a/b/c/d/e/"; + + CCNxName *nameA = ccnxName_CreateFromCString(uriA); + CCNxName *nameB = ccnxName_CreateFromCString(uriB); + + bool actual = ccnxName_StartsWith(nameA, nameA); + + assertTrue(actual, "Expected true"); + + ccnxName_Release(&nameA); + ccnxName_Release(&nameB); +} + +LONGBOW_TEST_CASE(Global, ccnxName_StartsWith_FalseShorterPrefix) +{ + const char *uriA = "lci:/a/b/c/d/e"; + const char *prefix = "lci:/a/b/d"; + + CCNxName *nameA = ccnxName_CreateFromCString(uriA); + CCNxName *nameB = ccnxName_CreateFromCString(prefix); + + bool actual = ccnxName_StartsWith(nameA, nameB); + + assertFalse(actual, "Expected false"); + + ccnxName_Release(&nameA); + ccnxName_Release(&nameB); +} + +LONGBOW_TEST_CASE(Global, ccnxName_StartsWith_FalseLongerPrefix) +{ + const char *uriA = "lci:/a/b/c/d/e"; + const char *prefix = "lci:/a/b/c/d/e/f"; + + CCNxName *nameA = ccnxName_CreateFromCString(uriA); + CCNxName *nameB = ccnxName_CreateFromCString(prefix); + + bool actual = ccnxName_StartsWith(nameA, nameB); + + assertFalse(actual, "Expected false"); + + ccnxName_Release(&nameA); + ccnxName_Release(&nameB); +} + +static CCNxNameSegment * +createSegment(PARCBuffer *buffer, size_t start, size_t end) +{ + parcBuffer_SetPosition(buffer, start); + PARCBuffer *slice = parcBuffer_Slice(buffer); + parcBuffer_SetLimit(slice, end); + + CCNxNameSegment *segment = ccnxNameSegment_CreateTypeValue(CCNxNameLabelType_NAME, slice); + parcBuffer_Release(&slice); + + return segment; +} + +LONGBOW_TEST_CASE(Global, MemoryProblem) +{ + char memory[] = "abcdefghijklmnopqrstuvwxyz"; + PARCBuffer *buffer = parcBuffer_Wrap(memory, sizeof(memory), 0, sizeof(memory)); + + CCNxName *name = ccnxName_Create(); + + CCNxNameSegment *segment1 = createSegment(buffer, 2, 4); // "cd" + ccnxName_Append(name, segment1); + + CCNxNameSegment *segment2 = createSegment(buffer, 10, 14); // "klmn" + ccnxName_Append(name, segment2); + + CCNxName *name2 = ccnxName_Acquire(name); + + parcBuffer_Release(&buffer); + ccnxName_Release(&name2); + + ccnxName_Release(&name); + ccnxNameSegment_Release(&segment1); + ccnxNameSegment_Release(&segment2); +} + +LONGBOW_TEST_CASE(Global, ParseTest1) +{ + CCNxName *name = ccnxName_CreateFromCString("lci:/" CCNxNameLabel_Name "=foot/3=toe/4=nail"); + assertNotNull(name, "Expected non-null value from ccnxName_CreateFromCString"); + + ccnxName_Display(name, 0); + + ccnxName_Release(&name); +} + +LONGBOW_TEST_CASE(Global, ParseTest2) +{ + CCNxName *a = ccnxName_CreateFromCString("lci:/a/b/c"); + CCNxName *b = ccnxName_CreateFromCString("lci:/Name=a/Name=b/Name=c"); + assertTrue(ccnxName_Equals(a, b), "Expected to be equal"); + ccnxName_Release(&a); + ccnxName_Release(&b); + + char *expected = "ccnx:/test/Name=MiISAg%3D%3D"; + CCNxName *name = ccnxName_CreateFromCString(expected); + assertNotNull(name, "Expected non-null value from ccnxName_CreateFromCString"); + char *actual = ccnxName_ToString(name); + printf("%s\n", actual); + assertTrue(strcmp(expected, actual) == 0, "Expected '%s' actual '%s'", expected, actual); + parcMemory_Deallocate(&actual); + + ccnxName_Display(name, 0); + + ccnxName_Release(&name); +} + +LONGBOW_TEST_FIXTURE(Local) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Specialization) +{ + LONGBOW_RUN_TEST_CASE(Specialization, ccnxName_Prefix); + LONGBOW_RUN_TEST_CASE(Specialization, ccnxName_Prefix_Excess); + LONGBOW_RUN_TEST_CASE(Specialization, ccnxName_Prefix_0); +} + +LONGBOW_TEST_FIXTURE_SETUP(Specialization) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Specialization) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Specialization, ccnxName_Prefix) +{ + CCNxName *a = ccnxName_CreateFromCString("ccnx:/a/b/c"); + CCNxName *expected = ccnxName_CreateFromCString("ccnx:/a"); + + CCNxName *actual = ccnxName_CreatePrefix(a, 1); + + assertTrue(ccnxName_Equals(expected, actual), "Mismatched results."); + + ccnxName_Release(&a); + ccnxName_Release(&expected); + ccnxName_Release(&actual); +} + +LONGBOW_TEST_CASE(Specialization, ccnxName_Prefix_Excess) +{ + CCNxName *a = ccnxName_CreateFromCString("ccnx:/a/b/c"); + CCNxName *expected = ccnxName_CreateFromCString("ccnx:/a/b/c"); + + CCNxName *actual = ccnxName_CreatePrefix(a, 100); + + assertTrue(ccnxName_Equals(expected, actual), "Mismatched results."); + + ccnxName_Release(&a); + ccnxName_Release(&expected); + ccnxName_Release(&actual); +} + +LONGBOW_TEST_CASE(Specialization, ccnxName_Prefix_0) +{ + CCNxName *a = ccnxName_CreateFromCString("ccnx:/a/b/c"); + CCNxName *expected = ccnxName_CreateFromCString("ccnx:"); + + CCNxName *actual = ccnxName_CreatePrefix(a, 0); + + assertTrue(ccnxName_Equals(expected, actual), "Mismatched results."); + + ccnxName_Release(&a); + ccnxName_Release(&expected); + ccnxName_Release(&actual); +} + +LONGBOW_TEST_FIXTURE_OPTIONS(Performance, .enabled = false) +{ + LONGBOW_RUN_TEST_CASE(Performance, ccnxName_Create); +} + +LONGBOW_TEST_FIXTURE_SETUP(Performance) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Performance) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Performance, ccnxName_Create) +{ + PARCBuffer *value = parcBuffer_WrapCString("Hello"); + + for (int i = 0; i < 10000; i++) { + CCNxName *name = ccnxName_Create(); + CCNxNameSegment *segment = ccnxNameSegment_CreateTypeValue(CCNxNameLabelType_NAME, value); + for (int j = 0; j < 1000; j++) { + ccnxName_Append(name, segment); + } + ccnxNameSegment_Release(&segment); + ccnxName_Release(&name); + } + parcBuffer_Release(&value); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(ccnx_Name); + int exitStatus = longBowMain(argc, argv, testRunner); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-common/ccnx/common/test/test_ccnx_NameLabel.c b/libccnx-common/ccnx/common/test/test_ccnx_NameLabel.c new file mode 100755 index 00000000..428c2f29 --- /dev/null +++ b/libccnx-common/ccnx/common/test/test_ccnx_NameLabel.c @@ -0,0 +1,470 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ +#include "../ccnx_NameLabel.c" + +#include <LongBow/unit-test.h> +#include <stdio.h> +#include <parc/algol/parc_SafeMemory.h> +#include <parc/testing/parc_ObjectTesting.h> + + +LONGBOW_TEST_RUNNER(ccnx_NameType) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Errors); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(ccnx_NameType) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(ccnx_NameType) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, ccnxNameLabel_Create); + LONGBOW_RUN_TEST_CASE(Global, ccnxNameLabel_AcquireRelease); + LONGBOW_RUN_TEST_CASE(Global, ccnxNameLabel_GetType); + LONGBOW_RUN_TEST_CASE(Global, ccnxNameLabel_GetParameter); + LONGBOW_RUN_TEST_CASE(Global, ccnxNameLabel_Equals); + LONGBOW_RUN_TEST_CASE(Global, ccnxNameLabel_Copy); + + LONGBOW_RUN_TEST_CASE(Global, ccnxNameType_Resolve_Mnemonic); + LONGBOW_RUN_TEST_CASE(Global, ccnxNameType_Resolve_Mnemonic_NULL); + LONGBOW_RUN_TEST_CASE(Global, ccnxNameType_Resolve_Mnemonic_Empty); + LONGBOW_RUN_TEST_CASE(Global, ccnxNameType_Resolve_Mnemonic_App0); + LONGBOW_RUN_TEST_CASE(Global, ccnxNameType_Resolve_Mnemonic_App); + LONGBOW_RUN_TEST_CASE(Global, ccnxNameType_Resolve_Numeric_Decimal); + LONGBOW_RUN_TEST_CASE(Global, ccnxNameType_Resolve_Numeric_Hex); + LONGBOW_RUN_TEST_CASE(Global, ccnxNameType_Resolve_Unknown_Mnemonic); + LONGBOW_RUN_TEST_CASE(Global, ccnxNameType_BuildString_KnownLabel); + LONGBOW_RUN_TEST_CASE(Global, ccnxNameType_BuildString_UnknownLabel); + LONGBOW_RUN_TEST_CASE(Global, ccnxNameType_BuildString_AppLabel); + LONGBOW_RUN_TEST_CASE(Global, ccnxNameType_BuildString_AppLabel4096); + LONGBOW_RUN_TEST_CASE(Global, ccnxNameType_Parse); + LONGBOW_RUN_TEST_CASE(Global, ccnxNameType_Parse_DefaultLabel); + LONGBOW_RUN_TEST_CASE(Global, ccnxNameType_Parse_EmptyLabel); + LONGBOW_RUN_TEST_CASE(Global, ccnxNameType_Parse_KnownLabel); + LONGBOW_RUN_TEST_CASE(Global, ccnxNameType_Parse_DecimalParameterLabel); + LONGBOW_RUN_TEST_CASE(Global, ccnxNameType_Parse_HexadecimalParameterLabel); + LONGBOW_RUN_TEST_CASE(Global, ccnxNameType_Parse_OutOfRangeLabel); + LONGBOW_RUN_TEST_CASE(Global, ccnxNameType_Parse_BadHexLabel); + LONGBOW_RUN_TEST_CASE(Global, ccnxNameType_Parse_UknownMnemonicLabel); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDOUT_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, ccnxNameType_Resolve_Mnemonic) +{ + char *mnemonic = "Name"; + PARCBuffer *label = parcBuffer_WrapCString(mnemonic); + CCNxNameLabelType actual = _ccnxNameLabelType_Resolve(label); + assertTrue(actual == CCNxNameLabelType_NAME, + "Expected an CCNxNameType_NAME type for the mnemonic '%s'", mnemonic); + parcBuffer_Release(&label); +} + +LONGBOW_TEST_CASE(Global, ccnxNameType_Resolve_Mnemonic_NULL) +{ + CCNxNameLabelType actual = _ccnxNameLabelType_Resolve(NULL); + assertTrue(actual == CCNxNameLabelType_NAME, + "Expected an CCNxNameType_NAME type for a NULL mnemonic."); +} + +LONGBOW_TEST_CASE(Global, ccnxNameType_Resolve_Mnemonic_Empty) +{ + PARCBuffer *label = parcBuffer_Allocate(0); + CCNxNameLabelType actual = _ccnxNameLabelType_Resolve(label); + assertTrue(actual == CCNxNameLabelType_NAME, + "Expected an CCNxNameType_NAME type for an empty mnemonic."); + parcBuffer_Release(&label); +} + +LONGBOW_TEST_CASE(Global, ccnxNameType_Resolve_Mnemonic_App0) +{ + PARCBuffer *label = parcBuffer_WrapCString("app"); + CCNxNameLabelType actual = _ccnxNameLabelType_Resolve(label); + assertTrue(actual == CCNxNameLabelType_App(0), + "Expected 0x%04x type, actual 0x%04x", CCNxNameLabelType_App(0), actual); + parcBuffer_Release(&label); +} + +LONGBOW_TEST_CASE(Global, ccnxNameType_Resolve_Mnemonic_App) +{ + PARCBuffer *label = parcBuffer_WrapCString("app"); + CCNxNameLabelType actual = _ccnxNameLabelType_Resolve(label); + assertTrue(actual == CCNxNameLabelType_App(0), + "Expected 0x%04x type, actual 0x%04x", CCNxNameLabelType_App(0), actual); + parcBuffer_Release(&label); +} + +LONGBOW_TEST_CASE(Global, ccnxNameType_Resolve_Numeric_Decimal) +{ + PARCBuffer *label = parcBuffer_WrapCString("16"); + CCNxNameLabelType actual = _ccnxNameLabelType_Resolve(label); + assertTrue(actual == CCNxNameLabelType_CHUNK, + "Expected type %d, actual %d", CCNxNameLabelType_CHUNK, actual); + parcBuffer_Release(&label); +} + +LONGBOW_TEST_CASE(Global, ccnxNameType_Resolve_Numeric_Hex) +{ + CCNxNameLabelType expected = 0xF000; + PARCBuffer *label = parcBuffer_WrapCString("0xF000"); + CCNxNameLabelType actual = _ccnxNameLabelType_Resolve(label); + assertTrue(actual == expected, + "Expected type %d, actual %d", expected, actual); + parcBuffer_Release(&label); +} + +LONGBOW_TEST_CASE(Global, ccnxNameType_Resolve_Unknown_Mnemonic) +{ + CCNxNameLabelType expected = CCNxNameLabelType_Unknown; + PARCBuffer *label = parcBuffer_WrapCString("xyzzy"); + CCNxNameLabelType actual = _ccnxNameLabelType_Resolve(label); + assertTrue(actual == expected, + "Expected type %d, actual %d", expected, actual); + parcBuffer_Release(&label); +} + +LONGBOW_TEST_CASE(Global, ccnxNameType_BuildString_KnownLabel) +{ + PARCBufferComposer *composer = parcBufferComposer_Create(); + + CCNxNameLabel *label = ccnxNameLabel_Create(CCNxNameLabelType_CHUNK, NULL); + ccnxNameLabel_BuildString(label, composer); + ccnxNameLabel_Release(&label); + + PARCBuffer *expected = parcBuffer_WrapCString(CCNxNameLabel_Chunk "="); + PARCBuffer *actual = parcBuffer_Flip(parcBufferComposer_GetBuffer(composer)); + + assertTrue(parcBuffer_Equals(expected, actual), + "Expected a successful label lookup."); + parcBuffer_Release(&expected); + parcBufferComposer_Release(&composer); +} + +LONGBOW_TEST_CASE(Global, ccnxNameType_BuildString_UnknownLabel) +{ + PARCBufferComposer *composer = parcBufferComposer_Create(); + CCNxNameLabel *label = ccnxNameLabel_Create(1111, NULL); + ccnxNameLabel_BuildString(label, composer); + ccnxNameLabel_Release(&label); + + PARCBuffer *expected = parcBuffer_WrapCString("1111" "="); + PARCBuffer *actual = parcBuffer_Flip(parcBufferComposer_GetBuffer(composer)); + + assertTrue(parcBuffer_Equals(expected, actual), + "Expected a successful label lookup."); + + parcBuffer_Release(&expected); + parcBufferComposer_Release(&composer); +} + +LONGBOW_TEST_CASE(Global, ccnxNameType_BuildString_AppLabel) +{ + PARCBufferComposer *composer = parcBufferComposer_Create(); + + PARCBuffer *parameter = parcBuffer_WrapCString("0"); + CCNxNameLabel *label = ccnxNameLabel_Create(CCNxNameLabelType_App(0), parameter); + ccnxNameLabel_BuildString(label, composer); + ccnxNameLabel_Release(&label); + parcBuffer_Release(¶meter); + + PARCBuffer *expected = parcBuffer_WrapCString("App:0" "="); + PARCBuffer *actual = parcBuffer_Flip(parcBufferComposer_GetBuffer(composer)); + + assertTrue(parcBuffer_Equals(expected, actual), + "Expected a successful label lookup."); + + parcBuffer_Release(&expected); + parcBufferComposer_Release(&composer); +} + +LONGBOW_TEST_CASE(Global, ccnxNameType_BuildString_AppLabel4096) +{ + PARCBufferComposer *composer = parcBufferComposer_Create(); + PARCBuffer *parameter = parcBuffer_WrapCString("4096"); + CCNxNameLabel *label = ccnxNameLabel_Create(CCNxNameLabelType_App(4096), parameter); + ccnxNameLabel_BuildString(label, composer); + + ccnxNameLabel_Release(&label); + parcBuffer_Release(¶meter); + + PARCBuffer *expected = parcBuffer_WrapCString("App:4096" "="); + PARCBuffer *actual = parcBuffer_Flip(parcBufferComposer_GetBuffer(composer)); + + assertTrue(parcBuffer_Equals(expected, actual), + "Expected a successful label lookup."); + + parcBuffer_Release(&expected); + parcBufferComposer_Release(&composer); +} + +LONGBOW_TEST_CASE(Global, ccnxNameLabel_GetType) +{ + CCNxNameLabelType type = CCNxNameLabelType_NAME; + PARCBuffer *parameter = parcBuffer_WrapCString("Hello"); + CCNxNameLabel *label = ccnxNameLabel_Create(type, parameter); + + CCNxNameLabelType actual = ccnxNameLabel_GetType(label); + + assertTrue(type == actual, "Expected type %u, actual %u", type, actual); + + parcBuffer_Release(¶meter); + ccnxNameLabel_Release(&label); +} + +LONGBOW_TEST_CASE(Global, ccnxNameLabel_GetParameter) +{ + CCNxNameLabelType type = CCNxNameLabelType_NAME; + PARCBuffer *parameter = parcBuffer_WrapCString("Hello"); + CCNxNameLabel *label = ccnxNameLabel_Create(type, parameter); + PARCBuffer *actual = ccnxNameLabel_GetParameter(label); + + assertTrue(parcBuffer_Equals(parameter, actual), "Expected parameter to be equal to the initial parameter."); + parcBuffer_Release(¶meter); + ccnxNameLabel_Release(&label); +} + +LONGBOW_TEST_CASE(Global, ccnxNameLabel_Create) +{ + CCNxNameLabelType type = CCNxNameLabelType_NAME; + PARCBuffer *parameter = parcBuffer_WrapCString("Hello"); + CCNxNameLabel *label = ccnxNameLabel_Create(type, parameter); + parcBuffer_Release(¶meter); + ccnxNameLabel_Release(&label); +} + +LONGBOW_TEST_CASE(Global, ccnxNameLabel_Copy) +{ + PARCBuffer *parameter = parcBuffer_WrapCString("Hello"); + CCNxNameLabel *label = ccnxNameLabel_Create(CCNxNameLabelType_NAME, parameter); + parcBuffer_Release(¶meter); + + CCNxNameLabel *copy = ccnxNameLabel_Copy(label); + + assertTrue(ccnxNameLabel_Equals(label, copy), "Expected copy to the equal to the original."); + + assertTrue(label != copy, "Expected a copy to be distinct from the original."); + ccnxNameLabel_Release(&label); + ccnxNameLabel_Release(©); +} + +LONGBOW_TEST_CASE(Global, ccnxNameLabel_Equals) +{ + PARCBuffer *parameter = parcBuffer_WrapCString("Hello"); + CCNxNameLabel *x = ccnxNameLabel_Create(CCNxNameLabelType_SERIAL, parameter); + parcBuffer_Release(¶meter); + + parameter = parcBuffer_WrapCString("Hello"); + CCNxNameLabel *y = ccnxNameLabel_Create(CCNxNameLabelType_SERIAL, parameter); + parcBuffer_Release(¶meter); + + parameter = parcBuffer_WrapCString("Hello"); + CCNxNameLabel *z = ccnxNameLabel_Create(CCNxNameLabelType_SERIAL, parameter); + parcBuffer_Release(¶meter); + + parameter = parcBuffer_WrapCString("Hello"); + CCNxNameLabel *u1 = ccnxNameLabel_Create(CCNxNameLabelType_CHUNK, parameter); + parcBuffer_Release(¶meter); + + parameter = parcBuffer_WrapCString("Goodbye"); + CCNxNameLabel *u2 = ccnxNameLabel_Create(CCNxNameLabelType_SERIAL, parameter); + parcBuffer_Release(¶meter); + + assertEqualsContract(ccnxNameLabel_Equals, x, y, z, u1, u2, NULL); + + ccnxNameLabel_Release(&x); + ccnxNameLabel_Release(&y); + ccnxNameLabel_Release(&z); + ccnxNameLabel_Release(&u1); + ccnxNameLabel_Release(&u2); +} + +LONGBOW_TEST_CASE(Global, ccnxNameLabel_AcquireRelease) +{ + CCNxNameLabelType type = CCNxNameLabelType_NAME; + PARCBuffer *parameter = parcBuffer_WrapCString("Hello"); + CCNxNameLabel *label = ccnxNameLabel_Create(type, parameter); + + parcObjectTesting_AssertAcquireReleaseContract(ccnxNameLabel_Acquire, label); + + parcBuffer_Release(¶meter); + ccnxNameLabel_Release(&label); +} + +LONGBOW_TEST_CASE(Global, ccnxNameType_Parse) +{ + char *expected = "App:1=value"; + PARCBuffer *buffer = parcBuffer_WrapCString(expected); + CCNxNameLabel *label = ccnxNameLabel_Parse(buffer); + char *actual = ccnxNameLabel_ToString(label); + assertTrue(parcBuffer_Position(buffer) == 6, "Expected position to be 6, actual %zd", parcBuffer_Position(buffer)); + assertTrue(strcmp("App:1=", actual) == 0, "Expected %s, actual %s", expected, actual); + parcMemory_Deallocate((void **) &actual); + ccnxNameLabel_Release(&label); + parcBuffer_Release(&buffer); + + expected = "10:param=value"; + buffer = parcBuffer_WrapCString(expected); + label = ccnxNameLabel_Parse(buffer); + actual = ccnxNameLabel_ToString(label); + assertTrue(parcBuffer_Position(buffer) == 9, "Expected position to be 9, actual %zd", parcBuffer_Position(buffer)); + assertTrue(strcmp("10:param=", actual) == 0, "Expected %s, actual %s", "10:param=", actual); + parcMemory_Deallocate((void **) &actual); + ccnxNameLabel_Release(&label); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, ccnxNameType_Parse_DecimalParameterLabel) +{ + char *expected = "10:param=value"; + PARCBuffer *buffer = parcBuffer_WrapCString(expected); + CCNxNameLabel *label = ccnxNameLabel_Parse(buffer); + char *actual = ccnxNameLabel_ToString(label); + assertTrue(parcBuffer_Position(buffer) == 9, "Expected position to be 9, actual %zd", parcBuffer_Position(buffer)); + assertTrue(strcmp("10:param=", actual) == 0, "Expected %s, actual %s", "10:param=", actual); + parcMemory_Deallocate((void **) &actual); + ccnxNameLabel_Release(&label); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, ccnxNameType_Parse_HexadecimalParameterLabel) +{ + PARCBuffer *buffer = parcBuffer_WrapCString("0xaa:param=value"); + CCNxNameLabel *label = ccnxNameLabel_Parse(buffer); + char *actual = ccnxNameLabel_ToString(label); + assertTrue(parcBuffer_Position(buffer) == 11, "Expected position to be 11, actual %zd", parcBuffer_Position(buffer)); + char *expected = "170:param="; + assertTrue(strcmp(expected, actual) == 0, "Expected %s, actual %s", expected, actual); + parcMemory_Deallocate((void **) &actual); + ccnxNameLabel_Release(&label); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, ccnxNameType_Parse_BadHexLabel) +{ + PARCBuffer *buffer = parcBuffer_WrapCString("0xgg:param=value"); + CCNxNameLabel *label = ccnxNameLabel_Parse(buffer); + assertFalse(ccnxNameLabel_IsValid(label), "Expected an invalid CCNxNameLabel from an invalid specification."); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, ccnxNameType_Parse_OutOfRangeLabel) +{ + PARCBuffer *buffer = parcBuffer_WrapCString("0x123456=value"); + CCNxNameLabel *label = ccnxNameLabel_Parse(buffer); + char *actual = ccnxNameLabel_ToString(label); + + assertTrue(parcBuffer_Position(buffer) == 9, "Expected position to be 9, actual %zd", parcBuffer_Position(buffer)); + char *expected = "1193046="; + assertTrue(strcmp(expected, actual) == 0, "Expected %s, actual %s", expected, actual); + parcMemory_Deallocate((void **) &actual); + ccnxNameLabel_Release(&label); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, ccnxNameType_Parse_UknownMnemonicLabel) +{ + PARCBuffer *buffer = parcBuffer_WrapCString("abc=value"); + CCNxNameLabel *label = ccnxNameLabel_Parse(buffer); + + assertFalse(ccnxNameLabel_IsValid(label), "Expected an invalid CCNxNameLabel from an invalid specification."); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, ccnxNameType_Parse_KnownLabel) +{ + PARCBuffer *buffer = parcBuffer_WrapCString("Serial=value"); + CCNxNameLabel *label = ccnxNameLabel_Parse(buffer); + char *actual = ccnxNameLabel_ToString(label); + parcMemory_Deallocate((void **) &actual); + ccnxNameLabel_Release(&label); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, ccnxNameType_Parse_DefaultLabel) +{ + PARCBuffer *buffer = parcBuffer_WrapCString("value"); + CCNxNameLabel *label = ccnxNameLabel_Parse(buffer); + char *actual = ccnxNameLabel_ToString(label); + char *expected = "Name="; + assertTrue(strcmp(expected, actual) == 0, "Expected %s, actual %s", expected, actual); + parcMemory_Deallocate((void **) &actual); + ccnxNameLabel_Release(&label); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, ccnxNameType_Parse_EmptyLabel) +{ + PARCBuffer *buffer = parcBuffer_WrapCString("=value"); + CCNxNameLabel *label = ccnxNameLabel_Parse(buffer); + assertNull(label, "Expected a NULL return value from ccnxNameLabel_Parse for the invalid string '=value'"); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_FIXTURE(Errors) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Errors) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Errors) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDOUT_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +int +main(int argc, char *argv[argc]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(ccnx_NameType); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-common/ccnx/common/test/test_ccnx_NameSegment.c b/libccnx-common/ccnx/common/test/test_ccnx_NameSegment.c new file mode 100644 index 00000000..b691c52a --- /dev/null +++ b/libccnx-common/ccnx/common/test/test_ccnx_NameSegment.c @@ -0,0 +1,739 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ +#include <config.h> +#include <stdio.h> +#include <LongBow/unit-test.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_SafeMemory.h> + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../ccnx_NameSegment.c" + +LONGBOW_TEST_RUNNER(ccnx_NameComponent) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(ccnx_NameComponent) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(ccnx_NameComponent) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, ccnxNameSegment_CreateTypeValue); + LONGBOW_RUN_TEST_CASE(Global, ccnxNameSegment_Copy); + LONGBOW_RUN_TEST_CASE(Global, ccnxNameSegment_Copy_WithParameter); + LONGBOW_RUN_TEST_CASE(Global, ccnxNameSegment_Length); + LONGBOW_RUN_TEST_CASE(Global, ccnxNameSegment_GetType); + + LONGBOW_RUN_TEST_CASE(Global, ccnxNameSegment_ParseURISegment); + + LONGBOW_RUN_TEST_CASE(Global, ccnxNameSegment_ToString_META); + LONGBOW_RUN_TEST_CASE(Global, ccnxNameSegment_ToString_APP0); + LONGBOW_RUN_TEST_CASE(Global, ccnxNameSegment_ToString_NAME); + LONGBOW_RUN_TEST_CASE(Global, ccnxNameSegment_ToString_NAME_NotDefault); + + LONGBOW_RUN_TEST_CASE(Global, ccnxNameSegment_ToString_SERIAL); + LONGBOW_RUN_TEST_CASE(Global, ccnxNameSegment_ParseURISegment_RawNAME); + LONGBOW_RUN_TEST_CASE(Global, ccnxNameSegment_ToString_APP256); + LONGBOW_RUN_TEST_CASE(Global, ccnxNameSegment_ToString_PAYLOADHASH); + + LONGBOW_RUN_TEST_CASE(Global, ccnxNameSegment_ParseURISegment_NAME); + LONGBOW_RUN_TEST_CASE(Global, ccnxNameSegment_ParseURISegment_META); + LONGBOW_RUN_TEST_CASE(Global, ccnxNameSegment_ParseURISegment_list); + LONGBOW_RUN_TEST_CASE(Global, ccnxNameSegment_ParseURISegment_UnknownLabel); + + LONGBOW_RUN_TEST_CASE(Global, ccnxNameSegment_ZeroLength); + + LONGBOW_RUN_TEST_CASE(Global, ccnxNameSegment_Compare_Contract); + + LONGBOW_RUN_TEST_CASE(Global, ccnxNameSegment_Equals_Contract); + LONGBOW_RUN_TEST_CASE(Global, ccnxNameSegment_HashCode); + LONGBOW_RUN_TEST_CASE(Global, ccnxNameSegment_Display); + + LONGBOW_RUN_TEST_CASE(Global, ccnxNameSegment_IsValid); + LONGBOW_RUN_TEST_CASE(Global, ccnxNameSegment_IsValid_NULL); + LONGBOW_RUN_TEST_CASE(Global, ccnxNameSegment_IsValid_InnerNULL1); +} + +static uint32_t initialAllocationCount; + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + initialAllocationCount = parcMemory_Outstanding(); + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t remainingAllocations = parcMemory_Outstanding() - initialAllocationCount; + if (remainingAllocations > 0) { + printf("%s leaks memory by %u allocations\n", longBowTestCase_GetName(testCase), remainingAllocations); + parcSafeMemory_ReportAllocation(STDOUT_FILENO); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, ccnxNameSegment_IsValid) +{ + PARCBuffer *value = parcBuffer_WrapCString("Test"); + CCNxNameSegment *segment = ccnxNameSegment_CreateTypeValue(CCNxNameLabelType_NAME, value); + + assertTrue(ccnxNameSegment_IsValid(segment), "Expected a valid CCNxNameSegment."); + + ccnxNameSegment_Release(&segment); + parcBuffer_Release(&value); +} + +LONGBOW_TEST_CASE(Global, ccnxNameSegment_IsValid_NULL) +{ + assertFalse(ccnxNameSegment_IsValid(NULL), "Expected NULL to be an invalid CCNxNameSegment."); +} + +LONGBOW_TEST_CASE(Global, ccnxNameSegment_IsValid_InnerNULL1) +{ + PARCBuffer *buf = parcBuffer_WrapCString("Test"); + CCNxNameSegment *segment = ccnxNameSegment_CreateTypeValue(CCNxNameLabelType_NAME, buf); + + // Hit the NULL case + PARCBuffer *oldBuffer = segment->value; + segment->value = NULL; + + assertFalse(ccnxNameSegment_IsValid(segment), + "Expected a name segment with a NULL value to be invalid."); + + segment->value = oldBuffer; + + ccnxNameSegment_Release(&segment); + parcBuffer_Release(&buf); +} + +LONGBOW_TEST_CASE(Global, ccnxNameSegment_CreateTypeValue) +{ + PARCBuffer *buf = parcBuffer_WrapCString("Test"); + + CCNxNameSegment *segment = ccnxNameSegment_CreateTypeValue(CCNxNameLabelType_NAME, buf); + assertNotNull(segment, "Expected non-null"); + + ccnxNameSegment_Release(&segment); + parcBuffer_Release(&buf); +} + +LONGBOW_TEST_CASE(Global, ccnxNameSegment_CreateTypeParameterValue) +{ + PARCBuffer *parameter = parcBuffer_WrapCString("param"); + PARCBuffer *value = parcBuffer_WrapCString("Value"); + + CCNxNameLabel *label = ccnxNameLabel_Create(CCNxNameLabelType_NAME, parameter); + CCNxNameSegment *segment = ccnxNameSegment_CreateLabelValue(label, value); + ccnxNameLabel_Release(&label); + assertNotNull(segment, "Expected non-null"); + + ccnxNameSegment_Release(&segment); + parcBuffer_Release(¶meter); + parcBuffer_Release(&value); +} + +// Convert the tests to use this table, instead of individual tests. +struct nameSegments { + char *lciSegment; + CCNxNameLabelType nameType; + char *parameter; + char *value; +} nameSegment[] = { + { .lciSegment = "NAME", + .nameType = CCNxNameLabelType_NAME, .parameter = NULL, .value = "NAME", }, + { .lciSegment = CCNxNameLabel_Name "=" "NAME", + .nameType = CCNxNameLabelType_NAME, .parameter = NULL, .value = "NAME", }, + { .lciSegment = CCNxNameLabel_Chunk "=" "Chunk", + .nameType = CCNxNameLabelType_CHUNK, .parameter = NULL, .value = "Chunk", }, + { .lciSegment = CCNxNameLabel_Chunk ":param=" "Chunk", + .nameType = CCNxNameLabelType_CHUNK, .parameter = "param", .value = "Chunk", }, + { .lciSegment = CCNxNameLabel_App ":100=" "app100", + .nameType = CCNxNameLabelType_App(100), .parameter = NULL, .value = "app100", }, + { .lciSegment = NULL }, +}; + +LONGBOW_TEST_CASE(Global, ccnxNameSegment_ParseURISegment_list) +{ + for (struct nameSegments *p = &nameSegment[0]; p->lciSegment != NULL; p++) { + PARCURISegment *segment = parcURISegment_Parse(p->lciSegment, NULL); + + CCNxNameSegment *actual = ccnxNameSegment_ParseURISegment(segment); + + CCNxNameLabelType type = ccnxNameSegment_GetType(actual); + assertTrue(p->nameType == type, "Expected %04x, actual %04x", p->nameType, type); + + PARCBuffer *valueValue = parcBuffer_WrapCString(p->value); + PARCBuffer *expectedParam = p->parameter == NULL ? NULL : parcBuffer_WrapCString(p->parameter); + CCNxNameLabel *label = ccnxNameLabel_Create(p->nameType, expectedParam); + CCNxNameSegment *expected = ccnxNameSegment_CreateLabelValue(label, valueValue); + ccnxNameLabel_Release(&label); + + parcBuffer_Release(&valueValue); + if (expectedParam != NULL) { + parcBuffer_Release(&expectedParam); + } + + char *expectedString = ccnxNameSegment_ToString(expected); + char *actualString = ccnxNameSegment_ToString(actual); + assertTrue(ccnxNameSegment_Equals(expected, actual), + "Expected '%s' Actual, '%s", expectedString, actualString); + parcMemory_Deallocate((void **) &expectedString); + parcMemory_Deallocate((void **) &actualString); + + ccnxNameSegment_Release(&expected); + ccnxNameSegment_Release(&actual); + parcURISegment_Release(&segment); + } +} + +LONGBOW_TEST_CASE(Global, ccnxNameSegment_ParseURISegment_RawNAME) +{ + char *lciSegment = "NAME"; + PARCURISegment *segment = parcURISegment_Parse(lciSegment, NULL); + + CCNxNameSegment *actual = ccnxNameSegment_ParseURISegment(segment); + + CCNxNameLabelType type = ccnxNameSegment_GetType(actual); + assertTrue(CCNxNameLabelType_NAME == type, "Expected %04x, actual %04x", CCNxNameLabelType_NAME, type); + + PARCBuffer *buf = parcBuffer_WrapCString("NAME"); + CCNxNameSegment *expected = ccnxNameSegment_CreateTypeValue(CCNxNameLabelType_NAME, buf); + + assertTrue(ccnxNameSegment_Equals(expected, actual), "Error in ccnxNameSegment_ParseURISegment") + { + char *expectedString = ccnxNameSegment_ToString(expected); + char *actualString = ccnxNameSegment_ToString(expected); + fprintf(stderr, "Expected '%s', actual '%s'", expectedString, actualString); + parcMemory_Deallocate((void **) expectedString); + parcMemory_Deallocate((void **) actualString); + }; + + parcBuffer_Release(&buf); + ccnxNameSegment_Release(&expected); + ccnxNameSegment_Release(&actual); + parcURISegment_Release(&segment); +} + +LONGBOW_TEST_CASE(Global, ccnxNameSegment_ParseURISegment_NAME) +{ + char *lciSegment = CCNxNameLabel_Name "=" "NAME"; + PARCURISegment *segment = parcURISegment_Parse(lciSegment, NULL); + + CCNxNameSegment *actual = ccnxNameSegment_ParseURISegment(segment); + + CCNxNameLabelType type = ccnxNameSegment_GetType(actual); + assertTrue(CCNxNameLabelType_NAME == type, "Expected %04x, actual %04x", CCNxNameLabelType_NAME, type); + + PARCBuffer *buf = parcBuffer_WrapCString("NAME"); + CCNxNameSegment *expected = ccnxNameSegment_CreateTypeValue(CCNxNameLabelType_NAME, buf); + + assertTrue(ccnxNameSegment_Equals(expected, actual), "Error in ccnxNameSegment_ParseURISegment") + { + char *expectedString = ccnxNameSegment_ToString(expected); + char *actualString = ccnxNameSegment_ToString(expected); + fprintf(stderr, "Expected '%s', actual '%s'", expectedString, actualString); + parcMemory_Deallocate((void **) expectedString); + parcMemory_Deallocate((void **) actualString); + }; + + ccnxNameSegment_Release(&expected); + ccnxNameSegment_Release(&actual); + parcURISegment_Release(&segment); + parcBuffer_Release(&buf); +} + +LONGBOW_TEST_CASE(Global, ccnxNameSegment_ParseURISegment_META) +{ + char *lciSegment = CCNxNameLabel_ChunkMeta "=" "META"; + PARCURISegment *segment = parcURISegment_Parse(lciSegment, NULL); + + CCNxNameSegment *actual = ccnxNameSegment_ParseURISegment(segment); + + CCNxNameLabelType type = ccnxNameSegment_GetType(actual); + assertTrue(CCNxNameLabelType_CHUNKMETA == type, "Expected 0x%04x, actual 0x%04x", CCNxNameLabelType_CHUNKMETA, type); + + PARCBuffer *buf = parcBuffer_WrapCString("META"); + + CCNxNameSegment *expected = ccnxNameSegment_CreateTypeValue(CCNxNameLabelType_CHUNKMETA, buf); + + assertTrue(ccnxNameSegment_Equals(expected, actual), "Error in ccnxNameSegment_ParseURISegment") + { + char *expectedString = ccnxNameSegment_ToString(expected); + char *actualString = ccnxNameSegment_ToString(expected); + fprintf(stderr, "Expected '%s', actual '%s'", expectedString, actualString); + parcMemory_Deallocate((void **) expectedString); + parcMemory_Deallocate((void **) actualString); + }; + + ccnxNameSegment_Release(&expected); + ccnxNameSegment_Release(&actual); + parcURISegment_Release(&segment); + parcBuffer_Release(&buf); +} + +LONGBOW_TEST_CASE(Global, ccnxNameSegment_ParseURISegment_UnknownLabel) +{ + char *lciSegment = "unknown:param" "=" "abcdef"; + PARCURISegment *segment = parcURISegment_Parse(lciSegment, NULL); + + CCNxNameSegment *actual = ccnxNameSegment_ParseURISegment(segment); + + parcURISegment_Release(&segment); + + assertNull(actual, "Expected NULL return from ccnxNameSegment_ParseURISegment"); +} + +LONGBOW_TEST_CASE(Global, ccnxNameSegment_ParseURISegment_APP0) +{ + char *lciSegment = CCNxNameLabelType_LabelApp(0) "=" "APP0"; + PARCURISegment *segment = parcURISegment_Parse(lciSegment, NULL); + + CCNxNameSegment *actual = ccnxNameSegment_ParseURISegment(segment); + + CCNxNameLabelType type = ccnxNameSegment_GetType(actual); + assertTrue(CCNxNameLabelType_App(0) == type, "Expected %04x, actual %04x", CCNxNameLabelType_App(0), type); + + PARCBuffer *buf = parcBuffer_WrapCString("APP0"); + + CCNxNameSegment *expected = ccnxNameSegment_CreateTypeValue(CCNxNameLabelType_App(0), buf); + assertTrue(ccnxNameSegment_Equals(expected, actual), "Error in ccnxNameSegment_ParseURISegment") + { + char *expectedString = ccnxNameSegment_ToString(expected); + char *actualString = ccnxNameSegment_ToString(expected); + fprintf(stderr, "Expected '%s', actual '%s'", expectedString, actualString); + parcMemory_Deallocate((void **) expectedString); + parcMemory_Deallocate((void **) actualString); + }; + + ccnxNameSegment_Release(&expected); + ccnxNameSegment_Release(&actual); + parcURISegment_Release(&segment); + parcBuffer_Release(&buf); +} + +LONGBOW_TEST_CASE(Global, ccnxNameSegment_ParseURISegment_APP256) +{ + char *lciSegment = CCNxNameLabelType_LabelApp(255) "=" "APP255"; + PARCURISegment *segment = parcURISegment_Parse(lciSegment, NULL); + + CCNxNameSegment *actual = ccnxNameSegment_ParseURISegment(segment); + + CCNxNameLabelType type = ccnxNameSegment_GetType(actual); + assertTrue(CCNxNameLabelType_App(255) == type, "Expected %04x, actual %04x", CCNxNameLabelType_App(255), type); + + PARCBuffer *buf = parcBuffer_WrapCString("APP255"); + CCNxNameSegment *expected = ccnxNameSegment_CreateTypeValue(CCNxNameLabelType_App(255), buf); + parcBuffer_Release(&buf); + + assertTrue(ccnxNameSegment_Equals(expected, actual), "Error in ccnxNameSegment_ParseURISegment") + { + char *expectedString = ccnxNameSegment_ToString(expected); + char *actualString = ccnxNameSegment_ToString(expected); + fprintf(stderr, "Expected '%s', actual '%s'", expectedString, actualString); + parcMemory_Deallocate((void **) expectedString); + parcMemory_Deallocate((void **) actualString); + }; + + ccnxNameSegment_Release(&expected); + ccnxNameSegment_Release(&actual); + parcURISegment_Release(&segment); +} + +LONGBOW_TEST_CASE(Global, ccnxNameSegment_ParseURISegment) +{ + char *lciSegment = CCNxNameLabel_Name "=" "abcde"; + PARCURISegment *segment = parcURISegment_Parse(lciSegment, NULL); + + CCNxNameSegment *actual = ccnxNameSegment_ParseURISegment(segment); + + CCNxNameLabelType type = ccnxNameSegment_GetType(actual); + assertTrue(CCNxNameLabelType_NAME == type, + "Expected %04x, actual %04x", 0x20, type); + + PARCBuffer *buf = parcBuffer_WrapCString("abcde"); + + CCNxNameSegment *expected = ccnxNameSegment_CreateTypeValue(CCNxNameLabelType_NAME, buf); + + assertTrue(ccnxNameSegment_Equals(expected, actual), "Error in ccnxNameSegment_ParseURISegment") + { + char *expectedString = ccnxNameSegment_ToString(expected); + char *actualString = ccnxNameSegment_ToString(expected); + fprintf(stderr, "Expected '%s', actual '%s'", expectedString, actualString); + parcMemory_Deallocate((void **) expectedString); + parcMemory_Deallocate((void **) actualString); + }; + + ccnxNameSegment_Release(&expected); + parcBuffer_Release(&buf); + ccnxNameSegment_Release(&actual); + parcURISegment_Release(&segment); +} + +LONGBOW_TEST_CASE(Global, ccnxNameSegment_ZeroLength) +{ + char *begin = ""; + + PARCBuffer *buffer = parcBuffer_WrapCString(begin); + CCNxNameSegment *segment = ccnxNameSegment_CreateTypeValue(CCNxNameLabelType_NAME, buffer); + + assertNotNull(segment, "Expected non-null"); + assertTrue(ccnxNameSegment_Length(segment) == 0, "Failed to create a zero length segment"); + + ccnxNameSegment_Release(&segment); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, ccnxNameSegment_Equals_Contract) +{ + PARCBuffer *bufX = parcBuffer_WrapCString("Test"); + PARCBuffer *bufY = parcBuffer_WrapCString("Test"); + PARCBuffer *bufZ = parcBuffer_WrapCString("Test"); + PARCBuffer *bufU1 = parcBuffer_WrapCString("Test"); + PARCBuffer *bufU2 = parcBuffer_WrapCString("blah"); + + CCNxNameSegment *x = ccnxNameSegment_CreateTypeValue(CCNxNameLabelType_NAME, bufX); + CCNxNameSegment *y = ccnxNameSegment_CreateTypeValue(CCNxNameLabelType_NAME, bufY); + CCNxNameSegment *z = ccnxNameSegment_CreateTypeValue(CCNxNameLabelType_NAME, bufZ); + CCNxNameSegment *u1 = ccnxNameSegment_CreateTypeValue(CCNxNameLabelType_CHUNKMETA, bufU1); + CCNxNameSegment *u2 = ccnxNameSegment_CreateTypeValue(CCNxNameLabelType_NAME, bufU2); + + assertEqualsContract(ccnxNameSegment_Equals, x, y, z, u1, u2, NULL); + + ccnxNameSegment_Release(&x); + ccnxNameSegment_Release(&y); + ccnxNameSegment_Release(&z); + ccnxNameSegment_Release(&u1); + ccnxNameSegment_Release(&u2); + + parcBuffer_Release(&bufX); + parcBuffer_Release(&bufY); + parcBuffer_Release(&bufZ); + parcBuffer_Release(&bufU1); + parcBuffer_Release(&bufU2); +} + +LONGBOW_TEST_CASE(Global, ccnxNameSegment_Compare_Contract) +{ + char *aString = "foo"; + PARCBuffer *bufA = parcBuffer_WrapCString(aString); + CCNxNameSegment *a = ccnxNameSegment_CreateTypeValue(CCNxNameLabelType_NAME, bufA); + + PARCBuffer *bufFoo = parcBuffer_WrapCString("foo"); + PARCBuffer *bufFon = parcBuffer_WrapCString("fon"); + PARCBuffer *bufFo = parcBuffer_WrapCString("fo"); + PARCBuffer *bufFop = parcBuffer_WrapCString("fop"); + PARCBuffer *bufFooa = parcBuffer_WrapCString("fooa"); + + CCNxNameSegment **equivalents = (CCNxNameSegment *[]) { + ccnxNameSegment_CreateTypeValue(CCNxNameLabelType_NAME, bufFoo), + NULL + }; + CCNxNameSegment **lessers = (CCNxNameSegment *[]) { + ccnxNameSegment_CreateTypeValue(CCNxNameLabelType_NAME, bufFon), + ccnxNameSegment_CreateTypeValue(CCNxNameLabelType_NAME, bufFo), + NULL + }; + CCNxNameSegment **greaters = (CCNxNameSegment *[]) { + ccnxNameSegment_CreateTypeValue(CCNxNameLabelType_NAME, bufFop), + ccnxNameSegment_CreateTypeValue(CCNxNameLabelType_NAME, bufFooa), + NULL + }; + + assertCompareToContract(ccnxNameSegment_Compare, a, equivalents, lessers, greaters); + + ccnxNameSegment_Release(&a); + + for (int i = 0; equivalents[i] != NULL; i++) { + ccnxNameSegment_Release(&equivalents[i]); + } + for (int i = 0; lessers[i] != NULL; i++) { + ccnxNameSegment_Release(&lessers[i]); + } + for (int i = 0; greaters[i] != NULL; i++) { + ccnxNameSegment_Release(&greaters[i]); + } + + parcBuffer_Release(&bufFoo); + parcBuffer_Release(&bufFon); + parcBuffer_Release(&bufFo); + parcBuffer_Release(&bufFop); + parcBuffer_Release(&bufFooa); + parcBuffer_Release(&bufA); +} + +LONGBOW_TEST_CASE(Global, ccnxNameSegment_Length) +{ + char *expected = "foo"; + + PARCBuffer *buffer = parcBuffer_WrapCString(expected); + CCNxNameSegment *segment = ccnxNameSegment_CreateTypeValue(CCNxNameLabelType_NAME, buffer); + + size_t actual = ccnxNameSegment_Length(segment); + + ccnxNameSegment_Release(&segment); + + assertTrue(strlen(expected) == actual, + "Expected %zd, actual %zd", strlen(expected), actual); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, ccnxNameSegment_Copy) +{ + PARCBuffer *buf = parcBuffer_WrapCString("foo"); + CCNxNameSegment *expected = ccnxNameSegment_CreateTypeValue(CCNxNameLabelType_NAME, buf); + CCNxNameSegment *actual = ccnxNameSegment_Copy(expected); + CCNxNameSegment *acquiredCopy = ccnxNameSegment_Acquire(expected); + + assertTrue(expected != actual, "Expected a distinct copy of the original."); + + char *expectedString = ccnxNameSegment_ToString(expected); + char *actualString = ccnxNameSegment_ToString(actual); + char *acquiredString = ccnxNameSegment_ToString(acquiredCopy); + assertTrue(ccnxNameSegment_Equals(expected, actual), "Expected %s, actual %s", expectedString, actualString); + assertTrue(ccnxNameSegment_Equals(expected, acquiredCopy), "Expected %s, actual %s", expectedString, acquiredString); + + parcMemory_Deallocate((void **) &expectedString); + parcMemory_Deallocate((void **) &actualString); + parcMemory_Deallocate((void **) &acquiredString); + + ccnxNameSegment_Release(&acquiredCopy); + ccnxNameSegment_Release(&expected); + ccnxNameSegment_Release(&actual); + parcBuffer_Release(&buf); +} + +LONGBOW_TEST_CASE(Global, ccnxNameSegment_Copy_WithParameter) +{ + PARCBuffer *value = parcBuffer_WrapCString("value"); + PARCBuffer *parameter = parcBuffer_WrapCString("param"); + CCNxNameLabel *label = ccnxNameLabel_Create(CCNxNameLabelType_NAME, parameter); + CCNxNameSegment *expected = ccnxNameSegment_CreateLabelValue(label, value); + ccnxNameLabel_Release(&label); + parcBuffer_Release(&value); + parcBuffer_Release(¶meter); + + CCNxNameSegment *actual = ccnxNameSegment_Copy(expected); + + assertTrue(expected != actual, "Expected a distinct copy of the original."); + + char *expectedString = ccnxNameSegment_ToString(expected); + char *actualString = ccnxNameSegment_ToString(actual); + assertTrue(ccnxNameSegment_Equals(expected, actual), "Expected '%s', actual '%s'", expectedString, actualString); + + parcMemory_Deallocate((void **) &expectedString); + parcMemory_Deallocate((void **) &actualString); + + ccnxNameSegment_Release(&expected); + ccnxNameSegment_Release(&actual); +} + +LONGBOW_TEST_CASE(Global, ccnxNameSegment_GetType) +{ + PARCBuffer *buf = parcBuffer_WrapCString("hello"); + CCNxNameSegment *segment = ccnxNameSegment_CreateTypeValue(CCNxNameLabelType_NAME, buf); + + assertTrue(CCNxNameLabelType_NAME == ccnxNameSegment_GetType(segment), + "Expected type %d, actual %d", CCNxNameLabelType_NAME, ccnxNameSegment_GetType(segment)); + + ccnxNameSegment_Release(&segment); + parcBuffer_Release(&buf); +} + +LONGBOW_TEST_CASE(Global, ccnxNameSegment_ToString_META) +{ + char *expected = CCNxNameLabel_ChunkMeta "=META"; + PARCBuffer *buf = parcBuffer_WrapCString("META"); + CCNxNameSegment *segment = ccnxNameSegment_CreateTypeValue(CCNxNameLabelType_CHUNKMETA, buf); + char *actual = ccnxNameSegment_ToString(segment); + assertTrue(strcmp(expected, actual) == 0, + "Expected %s, actual %s", expected, actual); + parcMemory_Deallocate((void **) &actual); + + ccnxNameSegment_Release(&segment); + parcBuffer_Release(&buf); +} + +LONGBOW_TEST_CASE(Global, ccnxNameSegment_ToString_PAYLOADHASH) +{ + char *expected = CCNxNameLabel_InterestPayloadId "=PAYLOADHASH"; + PARCBuffer *buf = parcBuffer_WrapCString("PAYLOADHASH"); + CCNxNameSegment *segment = ccnxNameSegment_CreateTypeValue(CCNxNameLabelType_PAYLOADID, buf); + + char *actual = ccnxNameSegment_ToString(segment); + assertTrue(strcmp(expected, actual) == 0, + "Expected %s, actual %s", expected, actual); + parcMemory_Deallocate((void **) &actual); + + ccnxNameSegment_Release(&segment); + parcBuffer_Release(&buf); +} + +LONGBOW_TEST_CASE(Global, ccnxNameSegment_ToString_NAME) +{ + PARCBuffer *buf = parcBuffer_WrapCString("NAME"); + CCNxNameSegment *segment = ccnxNameSegment_CreateTypeValue(CCNxNameLabelType_NAME, buf); + + // Note that this is different than the other tests for segments because a NAME name segment + // is the default type and as such the string representation doesn't include the leading label specification. + char *expected = "NAME"; + char *actual = ccnxNameSegment_ToString(segment); + assertTrue(strcmp(expected, actual) == 0, + "Expected %s, actual %s", expected, actual); + parcMemory_Deallocate((void **) &actual); + + ccnxNameSegment_Release(&segment); + parcBuffer_Release(&buf); +} + +LONGBOW_TEST_CASE(Global, ccnxNameSegment_ToString_NAME_NotDefault) +{ + PARCBuffer *value = parcBuffer_WrapCString("MiISAg=="); + CCNxNameSegment *segment = ccnxNameSegment_CreateTypeValue(CCNxNameLabelType_NAME, value); + + // Note that this is different than the other tests for segments because a NAME name segment + // is the default type and as such the string representation doesn't include the leading label specification. + char *expected = CCNxNameLabel_Name "=" "MiISAg%3D%3D"; + char *actual = ccnxNameSegment_ToString(segment); + assertTrue(strcmp(expected, actual) == 0, + "Expected %s, actual %s", expected, actual); + parcMemory_Deallocate((void **) &actual); + + ccnxNameSegment_Release(&segment); + parcBuffer_Release(&value); +} + +LONGBOW_TEST_CASE(Global, ccnxNameSegment_ToString_APP0) +{ + char *expected = CCNxNameLabelType_LabelApp(0) "=APP0"; + PARCBuffer *buf = parcBuffer_WrapCString("APP0"); + CCNxNameSegment *segment = ccnxNameSegment_CreateTypeValue(CCNxNameLabelType_App(0), buf); + + char *actual = ccnxNameSegment_ToString(segment); + assertTrue(strcmp(expected, actual) == 0, + "Expected %s, actual %s", expected, actual); + parcMemory_Deallocate((void **) &actual); + + ccnxNameSegment_Release(&segment); + parcBuffer_Release(&buf); +} + +LONGBOW_TEST_CASE(Global, ccnxNameSegment_ToString_SERIAL) +{ + char *expected = CCNxNameLabel_Serial "=serialnumber"; + PARCBuffer *buf = parcBuffer_WrapCString("serialnumber"); + CCNxNameSegment *segment = ccnxNameSegment_CreateTypeValue(CCNxNameLabelType_SERIAL, buf); + + char *actual = ccnxNameSegment_ToString(segment); + assertTrue(strcmp(expected, actual) == 0, + "Expected %s, actual %s", expected, actual); + parcMemory_Deallocate((void **) &actual); + + ccnxNameSegment_Release(&segment); + parcBuffer_Release(&buf); +} + +LONGBOW_TEST_CASE(Global, ccnxNameSegment_ToString_APP256) +{ + char *expected = CCNxNameLabelType_LabelApp(255) "=APP255"; + PARCBuffer *buf = parcBuffer_WrapCString("APP255"); + + CCNxNameSegment *segment = ccnxNameSegment_CreateTypeValue(CCNxNameLabelType_App(255), buf); + char *actual = ccnxNameSegment_ToString(segment); + assertTrue(strcmp(expected, actual) == 0, + "Expected %s, actual %s", expected, actual); + parcMemory_Deallocate((void **) &actual); + + ccnxNameSegment_Release(&segment); + parcBuffer_Release(&buf); +} + +LONGBOW_TEST_CASE(Global, ccnxNameSegment_HashCode) +{ + PARCBuffer *bufA = parcBuffer_WrapCString("Test"); + + CCNxNameSegment *segmentA = ccnxNameSegment_CreateTypeValue(CCNxNameLabelType_NAME, bufA); + CCNxNameSegment *segmentB = ccnxNameSegment_CreateTypeValue(CCNxNameLabelType_CHUNKMETA, bufA); + + assertFalse(ccnxNameSegment_HashCode(segmentA) == ccnxNameSegment_HashCode(segmentB), + "Expected different hash codes"); + + PARCBuffer *bufC = parcBuffer_WrapCString("Not Test"); + + CCNxNameSegment *segmentC = ccnxNameSegment_CreateTypeValue(CCNxNameLabelType_NAME, bufC); + + assertFalse(ccnxNameSegment_HashCode(segmentA) == ccnxNameSegment_HashCode(segmentC), + "Expected different hash codes"); + + CCNxNameSegment *segmentD = ccnxNameSegment_CreateTypeValue(CCNxNameLabelType_NAME, bufA); + + assertTrue(ccnxNameSegment_HashCode(segmentD) == ccnxNameSegment_HashCode(segmentA), + "Expected same hash codes"); + + ccnxNameSegment_Release(&segmentA); + ccnxNameSegment_Release(&segmentB); + ccnxNameSegment_Release(&segmentC); + ccnxNameSegment_Release(&segmentD); + + parcBuffer_Release(&bufA); + parcBuffer_Release(&bufC); +} + +LONGBOW_TEST_CASE(Global, ccnxNameSegment_Display) +{ + PARCBuffer *buf = parcBuffer_WrapCString("Test"); + + CCNxNameSegment *segment = ccnxNameSegment_CreateTypeValue(CCNxNameLabelType_NAME, buf); + + ccnxNameSegment_Display(segment, 0); + + ccnxNameSegment_Release(&segment); + parcBuffer_Release(&buf); +} + +LONGBOW_TEST_CASE_EXPECTS(Global, ccnxNameSegment_AssertValid_Invalid, .event = &LongBowAssertEvent) +{ + ccnxNameSegment_AssertValid(NULL); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(ccnx_NameComponent); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-common/ccnx/common/test/test_ccnx_NameSegmentNumber.c b/libccnx-common/ccnx/common/test/test_ccnx_NameSegmentNumber.c new file mode 100755 index 00000000..fb14ef39 --- /dev/null +++ b/libccnx-common/ccnx/common/test/test_ccnx_NameSegmentNumber.c @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ +#include "../ccnx_NameSegmentNumber.c" + +#include <LongBow/unit-test.h> + +#include <stdio.h> +#include <inttypes.h> + +#include <parc/algol/parc_SafeMemory.h> + +LONGBOW_TEST_RUNNER(test_ccnx_NameSegmentNumber) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(test_ccnx_NameSegmentNumber) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(test_ccnx_NameSegmentNumber) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, ccnxNameSegmentNumber_Create64bits); + LONGBOW_RUN_TEST_CASE(Global, ccnxNameSegmentNumber_Create56bits); + LONGBOW_RUN_TEST_CASE(Global, ccnxNameSegmentNumber_Create48bits); + LONGBOW_RUN_TEST_CASE(Global, ccnxNameSegmentNumber_Create40bits); + LONGBOW_RUN_TEST_CASE(Global, ccnxNameSegmentNumber_Create32bits); + LONGBOW_RUN_TEST_CASE(Global, ccnxNameSegmentNumber_Create24bits); + LONGBOW_RUN_TEST_CASE(Global, ccnxNameSegmentNumber_Create16bits); + LONGBOW_RUN_TEST_CASE(Global, ccnxNameSegmentNumber_Create8bits); + LONGBOW_RUN_TEST_CASE(Global, ccnxNameSegmentNumber_BorderCases); + LONGBOW_RUN_TEST_CASE(Global, ccnxNameSegmentNumber_IsValid); + LONGBOW_RUN_TEST_CASE(Global, ccnxNameSegmentNumber_IsValid_False); + LONGBOW_RUN_TEST_CASE(Global, ccnxNameSegmentNumber_AssertValid); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDOUT_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, ccnxNameSegmentNumber_Create64bits) +{ + uint64_t expected = 0x123456789ABCDEF0; + CCNxNameSegment *segment = ccnxNameSegmentNumber_Create(CCNxNameLabelType_CHUNK, expected); + + uint64_t actual = ccnxNameSegmentNumber_Value(segment); + + assertTrue(expected == actual, "Expected 0x%" PRIX64 " actual 0x%" PRIX64 "", expected, actual); + + ccnxNameSegment_Release(&segment); +} + +LONGBOW_TEST_CASE(Global, ccnxNameSegmentNumber_Create56bits) +{ + uint64_t expected = 0x123456789ABCDE; + CCNxNameSegment *segment = ccnxNameSegmentNumber_Create(CCNxNameLabelType_CHUNK, expected); + + uint64_t actual = ccnxNameSegmentNumber_Value(segment); + + assertTrue(expected == actual, "Expected 0x%" PRIX64 " actual 0x%" PRIX64 "", expected, actual); + ccnxNameSegment_Release(&segment); +} + +LONGBOW_TEST_CASE(Global, ccnxNameSegmentNumber_Create48bits) +{ + uint64_t expected = 0x123456789ABC; + CCNxNameSegment *segment = ccnxNameSegmentNumber_Create(CCNxNameLabelType_CHUNK, expected); + + uint64_t actual = ccnxNameSegmentNumber_Value(segment); + + assertTrue(expected == actual, "Expected 0x%" PRIX64 " actual 0x%" PRIX64 "", expected, actual); + ccnxNameSegment_Release(&segment); +} + +LONGBOW_TEST_CASE(Global, ccnxNameSegmentNumber_Create40bits) +{ + uint64_t expected = 0x123456789A; + CCNxNameSegment *segment = ccnxNameSegmentNumber_Create(CCNxNameLabelType_CHUNK, expected); + + uint64_t actual = ccnxNameSegmentNumber_Value(segment); + + assertTrue(expected == actual, "Expected 0x%" PRIX64 " actual 0x%" PRIX64 "", expected, actual); + ccnxNameSegment_Release(&segment); +} + +LONGBOW_TEST_CASE(Global, ccnxNameSegmentNumber_Create32bits) +{ + uint64_t expected = 0x12345678; + CCNxNameSegment *segment = ccnxNameSegmentNumber_Create(CCNxNameLabelType_CHUNK, expected); + + uint64_t actual = ccnxNameSegmentNumber_Value(segment); + + assertTrue(expected == actual, "Expected 0x%" PRIX64 " actual 0x%" PRIX64 "", expected, actual); + ccnxNameSegment_Release(&segment); +} + +LONGBOW_TEST_CASE(Global, ccnxNameSegmentNumber_Create24bits) +{ + uint64_t expected = 0x123456; + CCNxNameSegment *segment = ccnxNameSegmentNumber_Create(CCNxNameLabelType_CHUNK, expected); + + uint64_t actual = ccnxNameSegmentNumber_Value(segment); + + assertTrue(expected == actual, "Expected 0x%" PRIX64 " actual 0x%" PRIX64 "", expected, actual); + ccnxNameSegment_Release(&segment); +} + +LONGBOW_TEST_CASE(Global, ccnxNameSegmentNumber_Create16bits) +{ + uint64_t expected = 0x1234; + CCNxNameSegment *segment = ccnxNameSegmentNumber_Create(CCNxNameLabelType_CHUNK, expected); + + uint64_t actual = ccnxNameSegmentNumber_Value(segment); + + assertTrue(expected == actual, "Expected 0x%" PRIX64 " actual 0x%" PRIX64 "", expected, actual); + ccnxNameSegment_Release(&segment); +} + +LONGBOW_TEST_CASE(Global, ccnxNameSegmentNumber_Create8bits) +{ + uint64_t expected = 0x12; + CCNxNameSegment *segment = ccnxNameSegmentNumber_Create(CCNxNameLabelType_CHUNK, expected); + + uint64_t actual = ccnxNameSegmentNumber_Value(segment); + + assertTrue(expected == actual, "Expected 0x%" PRIX64 " actual 0x%" PRIX64 "", expected, actual); + ccnxNameSegment_Release(&segment); +} + +LONGBOW_TEST_CASE(Global, ccnxNameSegmentNumber_BorderCases) +{ + struct test_struct { + uint64_t value; + size_t length; + uint8_t *encoded; + } test_vector[] = { + { .value = 0x0000000000000000ULL, .length = 1, .encoded = (uint8_t[1]) { 0x00 } }, + { .value = 0x0000000000000001ULL, .length = 1, .encoded = (uint8_t[1]) { 0x01 } }, + { .value = 0x00000000000000FFULL, .length = 1, .encoded = (uint8_t[1]) { 0xFF } }, + { .value = 0x0000000000000100ULL, .length = 2, .encoded = (uint8_t[2]) { 0x01, 0x00} }, + { .value = 0x0100000000000100ULL, .length = 8, .encoded = (uint8_t[8]) { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00} }, + { .value = 0x8000000000000100ULL, .length = 8, .encoded = (uint8_t[8]) { 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00} }, + { .value = 0xFFFFFFFFFFFFFFFFULL, .length = 8, .encoded = (uint8_t[8]) { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} }, + { .value = 0, .length = 0, .encoded = NULL } + }; + + for (int i = 0; test_vector[i].encoded != NULL; i++) { + PARCBuffer *buffer = + parcBuffer_Wrap(test_vector[i].encoded, test_vector[i].length, 0, test_vector[i].length); + CCNxNameSegment *expected = ccnxNameSegment_CreateTypeValue(CCNxNameLabelType_NAME, buffer); + + CCNxNameSegment *actual = ccnxNameSegmentNumber_Create(CCNxNameLabelType_NAME, test_vector[i].value); + + assertTrue(ccnxNameSegment_Equals(expected, actual), + "Buffers do not match: test_vector[%d] value %" PRIX64 " Expected %" PRIX64 " actual %" PRIX64 "", + i, + test_vector[i].value, + ccnxNameSegmentNumber_Value(expected), + ccnxNameSegmentNumber_Value(actual)); + + ccnxNameSegment_Release(&expected); + ccnxNameSegment_Release(&actual); + parcBuffer_Release(&buffer); + } +} + +LONGBOW_TEST_CASE(Global, ccnxNameSegmentNumber_AssertValid) +{ + uint64_t expected = 0x12; + CCNxNameSegment *segment = ccnxNameSegmentNumber_Create(CCNxNameLabelType_CHUNK, expected); + + ccnxNameSegmentNumber_AssertValid(segment); + ccnxNameSegment_Release(&segment); +} + +LONGBOW_TEST_CASE(Global, ccnxNameSegmentNumber_IsValid) +{ + uint64_t expected = 0x12; + CCNxNameSegment *segment = ccnxNameSegmentNumber_Create(CCNxNameLabelType_CHUNK, expected); + + assertTrue(ccnxNameSegmentNumber_IsValid(segment), "Expected the CCNxNameSegment to be valid."); + ccnxNameSegment_Release(&segment); +} + +LONGBOW_TEST_CASE(Global, ccnxNameSegmentNumber_IsValid_False) +{ + uint64_t expected = 0x12; + CCNxNameSegment *segment = ccnxNameSegmentNumber_Create(CCNxNameLabelType_CHUNK, expected); + + PARCBuffer *value = ccnxNameSegment_GetValue(segment); + parcBuffer_SetPosition(value, parcBuffer_Limit(value)); // Wreck the buffer by making it zero length. + + assertFalse(ccnxNameSegmentNumber_IsValid(segment), "Expected the CCNxNameSegment to be valid."); + ccnxNameSegment_Release(&segment); +} + +int +main(int argc, char *argv[argc]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(test_ccnx_NameSegmentNumber); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-common/ccnx/common/test/test_ccnx_TimeStamp.c b/libccnx-common/ccnx/common/test/test_ccnx_TimeStamp.c new file mode 100755 index 00000000..d65ebd64 --- /dev/null +++ b/libccnx-common/ccnx/common/test/test_ccnx_TimeStamp.c @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ +#include <ccnx/common/ccnx_TimeStamp.c> + +#include <inttypes.h> +#include <time.h> + +#include <LongBow/unit-test.h> +#include <parc/algol/parc_SafeMemory.h> + + +LONGBOW_TEST_RUNNER(ccnx_TimeStamp) +{ + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +LONGBOW_TEST_RUNNER_SETUP(ccnx_TimeStamp) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_RUNNER_TEARDOWN(ccnx_TimeStamp) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, ccnxTimeStamp_CreateFromCurrentUTCTime); + LONGBOW_RUN_TEST_CASE(Global, ccnxTimeStamp_CreateFromTimespec); + LONGBOW_RUN_TEST_CASE(Global, ccnxTimeStamp_CreateFromMillisecondsSinceEpoch); + LONGBOW_RUN_TEST_CASE(Global, ccnxTimeStamp_Copy); + LONGBOW_RUN_TEST_CASE(Global, ccnxTimeStamp_Equals); + + LONGBOW_RUN_TEST_CASE(Global, ccnxTimeStamp_AsNanoSeconds); + LONGBOW_RUN_TEST_CASE(Global, ccnxTimeStamp_CreateFromNanosecondsSinceEpoch); + LONGBOW_RUN_TEST_CASE(Global, ccnxTimeStamp_ToString); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, ccnxTimeStamp_CreateFromCurrentUTCTime) +{ + CCNxTimeStamp *timeStamp = ccnxTimeStamp_CreateFromCurrentUTCTime(); + assertNotNull(timeStamp, "Expected a non-null response"); + + ccnxTimeStamp_Release(&timeStamp); + assertNull(timeStamp, "Release failed to NULL the pointer."); +} + +LONGBOW_TEST_CASE(Global, ccnxTimeStamp_CreateFromTimespec) +{ + struct timespec time = { .tv_sec = 1, .tv_nsec = 1 }; + + CCNxTimeStamp *timeStamp = ccnxTimeStamp_CreateFromTimespec(&time); + assertNotNull(timeStamp, "Expected a non-null response"); + + struct timespec actualTime = ccnxTimeStamp_AsTimespec(timeStamp); + + assertTrue(time.tv_sec == actualTime.tv_sec && time.tv_nsec == actualTime.tv_nsec, "Expected timespec to be equal."); + + ccnxTimeStamp_Release(&timeStamp); + assertNull(timeStamp, "Release failed to NULL the pointer."); +} + +LONGBOW_TEST_CASE(Global, ccnxTimeStamp_CreateFromMillisecondsSinceEpoch) +{ + time_t theTimeInSeconds; + time(&theTimeInSeconds); + + CCNxTimeStamp *timeStamp = ccnxTimeStamp_CreateFromMillisecondsSinceEpoch((uint64_t) theTimeInSeconds * 1000); + assertNotNull(timeStamp, "Expected a non-null response"); + + struct timespec timeSpec = ccnxTimeStamp_AsTimespec(timeStamp); + + assertTrue(theTimeInSeconds == timeSpec.tv_sec, "Expected %ld, actual %ld", theTimeInSeconds, timeSpec.tv_sec); + + assertTrue(0 == timeSpec.tv_nsec, "Expected %d, actual %ld", 0, timeSpec.tv_nsec); + + ccnxTimeStamp_Release(&timeStamp); + assertNull(timeStamp, "Release failed to NULL the pointer."); +} + +LONGBOW_TEST_CASE(Global, ccnxTimeStamp_CreateFromNanosecondsSinceEpoch) +{ + uint64_t expected = 1099511627776ULL; + + CCNxTimeStamp *timeStamp = ccnxTimeStamp_CreateFromNanosecondsSinceEpoch(expected); + + uint64_t actual = ccnxTimeStamp_AsNanoSeconds(timeStamp); + + assertTrue(expected == actual, "Expected %" PRIu64 " actual %" PRIu64, expected, actual); + + ccnxTimeStamp_Release(&timeStamp); +} + +LONGBOW_TEST_CASE(Global, ccnxTimeStamp_Equals) +{ + time_t theTimeInSeconds; + time(&theTimeInSeconds); + + CCNxTimeStamp *x = ccnxTimeStamp_CreateFromMillisecondsSinceEpoch(theTimeInSeconds * 1000); + CCNxTimeStamp *y = ccnxTimeStamp_CreateFromMillisecondsSinceEpoch(theTimeInSeconds * 1000); + CCNxTimeStamp *z = ccnxTimeStamp_CreateFromMillisecondsSinceEpoch(theTimeInSeconds * 1000); + CCNxTimeStamp *u1 = ccnxTimeStamp_CreateFromMillisecondsSinceEpoch((theTimeInSeconds + 1) * 1000); + CCNxTimeStamp *u2 = ccnxTimeStamp_CreateFromMillisecondsSinceEpoch((theTimeInSeconds + 2) * 1000); + + assertEqualsContract(ccnxTimeStamp_Equals, x, y, z, u1, u2) + + ccnxTimeStamp_Release(&x); + ccnxTimeStamp_Release(&y); + ccnxTimeStamp_Release(&z); + ccnxTimeStamp_Release(&u1); + ccnxTimeStamp_Release(&u2); +} + +LONGBOW_TEST_CASE(Global, ccnxTimeStamp_Copy) +{ + time_t theTime; + time(&theTime); + + CCNxTimeStamp *timeStamp = ccnxTimeStamp_CreateFromMillisecondsSinceEpoch(theTime); + assertNotNull(timeStamp, "Expected a non-null response"); + + CCNxTimeStamp *copy = ccnxTimeStamp_Copy(timeStamp); + + char *expected = ccnxTimeStamp_ToString(timeStamp); + char *actual = ccnxTimeStamp_ToString(copy); + assertTrue(ccnxTimeStamp_Equals(timeStamp, copy), + "Expected %s actual %s.", expected, actual); + parcMemory_Deallocate((void **) &expected); + parcMemory_Deallocate((void **) &actual); + + ccnxTimeStamp_Release(&timeStamp); + ccnxTimeStamp_Release(©); + assertNull(timeStamp, "Destroy failed to NULL the pointer."); +} + +LONGBOW_TEST_CASE(Global, ccnxTimeStamp_AsNanoSeconds) +{ + uint64_t expected = 1099501627776ULL; + + CCNxTimeStamp *timeStamp = ccnxTimeStamp_CreateFromNanosecondsSinceEpoch(expected); + + uint64_t actual = ccnxTimeStamp_AsNanoSeconds(timeStamp); + + assertTrue(expected == actual, "Expected %" PRIu64 " actual %" PRIu64, expected, actual); + + ccnxTimeStamp_Release(&timeStamp); +} + +LONGBOW_TEST_CASE(Global, ccnxTimeStamp_ToString) +{ + CCNxTimeStamp *timeStamp = ccnxTimeStamp_CreateFromCurrentUTCTime(); + assertNotNull(timeStamp, "Expected a non-null response"); + + char *string = ccnxTimeStamp_ToString(timeStamp); + assertNotNull(string, "Expected non-null result."); + + parcMemory_Deallocate((void **) &string); + + ccnxTimeStamp_Release(&timeStamp); + assertNull(timeStamp, "Destroy failed to NULL the pointer."); + // See case 1016 + testUnimplemented("This test is unimplemented"); +} + +LONGBOW_TEST_FIXTURE(Local) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +int +main(int argc, char *argv[argc]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(ccnx_TimeStamp); + exit(longBowMain(argc, argv, testRunner, NULL)); +} diff --git a/libccnx-common/ccnx/common/test/test_ccnx_WireFormatMessage.c b/libccnx-common/ccnx/common/test/test_ccnx_WireFormatMessage.c new file mode 100755 index 00000000..43b27dc2 --- /dev/null +++ b/libccnx-common/ccnx/common/test/test_ccnx_WireFormatMessage.c @@ -0,0 +1,502 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Runner. +#include "../ccnx_WireFormatMessage.c" + +#include <stdio.h> + +#include <LongBow/unit-test.h> + +#include <parc/algol/parc_SafeMemory.h> + +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_TlvDictionary.h> + +#include <ccnx/common/codec/ccnxCodec_TlvPacket.h> +#include <ccnx/common/codec/schema_v1/testdata/v1_interest_nameA.h> +#include <ccnx/common/ccnx_ContentObject.h> + + +LONGBOW_TEST_RUNNER(ccnx_WireFormatMessage) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Static); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(ccnx_WireFormatMessage) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(ccnx_WireFormatMessage) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, ccnxWireFormatMessage_AcquireRelease); + LONGBOW_RUN_TEST_CASE(Global, ccnxWireFormatMessage_AssertValid); + LONGBOW_RUN_TEST_CASE(Global, ccnxWireFormatMessage_Create); + LONGBOW_RUN_TEST_CASE(Global, ccnxWireFormatMessage_FromContentObjectPacketType); + LONGBOW_RUN_TEST_CASE(Global, ccnxWireFormatMessage_FromControlPacketType); + LONGBOW_RUN_TEST_CASE(Global, ccnxWireFormatMessage_FromInterestPacketType); + LONGBOW_RUN_TEST_CASE(Global, ccnxWireFormatMessage_FromInterestPacketTypeIoVec); + LONGBOW_RUN_TEST_CASE(Global, ccnxWireFormatMessage_GetDictionary); + LONGBOW_RUN_TEST_CASE(Global, ccnxWireFormatMessage_PutGetIoVec); + LONGBOW_RUN_TEST_CASE(Global, ccnxWireFormatMessage_GetWireFormatBuffer); + // + LONGBOW_RUN_TEST_CASE(Global, ccnxWireFormatMessage_CreateContentObjectHash); + LONGBOW_RUN_TEST_CASE(Global, ccnxWireFormatMessage_HashProtectedRegion); + // + LONGBOW_RUN_TEST_CASE(Global, ccnxWireFormatMessage_PutWireFormatBuffer); + LONGBOW_RUN_TEST_CASE(Global, ccnxWireFormatMessage_SetProtectedRegionLength); + LONGBOW_RUN_TEST_CASE(Global, ccnxWireFormatMessage_SetProtectedRegionStart); + LONGBOW_RUN_TEST_CASE(Global, ccnxWireFormatMessage_WriteToFile); + LONGBOW_RUN_TEST_CASE(Global, ccnxWireFormatMessage_SetHopLimit); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, ccnxWireFormatMessage_AcquireRelease) +{ + PARCBuffer *buffer = parcBuffer_Allocate(10); + CCNxWireFormatMessage *message = ccnxWireFormatMessage_FromInterestPacketType(CCNxTlvDictionary_SchemaVersion_V1, buffer); + + CCNxWireFormatMessage *ref = ccnxWireFormatMessage_Acquire(message); + assertNotNull(ref, "Expected a non-NULL reference to be acquired"); + + ccnxWireFormatMessage_Release(&message); + assertNotNull(ref, "Expected a non-NULL reference to be acquired"); + assertNull(message, "Expeced original message to be NULL"); + + ccnxWireFormatMessage_Release(&ref); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, ccnxWireFormatMessage_AssertValid) +{ + PARCBuffer *wireFormat = parcBuffer_Wrap(v1_interest_nameA, sizeof(v1_interest_nameA), 0, sizeof(v1_interest_nameA)); + + CCNxWireFormatMessage *message = ccnxWireFormatMessage_Create(wireFormat); + assertNotNull(message, "Got null CCNxWireFormatMessage, after attempting to create with buffer"); + + ccnxWireFormatMessage_AssertValid(message); + + ccnxWireFormatMessage_Release(&message); + parcBuffer_Release(&wireFormat); +} + +LONGBOW_TEST_CASE(Global, ccnxWireFormatMessage_FromContentObjectPacketType) +{ + PARCBuffer *buffer = parcBuffer_Allocate(10); + CCNxWireFormatMessage *message = ccnxWireFormatMessage_FromContentObjectPacketType(CCNxTlvDictionary_SchemaVersion_V1, buffer); + + assertTrue(ccnxTlvDictionary_IsContentObject((CCNxTlvDictionary *) message), "Wrong message type"); + + ccnxWireFormatMessage_Release(&message); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, ccnxWireFormatMessage_FromControlPacketType) +{ + PARCBuffer *buffer = parcBuffer_Allocate(10); + CCNxWireFormatMessage *message = ccnxWireFormatMessage_FromControlPacketType(CCNxTlvDictionary_SchemaVersion_V1, buffer); + + assertTrue(ccnxTlvDictionary_IsControl((CCNxTlvDictionary *) message), "Wrong message type"); + + ccnxWireFormatMessage_Release(&message); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, ccnxWireFormatMessage_FromInterestPacketType) +{ + PARCBuffer *buffer = parcBuffer_Allocate(10); + CCNxWireFormatMessage *message = ccnxWireFormatMessage_FromInterestPacketType(CCNxTlvDictionary_SchemaVersion_V1, buffer); + + assertTrue(ccnxTlvDictionary_IsInterest((CCNxTlvDictionary *) message), "Wrong message type"); + + ccnxWireFormatMessage_Release(&message); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, ccnxWireFormatMessage_Create) +{ + PARCBuffer *wireFormat = parcBuffer_Wrap(v1_interest_nameA, sizeof(v1_interest_nameA), 0, sizeof(v1_interest_nameA)); + + CCNxWireFormatMessage *message = ccnxWireFormatMessage_Create(wireFormat); + assertNotNull(message, "Got null CCNxWireFormatMessage, after attempting to create with buffer"); + + ccnxWireFormatMessage_Release(&message); + parcBuffer_Release(&wireFormat); +} + +/*** + *** allocator(), deallocator(), createNetworkBufferIoVec() taken from test_ccnx_WireFormatFacadeV1.c + ***/ + +/* + * Small size allocator for creating a network buffer + */ +static size_t +_allocator(void *userarg, size_t bytes, void **output) +{ + const size_t allocationSize = *(size_t *) userarg; + void *allocated = parcMemory_Allocate(allocationSize); + *output = allocated; + return allocationSize; +} + +/* + * deallocator for network buffer + */ +static void +_deallocator(void *userarg, void **memoryPtr) +{ + parcMemory_Deallocate(memoryPtr); +} +static const CCNxCodecNetworkBufferMemoryBlockFunctions memory = { .allocator = _allocator, .deallocator = _deallocator }; + +/* + * Create a network buffer that looks like this. The actual number of iovecs might + * be a little different, but the digest area will span several iovec. + * + * +-----------+-----------+-----------+-----------+-----------+ + * iov[0] iov[1] iov[2] iov[3] + * +-----------+-----------+-----------+-----------+-----------+ + * ^ ^ + * | | + * start end + */ +static CCNxCodecNetworkBufferIoVec * +_createNetworkBufferIoVec(size_t allocationSize, size_t padlen, uint8_t pad[padlen], size_t datalen, uint8_t data[datalen]) +{ + CCNxCodecNetworkBuffer *netbuff = ccnxCodecNetworkBuffer_Create(&memory, &allocationSize); + // build the network buffer + ccnxCodecNetworkBuffer_PutArray(netbuff, padlen, pad); + ccnxCodecNetworkBuffer_PutArray(netbuff, datalen, data); + ccnxCodecNetworkBuffer_PutArray(netbuff, padlen, pad); + + CCNxCodecNetworkBufferIoVec *vec = ccnxCodecNetworkBuffer_CreateIoVec(netbuff); + ccnxCodecNetworkBuffer_Release(&netbuff); + return vec; +} + +LONGBOW_TEST_CASE(Global, ccnxWireFormatMessage_FromInterestPacketTypeIoVec) +{ + uint8_t data[64]; + + uint8_t pad[32]; + CCNxCodecNetworkBufferIoVec *vec = _createNetworkBufferIoVec(512, 32, pad, 64, data); + + CCNxWireFormatMessage *message = ccnxWireFormatMessage_FromInterestPacketTypeIoVec(CCNxTlvDictionary_SchemaVersion_V1, vec); + + assertNotNull(message, "Got null CCNxWireFormatMessage"); + assertTrue(ccnxTlvDictionary_IsInterest(message), "Wrong message type"); + assertTrue(ccnxTlvDictionary_GetSchemaVersion(message) == CCNxTlvDictionary_SchemaVersion_V1, + "Wrong schema, got %d expected %d", + ccnxTlvDictionary_GetSchemaVersion(message), CCNxTlvDictionary_SchemaVersion_V1); + + ccnxWireFormatMessage_Release(&message); + ccnxCodecNetworkBufferIoVec_Release(&vec); +} + +LONGBOW_TEST_CASE(Global, ccnxWireFormatMessage_GetDictionary) +{ + PARCBuffer *buffer = parcBuffer_Allocate(10); + CCNxWireFormatMessage *message = ccnxWireFormatMessage_FromInterestPacketType(CCNxTlvDictionary_SchemaVersion_V1, buffer); + + CCNxTlvDictionary *dictionary = ccnxWireFormatMessage_GetDictionary(message); + assertNotNull(dictionary, "Expected to retrieve the dictionary from the CCNxWireFormatMessage"); + assertTrue(ccnxTlvDictionary_IsInterest(dictionary), "Wrong message type"); + + ccnxWireFormatMessage_Release(&message); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, ccnxWireFormatMessage_PutGetIoVec) +{ + uint8_t *data = parcMemory_Allocate(64); + memset(data, 0, 64); + + PARCBuffer *buffer = parcBuffer_Allocate(1); + CCNxCodecNetworkBuffer *netbuff = ccnxCodecNetworkBuffer_CreateFromArray(&ParcMemoryMemoryBlock, NULL, 64, data); + CCNxCodecNetworkBufferIoVec *iovec = ccnxCodecNetworkBuffer_CreateIoVec(netbuff); + + CCNxTlvDictionary *packet = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + ccnxWireFormatMessage_PutIoVec((CCNxWireFormatMessage *) packet, iovec); + + CCNxCodecNetworkBufferIoVec *test = ccnxWireFormatMessage_GetIoVec((CCNxWireFormatMessage *) packet); + assertTrue(test == iovec, "Failed to get iovec from dictionary, expected %p got %p", (void *) iovec, (void *) test); + + ccnxTlvDictionary_Release(&packet); + parcBuffer_Release(&buffer); + ccnxCodecNetworkBufferIoVec_Release(&iovec); + ccnxCodecNetworkBuffer_Release(&netbuff); +} + +LONGBOW_TEST_CASE(Global, ccnxWireFormatMessage_GetWireFormatBuffer) +{ + PARCBuffer *buffer = parcBuffer_Allocate(1); + CCNxWireFormatMessage *message = ccnxWireFormatMessage_FromInterestPacketType(CCNxTlvDictionary_SchemaVersion_V1, buffer); + + PARCBuffer *test = ccnxWireFormatMessage_GetWireFormatBuffer(message); + assertTrue(test == buffer, "Retrieved unexpected buffer: got %p expected %p", (void *) test, (void *) buffer); + + ccnxWireFormatMessage_Release(&message); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, ccnxWireFormatMessage_PutWireFormatBuffer) +{ + PARCBuffer *buffer = parcBuffer_Allocate(1); + CCNxTlvDictionary *packet = ccnxTlvDictionary_Create(20, 20); + ccnxTlvDictionary_SetMessageType_Interest(packet, CCNxTlvDictionary_SchemaVersion_V1); + bool success = ccnxWireFormatMessage_PutWireFormatBuffer(packet, buffer); + + assertTrue(success, "Failed to put buffer in to dictionary"); + + PARCBuffer *test = ccnxWireFormatMessage_GetWireFormatBuffer(packet); + assertTrue(test == buffer, "Retrieved unexpected buffer: got %p expected %p", (void *) test, (void *) buffer); + + ccnxTlvDictionary_Release(&packet); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, ccnxWireFormatMessage_HashProtectedRegion) +{ + // >1234< + const char string[] = "Hello dev null\n"; + + PARCBuffer *buffer = parcBuffer_Wrap((void *) string, sizeof(string), 0, sizeof(string)); + size_t start = 5; + size_t length = 4; + + CCNxWireFormatMessage *message = ccnxWireFormatMessage_FromContentObjectPacketType(CCNxTlvDictionary_SchemaVersion_V1, buffer); + + ccnxWireFormatMessage_SetProtectedRegionStart(message, start); + ccnxWireFormatMessage_SetProtectedRegionLength(message, length); + + PARCCryptoHasher *hasher = parcCryptoHasher_Create(PARCCryptoHashType_SHA256); + PARCCryptoHash *hash = ccnxWireFormatMessage_HashProtectedRegion(message, hasher); + + // the correctness of the has is tested in _ccnxWireFormatFacadeV1_ComputeHash + assertNotNull(hash, "Got null hash from a good packet"); + + ccnxWireFormatMessage_Release(&message); + parcCryptoHash_Release(&hash); + parcBuffer_Release(&buffer); + parcCryptoHasher_Release(&hasher); +} + +LONGBOW_TEST_CASE(Global, ccnxWireFormatMessage_SetProtectedRegionLength) +{ + const char string[] = "Hello dev null\n"; + PARCBuffer *buffer = parcBuffer_Wrap((void *) string, sizeof(string), 0, sizeof(string)); + + CCNxWireFormatMessage *message = ccnxWireFormatMessage_FromContentObjectPacketType(CCNxTlvDictionary_SchemaVersion_V1, buffer); + + size_t length = 5; + bool success = ccnxWireFormatMessage_SetProtectedRegionLength(message, length); + assertTrue(success, "Failed to put integer in to dictionary"); + + assertTrue(ccnxTlvDictionary_IsValueInteger(message, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_ProtectedLength), "ProtectedLength not set"); + + ccnxWireFormatMessage_Release(&message); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, ccnxWireFormatMessage_SetProtectedRegionStart) +{ + const char string[] = "Hello dev null\n"; + PARCBuffer *buffer = parcBuffer_Wrap((void *) string, sizeof(string), 0, sizeof(string)); + + CCNxWireFormatMessage *message = ccnxWireFormatMessage_FromContentObjectPacketType(CCNxTlvDictionary_SchemaVersion_V1, buffer); + + size_t start = 5; + bool success = ccnxWireFormatMessage_SetProtectedRegionStart(message, start); + assertTrue(success, "Failed to put integer in to dictionary"); + + assertTrue(ccnxTlvDictionary_IsValueInteger(message, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_ProtectedStart), "ProtectedStart not set"); + + ccnxWireFormatMessage_Release(&message); + parcBuffer_Release(&buffer); +} + +static PARCBuffer * +_iovecToParcBuffer(const CCNxCodecNetworkBufferIoVec *iovec) +{ + PARCBuffer *result = NULL; + + size_t iovcnt = ccnxCodecNetworkBufferIoVec_GetCount((CCNxCodecNetworkBufferIoVec *) iovec); + const struct iovec *array = ccnxCodecNetworkBufferIoVec_GetArray((CCNxCodecNetworkBufferIoVec *) iovec); + + size_t totalbytes = 0; + for (int i = 0; i < iovcnt; i++) { + totalbytes += array[i].iov_len; + } + + result = parcBuffer_Allocate(totalbytes); + for (int i = 0; i < iovcnt; i++) { + parcBuffer_PutArray(result, array[i].iov_len, array[i].iov_base); + } + + parcBuffer_Flip(result); + + + return result; +} + +LONGBOW_TEST_CASE(Global, ccnxWireFormatMessage_CreateContentObjectHash) +{ + // >1234< + const char string[] = "Hello dev null\n"; + + PARCBuffer *buffer = parcBuffer_Wrap((void *) string, sizeof(string), 0, sizeof(string)); + + CCNxWireFormatMessage *message = ccnxWireFormatMessage_FromContentObjectPacketType(CCNxTlvDictionary_SchemaVersion_V1, buffer); + + PARCCryptoHash *hash = ccnxWireFormatMessage_CreateContentObjectHash(message); + ccnxWireFormatMessage_Release(&message); + assertNull(hash, "Expect NULL for hash as it hasn't been encoded yet"); + + // We need to create a content object that is hashable + CCNxName *name = ccnxName_CreateFromCString("lci:/test/content"); + CCNxContentObject *contentObject = ccnxContentObject_CreateWithNameAndPayload(name, buffer); + ccnxName_Release(&name); + + // This next stuff is to force an encode/decode to setup hash extents + CCNxCodecNetworkBufferIoVec *iovec = ccnxCodecTlvPacket_DictionaryEncode(contentObject, NULL); + ccnxContentObject_Release(&contentObject); + PARCBuffer *encodedMessage = _iovecToParcBuffer(iovec); + ccnxCodecNetworkBufferIoVec_Release(&iovec); + // Decode + message = ccnxWireFormatMessage_Create(encodedMessage); + CCNxTlvDictionary *dictionary = ccnxWireFormatMessage_GetDictionary(message); + bool success = ccnxCodecTlvPacket_BufferDecode(encodedMessage, dictionary); + assertTrue(success, "Failed to decode buffer"); + parcBuffer_Release(&encodedMessage); + + hash = ccnxWireFormatMessage_CreateContentObjectHash(message); + + // the correctness of the hash is tested in _ccnxWireFormatFacadeV1_ComputeHash + assertNotNull(hash, "Got null hash from a good packet"); + + ccnxWireFormatMessage_Release(&message); + parcCryptoHash_Release(&hash); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, ccnxWireFormatMessage_WriteToFile) +{ + const char string[] = "Hello dev null\n"; + PARCBuffer *buffer = parcBuffer_Wrap((void *) string, sizeof(string), 0, sizeof(string)); + CCNxWireFormatMessage *message = ccnxWireFormatMessage_FromInterestPacketType(CCNxTlvDictionary_SchemaVersion_V1, buffer); + + ccnxWireFormatMessage_WriteToFile(message, "/dev/null"); + + ccnxWireFormatMessage_Release(&message); + parcBuffer_Release(&buffer); +} + +LONGBOW_TEST_CASE(Global, ccnxWireFormatMessage_SetHopLimit) +{ + uint8_t *data = parcMemory_Allocate(64); + memset(data, 0, 64); + + PARCBuffer *buffer = parcBuffer_Allocate(1); + CCNxCodecNetworkBuffer *netbuff = ccnxCodecNetworkBuffer_CreateFromArray(&ParcMemoryMemoryBlock, NULL, 64, data); + CCNxCodecNetworkBufferIoVec *iovec = ccnxCodecNetworkBuffer_CreateIoVec(netbuff); + + CCNxTlvDictionary *packet = ccnxCodecSchemaV1TlvDictionary_CreateInterest(); + ccnxWireFormatMessage_PutIoVec((CCNxWireFormatMessage *) packet, iovec); + + ccnxWireFormatMessage_SetHopLimit(packet, 10); + + CCNxCodecNetworkBufferIoVec *test = ccnxWireFormatMessage_GetIoVec((CCNxWireFormatMessage *) packet); + assertTrue(test == iovec, "Failed to get iovec from dictionary, expected %p got %p", (void *) iovec, (void *) test); + + ccnxTlvDictionary_Release(&packet); + parcBuffer_Release(&buffer); + ccnxCodecNetworkBufferIoVec_Release(&iovec); + ccnxCodecNetworkBuffer_Release(&netbuff); +} +LONGBOW_TEST_FIXTURE(Static) +{ + LONGBOW_RUN_TEST_CASE(Static, _getImplForSchema); + LONGBOW_RUN_TEST_CASE(Static, _ccnxWireFormatMessage_CreateWithImpl); +} + +LONGBOW_TEST_FIXTURE_SETUP(Static) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Static) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Static, _ccnxWireFormatMessage_CreateWithImpl) +{ + PARCBuffer *wireFormatV1 = parcBuffer_Wrap(v1_interest_nameA, sizeof(v1_interest_nameA), 0, sizeof(v1_interest_nameA)); + + CCNxWireFormatMessage *message = _ccnxWireFormatMessage_CreateWithImpl(&CCNxWireFormatFacadeV1_Implementation, wireFormatV1); + assertNotNull(message, "Expected to create a V1 CCNxWireFormatMessage"); + + ccnxWireFormatMessage_Release(&message); + parcBuffer_Release(&wireFormatV1); +} + +LONGBOW_TEST_CASE(Static, _getImplForSchema) +{ + CCNxWireFormatMessageInterface *impl = _getImplForSchema(CCNxTlvDictionary_SchemaVersion_V1); + assertTrue(impl = &CCNxWireFormatFacadeV1_Implementation, "Expected to see CCNxWireFormatFacadeV1_Implementation"); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(ccnx_WireFormatMessage); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-common/ccnx/common/validation/ccnxValidation_CRC32C.c b/libccnx-common/ccnx/common/validation/ccnxValidation_CRC32C.c new file mode 100644 index 00000000..f8ff7ba4 --- /dev/null +++ b/libccnx-common/ccnx/common/validation/ccnxValidation_CRC32C.c @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * See SCTP for a discussion of CRC32C http://tools.ietf.org/html/rfc4960#appendix-B + * It is also used by iSCSI and other protocols. + * + * CRC-32C uses an initial value of 0xFFFFFFFF and a final XOR value of 0xFFFFFFFF. + * + */ +#include <config.h> +#include <stdio.h> +#include <LongBow/runtime.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/security/parc_CryptoHasher.h> + +#include <ccnx/common/internal/ccnx_ValidationFacadeV1.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_TlvDictionary.h> + +#include <fcntl.h> +#include <errno.h> + +typedef struct crc32_signer { + PARCCryptoHasher *hasher; +} _CRC32Signer; + +typedef struct crc32_verifier { + PARCCryptoHasher *hasher; +} _CRC32Verifier; + +bool +ccnxValidationCRC32C_Set(CCNxTlvDictionary *message) +{ + bool success = true; + switch (ccnxTlvDictionary_GetSchemaVersion(message)) { + case CCNxTlvDictionary_SchemaVersion_V1: { + success &= ccnxTlvDictionary_PutInteger(message, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_CRYPTO_SUITE, PARCCryptoSuite_NULL_CRC32C); + + break; + } + + default: + trapIllegalValue(message, "Unknown schema version: %d", ccnxTlvDictionary_GetSchemaVersion(message)); + } + return success; +} + +bool +ccnxValidationCRC32C_Test(const CCNxTlvDictionary *message) +{ + switch (ccnxTlvDictionary_GetSchemaVersion(message)) { + case CCNxTlvDictionary_SchemaVersion_V1: { + if (ccnxTlvDictionary_IsValueInteger(message, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_CRYPTO_SUITE)) { + uint64_t cryptosuite = ccnxTlvDictionary_GetInteger(message, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_CRYPTO_SUITE); + return (cryptosuite == PARCCryptoSuite_NULL_CRC32C); + } + return false; + } + + default: + trapIllegalValue(message, "Unknown schema version: %d", ccnxTlvDictionary_GetSchemaVersion(message)); + } + return false; +} + +static bool +_crc32cSigner_Destructor(_CRC32Signer **interfaceContextPtr) +{ + _CRC32Signer *signer = *interfaceContextPtr; + parcCryptoHasher_Release(&signer->hasher); + return true; +} + +parcObject_ImplementAcquire(_crc32CSigner, _CRC32Signer); +parcObject_ImplementRelease(_crc32CSigner, _CRC32Signer); + +parcObject_Override(_CRC32Signer, PARCObject, + .destructor = (PARCObjectDestructor *) _crc32cSigner_Destructor); + +static bool +_crc32Verifier_Destructor(_CRC32Verifier **verifierPtr) +{ + _CRC32Verifier *verifier = (_CRC32Verifier *) *verifierPtr; + + parcCryptoHasher_Release(&(verifier->hasher)); + return true; +} + +parcObject_ImplementAcquire(_crc32Verifier, _CRC32Verifier); +parcObject_ImplementRelease(_crc32Verifier, _CRC32Verifier); + +parcObject_Override(_CRC32Verifier, PARCObject, + .destructor = (PARCObjectDestructor *) _crc32Verifier_Destructor); + +static PARCSignature * +_crc32Signer_SignDigest(_CRC32Signer *interfaceContext, const PARCCryptoHash *cryptoHash) +{ + PARCSignature *signature = + parcSignature_Create(PARCSigningAlgortihm_NULL, PARCCryptoHashType_CRC32C, parcCryptoHash_GetDigest(cryptoHash)); + return signature; +} + +static PARCSigningAlgorithm +_crc32Signer_GetSigningAlgorithm(_CRC32Signer *interfaceContext) +{ + return PARCSigningAlgortihm_NULL; +} + +static PARCCryptoHashType +_crc32Signer_GetCryptoHashType(_CRC32Signer *interfaceContext) +{ + return PARCCryptoHashType_CRC32C; +} + +static PARCCryptoHasher * +_crc32Signer_GetCryptoHasher(_CRC32Signer *signer) +{ + return signer->hasher; +} + +static PARCCryptoHasher * +_crc32Verifier_GetCryptoHasher(_CRC32Verifier *verifier, PARCKeyId *keyid, PARCCryptoHashType hashType) +{ + assertTrue(hashType == PARCCryptoHashType_CRC32C, "Only supports PARCCryptoHashType_CRC32C, got request for %s", parcCryptoHashType_ToString(hashType)); + + return verifier->hasher; +} + +static bool +_crc32Verifier_VerifyDigest(_CRC32Verifier *verifier, PARCKeyId *keyid, PARCCryptoHash *locallyComputedHash, + PARCCryptoSuite suite, PARCSignature *signatureToVerify) +{ + assertTrue(suite == PARCCryptoSuite_NULL_CRC32C, "Only supports PARC_SUITE_NULL_CRC32C, got request for %d", suite); + + PARCBuffer *calculatedCrc = parcCryptoHash_GetDigest(locallyComputedHash); + + // the signature is the CRC, so we just need to compare to the to calculated CRC32C "hash" + PARCBuffer *crcToVerify = parcSignature_GetSignature(signatureToVerify); + + return parcBuffer_Equals(calculatedCrc, crcToVerify); +} + +static bool +_crc32Verifier_AllowedCryptoSuite(_CRC32Verifier *verifier, PARCKeyId *keyid, PARCCryptoSuite suite) +{ + return (suite == PARCCryptoSuite_NULL_CRC32C); +} + +PARCSigningInterface *CRC32SignerAsPARCSigner = &(PARCSigningInterface) { + .GetCryptoHasher = (PARCCryptoHasher * (*)(void *))_crc32Signer_GetCryptoHasher, + .SignDigest = (PARCSignature * (*)(void *, const PARCCryptoHash *))_crc32Signer_SignDigest, + .GetSigningAlgorithm = (PARCSigningAlgorithm (*)(void *))_crc32Signer_GetSigningAlgorithm, + .GetCryptoHashType = (PARCCryptoHashType (*)(void *))_crc32Signer_GetCryptoHashType +}; + +PARCVerifierInterface *CRC32VerifierAsPARCVerifier = &(PARCVerifierInterface) { + .GetCryptoHasher = (PARCCryptoHasher * (*)(void *, PARCKeyId *, PARCCryptoHashType))_crc32Verifier_GetCryptoHasher, + .VerifyDigest = (bool (*)(void *, PARCKeyId *, PARCCryptoHash *, PARCCryptoSuite, PARCSignature *))_crc32Verifier_VerifyDigest, + .AddKey = NULL, + .RemoveKeyId = NULL, + .AllowedCryptoSuite = (bool (*)(void *, PARCKeyId *, PARCCryptoSuite))_crc32Verifier_AllowedCryptoSuite, +}; + +static PARCSigner * +_crc32Signer_Create(void) +{ + _CRC32Signer *crc32Signer = parcObject_CreateInstance(_CRC32Signer); + assertNotNull(crc32Signer, "parcObject_CreateInstance returned NULL"); + + crc32Signer->hasher = parcCryptoHasher_Create(PARCCryptoHashType_CRC32C); + PARCSigner *signer = parcSigner_Create(crc32Signer, CRC32SignerAsPARCSigner); + _crc32CSigner_Release(&crc32Signer); + + return signer; +} + +PARCSigner * +ccnxValidationCRC32C_CreateSigner(void) +{ + return _crc32Signer_Create(); +} + +static PARCVerifier * +_crc32Verifier_Create(void) +{ + _CRC32Verifier *crcVerifier = parcObject_CreateInstance(_CRC32Verifier); + assertNotNull(crcVerifier, "parcObject_CreateInstance returned NULL"); + + crcVerifier->hasher = parcCryptoHasher_Create(PARCCryptoHashType_CRC32C); + + PARCVerifier *verifier = parcVerifier_Create(crcVerifier, CRC32VerifierAsPARCVerifier); + _crc32Verifier_Release(&crcVerifier); + + return verifier; +} + +PARCVerifier * +ccnxValidationCRC32C_CreateVerifier(void) +{ + return _crc32Verifier_Create(); +} diff --git a/libccnx-common/ccnx/common/validation/ccnxValidation_CRC32C.h b/libccnx-common/ccnx/common/validation/ccnxValidation_CRC32C.h new file mode 100755 index 00000000..4d26015a --- /dev/null +++ b/libccnx-common/ccnx/common/validation/ccnxValidation_CRC32C.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file ccnxValidation_CRC32C.h + * @brief <#Brief Description#> + * + * <#Detailed Description#> + * + */ +#ifndef CCNx_Common_ccnxValidation_CRC32C_h +#define CCNx_Common_ccnxValidation_CRC32C_h + +#include <parc/security/parc_Signer.h> +#include <parc/security/parc_Verifier.h> +#include <ccnx/common/internal/ccnx_TlvDictionary.h> + +/** + * Sets the Validation algorithm to RSA-SHA256 + * + * Sets the validation algorithm to be RSA with a SHA-256 digest. Optionally includes + * a KeyId and KeyLocator with the message. + * + * @param [in] message The message dictionary + * + * @return `true` success + * @return `false` failure + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool ccnxValidationCRC32C_Set(CCNxTlvDictionary *message); + +/** + * Determines if the validation algorithm is RSA-SHA256 * + * @param [in] message The message to check + * + * @return `true` The validation algorithm in the dictionary is this one + * @return `false` The validaiton algorithm in the dictionary is something else or not present + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool ccnxValidationCRC32C_Test(const CCNxTlvDictionary *message); + +/** + * Creates a signer to compute a CRC32C + * + * @return non-null An allocated signer + * @return null An error + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCSigner *ccnxValidationCRC32C_CreateSigner(void); + +/** + * Creates a verifier to check a CRC32C "signature" + * + * @return non-null An allocated verifier + * @return null An error + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCVerifier *ccnxValidationCRC32C_CreateVerifier(void); +#endif // CCNx_Common_ccnxValidation_CRC32C_h diff --git a/libccnx-common/ccnx/common/validation/ccnxValidation_EcSecp256K1.c b/libccnx-common/ccnx/common/validation/ccnxValidation_EcSecp256K1.c new file mode 100755 index 00000000..d40a8378 --- /dev/null +++ b/libccnx-common/ccnx/common/validation/ccnxValidation_EcSecp256K1.c @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ +#include <config.h> +#include <stdio.h> +#include <LongBow/runtime.h> + +#include <ccnx/common/internal/ccnx_ValidationFacadeV1.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_TlvDictionary.h> + +// ======================================================================================== + +/** + * Sets the Validation algorithm to EC-SECP-256K1 + * + * Sets the validation algorithm to be Elliptical Curve with SECP-256K1 parameters. Optionally includes + * a KeyId and KeyLocator with the message. + * + * @param [in] message The message dictionary + * @param [in] keyid (Optional) The KEYID to include the the message + * @param [in] keyLocator (Optional) The KEY LOCATOR to include in the message + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool +ccnxValidationEcSecp256K1_Set(CCNxTlvDictionary *message, const PARCBuffer *keyid, const CCNxKeyLocator *keyLocator) +{ + bool success = true; + switch (ccnxTlvDictionary_GetSchemaVersion(message)) { + case CCNxTlvDictionary_SchemaVersion_V1: { + success &= ccnxTlvDictionary_PutInteger(message, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_CRYPTO_SUITE, PARCCryptoSuite_EC_SECP_256K1); + + if (keyid) { + success &= ccnxTlvDictionary_PutBuffer(message, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_KEYID, keyid); + } + + success &= ccnxValidationFacadeV1_SetKeyLocator(message, (CCNxKeyLocator *) keyLocator); // un-consting + + break; + } + + default: + trapIllegalValue(message, "Unknown schema version: %d", ccnxTlvDictionary_GetSchemaVersion(message)); + } + return success; +} + +bool +ccnxValidationEcSecp256K1_Test(const CCNxTlvDictionary *message) +{ + if (ccnxTlvDictionary_IsValueInteger(message, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_CRYPTO_SUITE)) { + uint64_t cryptosuite = ccnxTlvDictionary_GetInteger(message, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_CRYPTO_SUITE); + return (cryptosuite == PARCCryptoSuite_EC_SECP_256K1); + } + return false; +} diff --git a/libccnx-common/ccnx/common/validation/ccnxValidation_EcSecp256K1.h b/libccnx-common/ccnx/common/validation/ccnxValidation_EcSecp256K1.h new file mode 100755 index 00000000..3c949769 --- /dev/null +++ b/libccnx-common/ccnx/common/validation/ccnxValidation_EcSecp256K1.h @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file ccnxValidation_EcSecp256K1.h + * @brief <#Brief Description#> + * + * <#Detailed Description#> + * + */ +#ifndef CCNx_Common_ccnxValidation_EcSecp256K1_h +#define CCNx_Common_ccnxValidation_EcSecp256K1_h + +#include <stdbool.h> +#include <ccnx/common/internal/ccnx_TlvDictionary.h> +#include <ccnx/common/ccnx_KeyLocator.h> + +/** + * + * Sets the validation algorithm to be Elliptical Curve with SECP-256K1 parameters. + * Optionally includes a KeyId and KeyLocator with the message. + * + * @param [in] message The message dictionary + * @param [in] keyid (Optional) The KEYID to include the the message + * @param [in] keyLocator (Optional) The KEY LOCATOR to include in the message + * + * @return true success + * @return false failure + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool ccnxValidationEcSecp256K1_Set(CCNxTlvDictionary *message, const PARCBuffer *keyid, const CCNxKeyLocator *keyLocator); + +/** + * Determines if the validation algorithm is Elliptical Curve with SECP-256K1 parameters. + * + * <#Paragraphs Of Explanation#> + * + * @param [in] message The message to check + * + * @return true The validation algorithm in the dictionary is this one + * @return false The validaiton algorithm in the dictionary is something else or not present + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool ccnxValidationEcSecp256K1_Test(const CCNxTlvDictionary *message); + +/** + * Returns the KeyId associated with the validation algorithm + * + * <#Paragraphs Of Explanation#> + * + * @param [in] message The message to check + * + * @return non-NULL the keyid + * @return null An error or no keyid or no validation algorithm in the message + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCBuffer *ccnxValidationEcSecp256K1_GetKeyId(const CCNxTlvDictionary *message); + +/** + * Returns the KeyName associated with the validation algorithm + * + * This should return a LINK, see case 1018 + * + * @param [in] message The message to check + * + * @return non-NULL the KeyName + * @return null An error or no keyid or no validation algorithm in the message + * + * Example: + * @code + * <#example#> + * @endcode + */ +CCNxName *ccnxValidationEcSecp256K1_GetKeyLocatorName(const CCNxTlvDictionary *message); + +/** + * Returns the PublicKey associated with the validation algorithm + * + * @param [in] message The message to check + * + * @return non-NULL the PublicKey (DER encoded) + * @return null An error or no public key or no validation algorithm in the message + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCBuffer *ccnxValidationEcSecp256K1_GetKeyLocatorPublicKey(const CCNxTlvDictionary *message); + +/** + * Returns the Certificate associated with the validation algorithm + * + * @param [in] message The message to check + * + * @return non-NULL the Certificate (DER encoded) + * @return null An error or no certificate or no validation algorithm in the message + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCBuffer *ccnxValidationEcSecp256K1_GetKeyLocatorCertificate(const CCNxTlvDictionary *message); +#endif // CCNx_Common_ccnxValidation_EcSecp256K1_h diff --git a/libccnx-common/ccnx/common/validation/ccnxValidation_HmacSha256.c b/libccnx-common/ccnx/common/validation/ccnxValidation_HmacSha256.c new file mode 100644 index 00000000..5202508e --- /dev/null +++ b/libccnx-common/ccnx/common/validation/ccnxValidation_HmacSha256.c @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ +#include <config.h> +#include <stdio.h> +#include <LongBow/runtime.h> + +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_TlvDictionary.h> + +#include <parc/security/parc_Verifier.h> +#include <parc/security/parc_SymmetricKeyStore.h> +#include <parc/security/parc_SymmetricKeySigner.h> + +/** + * Sets the Validation algorithm to HMAC with SHA-256 hash + * + * Sets the validation algorithm to be HMAC with a SHA-256 digest. Optionally includes + * a KeyId with the message. + * + * @param [in] message The message dictionary + * @param [in] keyid (Optional) The KEYID to include the the message + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool +ccnxValidationHmacSha256_Set(CCNxTlvDictionary *message, const PARCBuffer *keyid) +{ + bool success = true; + switch (ccnxTlvDictionary_GetSchemaVersion(message)) { + case CCNxTlvDictionary_SchemaVersion_V1: { + success &= ccnxTlvDictionary_PutInteger(message, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_CRYPTO_SUITE, PARCCryptoSuite_HMAC_SHA256); + + if (keyid) { + success &= ccnxTlvDictionary_PutBuffer(message, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_KEYID, keyid); + } + + break; + } + + default: + trapIllegalValue(message, "Unknown schema version: %d", ccnxTlvDictionary_GetSchemaVersion(message)); + } + return success; +} + +bool +ccnxValidationHmacSha256_Test(const CCNxTlvDictionary *message) +{ + switch (ccnxTlvDictionary_GetSchemaVersion(message)) { + case CCNxTlvDictionary_SchemaVersion_V1: { + if (ccnxTlvDictionary_IsValueInteger(message, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_CRYPTO_SUITE)) { + uint64_t cryptosuite = ccnxTlvDictionary_GetInteger(message, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_CRYPTO_SUITE); + return (cryptosuite == PARCCryptoSuite_HMAC_SHA256); + } + return false; + } + + default: + trapIllegalValue(message, "Unknown schema version: %d", ccnxTlvDictionary_GetSchemaVersion(message)); + } + return false; +} + +PARCSigner * +ccnxValidationHmacSha256_CreateSigner(PARCBuffer *secretKey) +{ + PARCSymmetricKeyStore *keyStore = parcSymmetricKeyStore_Create(secretKey); + PARCSymmetricKeySigner *symmetricSigner = parcSymmetricKeySigner_Create(keyStore, PARCCryptoHashType_SHA256); + parcSymmetricKeyStore_Release(&keyStore); + + PARCSigner *signer = parcSigner_Create(symmetricSigner, PARCSymmetricKeySignerAsSigner); + parcSymmetricKeySigner_Release(&symmetricSigner); + + return signer; +} + +PARCVerifier * +ccnxValidationHmacSha256_CreateVerifier(PARCBuffer *secretKey) +{ + trapNotImplemented("not finished yet"); +} diff --git a/libccnx-common/ccnx/common/validation/ccnxValidation_HmacSha256.h b/libccnx-common/ccnx/common/validation/ccnxValidation_HmacSha256.h new file mode 100755 index 00000000..94f6af75 --- /dev/null +++ b/libccnx-common/ccnx/common/validation/ccnxValidation_HmacSha256.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file ccnxValidation_HmacSha256.h + * @brief <#Brief Description#> + * + * <#Detailed Description#> + * + */ +#ifndef CCNx_Common_ccnxValidation_HmacSha256_h +#define CCNx_Common_ccnxValidation_HmacSha256_h + +#include <parc/security/parc_Signer.h> +#include <parc/security/parc_Verifier.h> +#include <ccnx/common/internal/ccnx_TlvDictionary.h> + +/** + * Sets the Validation algorithm to HMAC-SHA256 + * + * Sets the validation algorithm to be HMAC with a SHA-256 digest. Optionally includes a KeyId + * + * @param [in] message The message dictionary + * @param [in] keyid (Optional) The KEYID to include the the message + * + * @return true success + * @return false failure + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool ccnxValidationHmacSha256_Set(CCNxTlvDictionary *message, const PARCBuffer *keyid); + +/** + * Determines if the validation algorithm is RSA-SHA256 + * + * <#Paragraphs Of Explanation#> + * + * @param [in] message The message to check + * + * @return true The validation algorithm in the dictionary is this one + * @return false The validaiton algorithm in the dictionary is something else or not present + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool ccnxValidationHmacSha256_Test(const CCNxTlvDictionary *message); + +/** + * Creates a signer using a specified secret key + * + * <#Paragraphs Of Explanation#> + * + * @param [in] secretKey The key to use as the authenticator + * + * @return non-null An allocated signer + * @return null An error + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCSigner *ccnxValidationHmacSha256_CreateSigner(PARCBuffer *secretKey); + +/** + * Creates a verifier to check a CRC32C "signature" + * + * Once the Verifier is created, you can add more keys using + * parcVerifier_AddKey(). If you provide a secretKey in the call, it will + * be added to the verifier automatically. + * + * @param [in] secretKey (Optional) The key to use as the authenticator, or NULL. + * + * @return non-null An allocated verifier + * @return null An error + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCVerifier *ccnxValidationHmacSha256_CreateVerifier(PARCBuffer *secretKey); +#endif // CCNx_Common_ccnxValidation_HmacSha256_h diff --git a/libccnx-common/ccnx/common/validation/ccnxValidation_RsaSha256.c b/libccnx-common/ccnx/common/validation/ccnxValidation_RsaSha256.c new file mode 100644 index 00000000..39590a9d --- /dev/null +++ b/libccnx-common/ccnx/common/validation/ccnxValidation_RsaSha256.c @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ +#include <config.h> +#include <stdio.h> +#include <LongBow/runtime.h> + +#include <ccnx/common/internal/ccnx_ValidationFacadeV1.h> + +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_TlvDictionary.h> + +// ======================================================================================== + +bool +ccnxValidationRsaSha256_Set(CCNxTlvDictionary *message, const PARCBuffer *keyid, const CCNxKeyLocator *keyLocator) +{ + bool success = true; + success &= ccnxTlvDictionary_PutInteger(message, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_CRYPTO_SUITE, PARCCryptoSuite_RSA_SHA256); + + if (keyid) { + success &= ccnxTlvDictionary_PutBuffer(message, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_KEYID, keyid); + } + + success &= ccnxValidationFacadeV1_SetKeyLocator(message, (CCNxKeyLocator *) keyLocator); // un-consting + + return success; +} + +bool +ccnxValidationRsaSha256_Test(const CCNxTlvDictionary *message) +{ + if (ccnxTlvDictionary_IsValueInteger(message, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_CRYPTO_SUITE)) { + uint64_t cryptosuite = ccnxTlvDictionary_GetInteger(message, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_CRYPTO_SUITE); + return (cryptosuite == PARCCryptoSuite_RSA_SHA256); + } + return false; +} diff --git a/libccnx-common/ccnx/common/validation/ccnxValidation_RsaSha256.h b/libccnx-common/ccnx/common/validation/ccnxValidation_RsaSha256.h new file mode 100755 index 00000000..f1835dca --- /dev/null +++ b/libccnx-common/ccnx/common/validation/ccnxValidation_RsaSha256.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file ccnxValidation_RsaSha256.h + * @brief <#Brief Description#> + * + * The RsaSha256 validation algorithm uses standard locations for KeyId, PublicKey, Certificate, and KeyName, + * so you should use ccnxValidationFacade getters to retrieve them. + * + */ +#ifndef CCNx_Common_ccnxValidation_RsaSha256_h +#define CCNx_Common_ccnxValidation_RsaSha256_h + +#include <stdbool.h> +#include <parc/algol/parc_Buffer.h> +#include <ccnx/common/ccnx_KeyLocator.h> +#include <ccnx/common/internal/ccnx_TlvDictionary.h> + +/** + * Sets the Validation algorithm to RSA-SHA256 + * + * Sets the validation algorithm to be RSA with a SHA-256 digest. Optionally includes + * a KeyId and KeyLocator with the message. + * + * @param [in] message The message dictionary + * @param [in] keyid (Optional) The KEYID to include the the message + * @param [in] keyLocator (Optional) The KEY LOCATOR to include in the message + * + * @return true success + * @return false failure + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool ccnxValidationRsaSha256_Set(CCNxTlvDictionary *message, const PARCBuffer *keyid, const CCNxKeyLocator *keyLocator); + +/** + * Determines if the validation algorithm is RSA-SHA256 + * + * <#Paragraphs Of Explanation#> + * + * @param [in] message The message to check + * + * @return true The validation algorithm in the dictionary is this one + * @return false The validaiton algorithm in the dictionary is something else or not present + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool ccnxValidationRsaSha256_Test(const CCNxTlvDictionary *message); +#endif // CCNx_Common_ccnxValidation_RsaSha256_h diff --git a/libccnx-common/ccnx/common/validation/test/.gitignore b/libccnx-common/ccnx/common/validation/test/.gitignore new file mode 100644 index 00000000..d5f2004f --- /dev/null +++ b/libccnx-common/ccnx/common/validation/test/.gitignore @@ -0,0 +1,4 @@ +test_ccnxValidation_CRC32C +test_ccnxValidation_EcSecp256K1 +test_ccnxValidation_HmacSha256 +test_ccnxValidation_RsaSha256 diff --git a/libccnx-common/ccnx/common/validation/test/CMakeLists.txt b/libccnx-common/ccnx/common/validation/test/CMakeLists.txt new file mode 100644 index 00000000..90f678f7 --- /dev/null +++ b/libccnx-common/ccnx/common/validation/test/CMakeLists.txt @@ -0,0 +1,16 @@ +# Enable gcov output for the tests +add_definitions(--coverage) +set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} " --coverage") + +set(TestsExpectedToPass + test_ccnxValidation_CRC32C + test_ccnxValidation_EcSecp256K1 + test_ccnxValidation_HmacSha256 + test_ccnxValidation_RsaSha256 +) + + +foreach(test ${TestsExpectedToPass}) + AddTest(${test}) +endforeach() + diff --git a/libccnx-common/ccnx/common/validation/test/test_ccnxValidation_CRC32C.c b/libccnx-common/ccnx/common/validation/test/test_ccnxValidation_CRC32C.c new file mode 100755 index 00000000..90eae917 --- /dev/null +++ b/libccnx-common/ccnx/common/validation/test/test_ccnxValidation_CRC32C.c @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../ccnxValidation_CRC32C.c" +#include <parc/algol/parc_SafeMemory.h> + +#include <LongBow/unit-test.h> +#include "testrig_validation.c" + +#include <sys/time.h> + +/* + * Ground truth set derived from CRC RevEng http://reveng.sourceforge.net + * e.g. reveng -c -m CRC-32C 313233343536373839 gives the canonical check value 0xe306928e + * + * You can also calcaulate them online at http://www.zorc.breitbandkatze.de/crc.html using + * CRC polynomial 0x1EDC6F41, init 0xFFFFFFFF, final 0xFFFFFFFF, reverse data bytes (check), + * and reverse CRC result before final XOR (check). + * + */ +struct test_vector { + uint32_t crc32c; + int length; + uint8_t *buffer; +} vectors[] = { + { .crc32c = 0xe3069283, .length = 9, .buffer = (uint8_t []) { '1', '2', '3', '4', '5', '6', '7', '8', '9' } }, + { .crc32c = 0xddb65633, .length = 1, .buffer = (uint8_t []) { 0x3D } }, + { .crc32c = 0xc203c1fd, .length = 2, .buffer = (uint8_t []) { 0x3D, 0x41 } }, + { .crc32c = 0x80a9d169, .length = 3, .buffer = (uint8_t []) { 'b', 'e', 'e' } }, + { .crc32c = 0xa099f534, .length = 4, .buffer = (uint8_t []) { 'h', 'e', 'l', 'l' } }, + { .crc32c = 0x9a71bb4c, .length = 5, .buffer = (uint8_t []) { 'h', 'e', 'l', 'l', 'o' } }, + { .crc32c = 0x2976E503, .length = 6, .buffer = (uint8_t []) { 'g', 'r', 'u', 'm', 'p', 'y' } }, + { .crc32c = 0xe627f441, .length = 7, .buffer = (uint8_t []) { 'a', 'b', 'c', 'd', 'e', 'f', 'g' } }, + { .crc32c = 0x2d265c1d, .length = 13, .buffer = (uint8_t []) { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'a', 'b', 'c', 'd', 'e', 'f'} }, + { .crc32c = 0, .length = 0, .buffer = NULL } +}; + +LONGBOW_TEST_RUNNER(ccnxValidation_CRC32C) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(ccnxValidation_CRC32C) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(ccnxValidation_CRC32C) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// =========================================================== + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, ccnxValidationCRC32C_Set); + LONGBOW_RUN_TEST_CASE(Global, ccnxValidationCRC32C_CreateSigner); + LONGBOW_RUN_TEST_CASE(Global, ccnxValidationCRC32C_CreateVerifier); + LONGBOW_RUN_TEST_CASE(Global, ccnxValidationCRC32C_DictionaryCryptoSuiteValue); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + longBowTestCase_SetClipBoardData(testCase, commonSetup()); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + commonTeardown(longBowTestCase_GetClipBoardData(testCase)); + + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, ccnxValidationCRC32C_Set) +{ + // do not test on V0 packets, no support + TestData *data = longBowTestCase_GetClipBoardData(testCase); + testValidationSet_NoParam(data, ccnxValidationCRC32C_Set, ccnxValidationCRC32C_Test, false, true); +} + +LONGBOW_TEST_CASE(Global, ccnxValidationCRC32C_CreateSigner) +{ + PARCSigner *signer = ccnxValidationCRC32C_CreateSigner(); + assertNotNull(signer, "Got null signer"); + + // now run all the test vectors through it + + for (int i = 0; vectors[i].buffer != NULL; i++) { + PARCCryptoHasher *hasher = parcSigner_GetCryptoHasher(signer); + + parcCryptoHasher_Init(hasher); + parcCryptoHasher_UpdateBytes(hasher, vectors[i].buffer, vectors[i].length); + PARCCryptoHash *hash = parcCryptoHasher_Finalize(hasher); + + PARCSignature *sig = parcSigner_SignDigest(signer, hash); + PARCBuffer *sigbits = parcSignature_GetSignature(sig); + uint32_t testCrc = parcBuffer_GetUint32(sigbits); + assertTrue(testCrc == vectors[i].crc32c, + "CRC32C values wrong, index %d got 0x%08x expected 0x%08x\n", + i, testCrc, vectors[i].crc32c); + + parcSignature_Release(&sig); + parcCryptoHash_Release(&hash); + } + + parcSigner_Release(&signer); +} + +LONGBOW_TEST_CASE(Global, ccnxValidationCRC32C_CreateVerifier) +{ + PARCSigner *signer = ccnxValidationCRC32C_CreateSigner(); + assertNotNull(signer, "Got null signer"); + + PARCVerifier *verifier = ccnxValidationCRC32C_CreateVerifier(); + assertNotNull(verifier, "Got null verifier"); + + for (int i = 0; vectors[i].buffer != NULL; i++) { + // Produce the signature + PARCSignature *sig = NULL; + { + PARCCryptoHasher *signingHasher = parcSigner_GetCryptoHasher(signer); + parcCryptoHasher_Init(signingHasher); + parcCryptoHasher_UpdateBytes(signingHasher, vectors[i].buffer, vectors[i].length); + PARCCryptoHash *signingHash = parcCryptoHasher_Finalize(signingHasher); + sig = parcSigner_SignDigest(signer, signingHash); + parcCryptoHash_Release(&signingHash); + } + + // Now do the verification stage + PARCCryptoHash *verifierHash = NULL; + { + PARCCryptoHasher *verifyHasher = parcVerifier_GetCryptoHasher(verifier, NULL, PARCCryptoHashType_CRC32C); + parcCryptoHasher_Init(verifyHasher); + parcCryptoHasher_UpdateBytes(verifyHasher, vectors[i].buffer, vectors[i].length); + verifierHash = parcCryptoHasher_Finalize(verifyHasher); + } + + bool success = parcVerifier_VerifyDigestSignature(verifier, NULL, verifierHash, PARCCryptoSuite_NULL_CRC32C, sig); + + assertTrue(success, + "Failed to verify signature, index %d expected 0x%08x\n", + i, vectors[i].crc32c); + + parcSignature_Release(&sig); + parcCryptoHash_Release(&verifierHash); + } + parcSigner_Release(&signer); + parcVerifier_Release(&verifier); +} + +LONGBOW_TEST_CASE(Global, ccnxValidationCRC32C_DictionaryCryptoSuiteValue) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + CCNxTlvDictionary *dictionary = ccnxContentObject_CreateWithImplAndPayload(&CCNxContentObjectFacadeV1_Implementation, + data->keyname, + CCNxPayloadType_DATA, + NULL); + ccnxValidationCRC32C_Set(dictionary); + uint64_t cryptosuite = ccnxTlvDictionary_GetInteger(dictionary, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_CRYPTO_SUITE); + assertTrue(cryptosuite == PARCCryptoSuite_NULL_CRC32C, "Unexpected PARCCryptoSuite value in dictionary"); + + ccnxTlvDictionary_Release(&dictionary); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(ccnxValidation_CRC32C); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-common/ccnx/common/validation/test/test_ccnxValidation_EcSecp256K1.c b/libccnx-common/ccnx/common/validation/test/test_ccnxValidation_EcSecp256K1.c new file mode 100755 index 00000000..8fe00f35 --- /dev/null +++ b/libccnx-common/ccnx/common/validation/test/test_ccnxValidation_EcSecp256K1.c @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../ccnxValidation_EcSecp256K1.c" +#include <parc/algol/parc_SafeMemory.h> + +#include <LongBow/unit-test.h> +#include "testrig_validation.c" + +LONGBOW_TEST_RUNNER(ccnxValidation_EcSecp256K1) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(ccnxValidation_EcSecp256K1) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(ccnxValidation_EcSecp256K1) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, ccnxValidationEcSecp256K1_Set); + LONGBOW_RUN_TEST_CASE(Global, ccnxValidationEcSecp256K1_DictionaryCryptoSuiteValue); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + longBowTestCase_SetClipBoardData(testCase, commonSetup()); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + commonTeardown(longBowTestCase_GetClipBoardData(testCase)); + + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, ccnxValidationEcSecp256K1_Set) +{ + // Do not run over V0 packets, no support + TestData *data = longBowTestCase_GetClipBoardData(testCase); + testValidationSet_KeyId_KeyLocator(data, ccnxValidationEcSecp256K1_Set, ccnxValidationEcSecp256K1_Test, false, true); +} + +LONGBOW_TEST_CASE(Global, ccnxValidationEcSecp256K1_DictionaryCryptoSuiteValue) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + CCNxTlvDictionary *dictionary = ccnxContentObject_CreateWithImplAndPayload(&CCNxContentObjectFacadeV1_Implementation, + data->keyname, + CCNxPayloadType_DATA, + NULL); + ccnxValidationEcSecp256K1_Set(dictionary, data->keyid, NULL); + uint64_t cryptosuite = ccnxTlvDictionary_GetInteger(dictionary, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_CRYPTO_SUITE); + assertTrue(cryptosuite == PARCCryptoSuite_EC_SECP_256K1, "Unexpected PARCCryptoSuite value in dictionary"); + + ccnxTlvDictionary_Release(&dictionary); +} + +LONGBOW_TEST_FIXTURE(Local) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(ccnxValidation_EcSecp256K1); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-common/ccnx/common/validation/test/test_ccnxValidation_HmacSha256.c b/libccnx-common/ccnx/common/validation/test/test_ccnxValidation_HmacSha256.c new file mode 100755 index 00000000..4d095567 --- /dev/null +++ b/libccnx-common/ccnx/common/validation/test/test_ccnxValidation_HmacSha256.c @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../ccnxValidation_HmacSha256.c" +#include <parc/algol/parc_SafeMemory.h> + +#include <parc/algol/parc_Object.h> + +#include <LongBow/unit-test.h> +#include "testrig_validation.c" + +LONGBOW_TEST_RUNNER(ccnxValidation_HmacSha256) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(ccnxValidation_HmacSha256) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(ccnxValidation_HmacSha256) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, ccnxValidationHmacSha256_Set); + LONGBOW_RUN_TEST_CASE(Global, ccnxValidationHmacSha256_CreateSigner); + LONGBOW_RUN_TEST_CASE(Global, ccnxValidationHmacSha256_DictionaryCryptoSuiteValue); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + longBowTestCase_SetClipBoardData(testCase, commonSetup()); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + commonTeardown(longBowTestCase_GetClipBoardData(testCase)); + + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, ccnxValidationHmacSha256_Set) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + testValidationSet_KeyId(data, ccnxValidationHmacSha256_Set, ccnxValidationHmacSha256_Test, true, true); +} + +LONGBOW_TEST_CASE(Global, ccnxValidationHmacSha256_CreateSigner) +{ + char secretKeyString[] = "0123456789ABCDEF0123456789ABCDEF"; + PARCBuffer *secretKey = bufferFromString(strlen(secretKeyString), secretKeyString); + + PARCSigner *signer = ccnxValidationHmacSha256_CreateSigner(secretKey); + assertNotNull(signer, "Got null signer"); + + parcSigner_Release(&signer); + parcBuffer_Release(&secretKey); +} + +LONGBOW_TEST_CASE(Global, ccnxValidationHmacSha256_DictionaryCryptoSuiteValue) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + CCNxTlvDictionary *dictionary = ccnxContentObject_CreateWithImplAndPayload(&CCNxContentObjectFacadeV1_Implementation, + data->keyname, + CCNxPayloadType_DATA, + NULL); + ccnxValidationHmacSha256_Set(dictionary, data->keyid); + uint64_t cryptosuite = ccnxTlvDictionary_GetInteger(dictionary, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_CRYPTO_SUITE); + assertTrue(cryptosuite == PARCCryptoSuite_HMAC_SHA256, "Unexpected PARCCryptoSuite value in dictionary"); + + ccnxTlvDictionary_Release(&dictionary); +} + +LONGBOW_TEST_FIXTURE(Local) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(ccnxValidation_HmacSha256); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-common/ccnx/common/validation/test/test_ccnxValidation_RsaSha256.c b/libccnx-common/ccnx/common/validation/test/test_ccnxValidation_RsaSha256.c new file mode 100755 index 00000000..5119a262 --- /dev/null +++ b/libccnx-common/ccnx/common/validation/test/test_ccnxValidation_RsaSha256.c @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../ccnxValidation_RsaSha256.c" +#include <parc/algol/parc_SafeMemory.h> + +#include <LongBow/unit-test.h> +#include "testrig_validation.c" +#include <ccnx/common/validation/ccnxValidation_HmacSha256.h> + + +LONGBOW_TEST_RUNNER(ccnxValidation_RsaSha256) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(ccnxValidation_RsaSha256) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(ccnxValidation_RsaSha256) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, ccnxValidationRsaSha256_Set); + LONGBOW_RUN_TEST_CASE(Global, ccnxValidationRsaSha256_DictionaryCryptoSuiteValue); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + longBowTestCase_SetClipBoardData(testCase, commonSetup()); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + commonTeardown(longBowTestCase_GetClipBoardData(testCase)); + + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, ccnxValidationRsaSha256_Set) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + testValidationSet_KeyId_KeyLocator(data, ccnxValidationRsaSha256_Set, ccnxValidationRsaSha256_Test, true, true); +} + +LONGBOW_TEST_CASE(Global, ccnxValidationRsaSha256_DictionaryCryptoSuiteValue) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + CCNxTlvDictionary *dictionary = ccnxContentObject_CreateWithImplAndPayload(&CCNxContentObjectFacadeV1_Implementation, + data->keyname, + CCNxPayloadType_DATA, + NULL); + ccnxValidationRsaSha256_Set(dictionary, data->keyid, NULL); + + uint64_t cryptosuite = ccnxTlvDictionary_GetInteger(dictionary, CCNxCodecSchemaV1TlvDictionary_ValidationFastArray_CRYPTO_SUITE); + assertTrue(cryptosuite == PARCCryptoSuite_RSA_SHA256, "Unexpected PARCCryptoSuite value in dictionary"); + + ccnxTlvDictionary_Release(&dictionary); +} + +LONGBOW_TEST_FIXTURE(Local) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(ccnxValidation_RsaSha256); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-common/ccnx/common/validation/test/testrig_validation.c b/libccnx-common/ccnx/common/validation/test/testrig_validation.c new file mode 100755 index 00000000..185be143 --- /dev/null +++ b/libccnx-common/ccnx/common/validation/test/testrig_validation.c @@ -0,0 +1,298 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Common routines to test validators + * + */ + +#include <ccnx/common/ccnx_KeyLocator.h> +#include <ccnx/common/internal/ccnx_ValidationFacadeV1.h> + +#include <ccnx/common/ccnx_ContentObject.h> + +#include <parc/algol/parc_Buffer.h> + +typedef struct test_data { + PARCBuffer *keyid; + PARCBuffer *key; + PARCBuffer *cert; + CCNxName *keyname; + + CCNxKeyLocator *locatorByKey; + CCNxKeyLocator *locatorByName; +} TestData; + +PARCBuffer * +bufferFromString(size_t length, const char string[length]) +{ + return parcBuffer_Flip(parcBuffer_PutArray(parcBuffer_Allocate(length), length, (const uint8_t *) string)); +} + +TestData * +testData_Create(void) +{ + char keyidString[] = "the keyid"; + char keyString[] = "Memory, all alone in the moonlight"; + char certString[] = "The quick brown fox"; + + TestData *data = parcMemory_AllocateAndClear(sizeof(TestData)); + assertNotNull(data, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(TestData)); + + data->keyid = bufferFromString(sizeof(keyidString), keyidString); + data->key = bufferFromString(sizeof(keyString), keyString); + data->cert = bufferFromString(sizeof(certString), certString); + data->keyname = ccnxName_CreateFromCString("lci:/lazy/dog"); + + PARCBuffer *bb_id = parcBuffer_Wrap("choo choo", 9, 0, 9); + PARCKeyId *keyid = parcKeyId_Create(bb_id); + parcBuffer_Release(&bb_id); + + PARCKey *key = parcKey_CreateFromDerEncodedPublicKey(keyid, PARCSigningAlgorithm_RSA, data->key); + + data->locatorByKey = ccnxKeyLocator_CreateFromKey(key); + parcKey_Release(&key); + parcKeyId_Release(&keyid); + + CCNxLink *link = ccnxLink_Create(data->keyname, NULL, NULL); + data->locatorByName = ccnxKeyLocator_CreateFromKeyLink(link); + ccnxLink_Release(&link); + + return data; +} + +void +testData_Release(TestData **dataPtr) +{ + TestData *data = *dataPtr; + + ccnxKeyLocator_Release(&data->locatorByKey); + ccnxKeyLocator_Release(&data->locatorByName); + ccnxName_Release(&data->keyname); + parcBuffer_Release(&data->cert); + parcBuffer_Release(&data->key); + parcBuffer_Release(&data->keyid); + + parcMemory_Deallocate((void **) &data); + *dataPtr = NULL; +} + +TestData * +commonSetup(void) +{ + TestData *data = testData_Create(); + return data; +} + +int +commonTeardown(TestData *data) +{ + testData_Release(&data); + return 0; +} + +// === V1 + +void +testValidationSetV1_NoParam(TestData *data, bool (*set)(CCNxTlvDictionary *message), bool (*test)(const CCNxTlvDictionary *message)) +{ + CCNxName *name = ccnxName_CreateFromCString("lci:/parc/validation/test"); + CCNxTlvDictionary *packetV1 = ccnxContentObject_CreateWithImplAndPayload(&CCNxContentObjectFacadeV1_Implementation, + name, + CCNxPayloadType_DATA, + NULL); + bool success = set(packetV1); + assertTrue(success, "Failed to set on V1"); + + bool testResult = test(packetV1); + assertTrue(testResult, "Test function failed on V1 packet"); + + ccnxName_Release(&name); + ccnxTlvDictionary_Release(&packetV1); +} + +void +testValidationSetV1_KeyId_Null(TestData *data, bool (*set)(CCNxTlvDictionary *message, const PARCBuffer *keyid), bool (*test)(const CCNxTlvDictionary *message)) +{ + CCNxName *name = ccnxName_CreateFromCString("lci:/parc/validation/test"); + CCNxTlvDictionary *packetV1 = ccnxContentObject_CreateWithImplAndPayload(&CCNxContentObjectFacadeV1_Implementation, + name, + CCNxPayloadType_DATA, + NULL); + bool success = set(packetV1, NULL); + assertTrue(success, "Failed to set on V1"); + + bool testResult = test(packetV1); + assertTrue(testResult, "Test function failed on V1 packet"); + + ccnxName_Release(&name); + ccnxTlvDictionary_Release(&packetV1); +} + +void +testValidationSetV1_KeyId_KeyId(TestData *data, bool (*set)(CCNxTlvDictionary *message, const PARCBuffer *keyid), bool (*test)(const CCNxTlvDictionary *message)) +{ + CCNxName *name = ccnxName_CreateFromCString("lci:/parc/validation/test"); + CCNxTlvDictionary *packetV1 = ccnxContentObject_CreateWithImplAndPayload(&CCNxContentObjectFacadeV1_Implementation, + name, + CCNxPayloadType_DATA, + NULL); + bool success = set(packetV1, data->keyid); + assertTrue(success, "Failed to set on V1"); + + bool testResult = test(packetV1); + assertTrue(testResult, "Test function failed on V1 packet"); + + PARCBuffer *testKeyId = ccnxValidationFacadeV1_GetKeyId(packetV1); + assertTrue(parcBuffer_Equals(testKeyId, data->keyid), "keyid not equal"); + + ccnxName_Release(&name); + ccnxTlvDictionary_Release(&packetV1); +} + +void +testValidationSetV1_KeyId_KeyLocator_Null_Null(TestData *data, + bool (*set)(CCNxTlvDictionary *message, const PARCBuffer *keyid, + const CCNxKeyLocator *keyLocator), + bool (*test)(const CCNxTlvDictionary *message)) +{ + CCNxName *name = ccnxName_CreateFromCString("lci:/parc/validation/test"); + CCNxTlvDictionary *packetV1 = ccnxContentObject_CreateWithImplAndPayload(&CCNxContentObjectFacadeV1_Implementation, + name, + CCNxPayloadType_DATA, + NULL); + bool success = set(packetV1, NULL, NULL); + assertTrue(success, "Failed to set on V1"); + + bool testResult = test(packetV1); + assertTrue(testResult, "Test function failed on V1 packet"); + + ccnxName_Release(&name); + ccnxTlvDictionary_Release(&packetV1); +} + +void +testValidationSetV1_KeyId_KeyLocator_KeyId_Null(TestData *data, + bool (*set)(CCNxTlvDictionary *message, const PARCBuffer *keyid, + const CCNxKeyLocator *keyLocator), + bool (*test)(const CCNxTlvDictionary *message)) +{ + CCNxName *name = ccnxName_CreateFromCString("lci:/parc/validation/test"); + CCNxTlvDictionary *packetV1 = ccnxContentObject_CreateWithImplAndPayload(&CCNxContentObjectFacadeV1_Implementation, + name, + CCNxPayloadType_DATA, + NULL); + bool success = set(packetV1, data->keyid, NULL); + assertTrue(success, "Failed to set on V1"); + + bool testResult = test(packetV1); + assertTrue(testResult, "Test function failed on V1 packet"); + + PARCBuffer *testKeyId = ccnxValidationFacadeV1_GetKeyId(packetV1); + assertTrue(parcBuffer_Equals(testKeyId, data->keyid), "keyid not equal"); + + ccnxName_Release(&name); + ccnxTlvDictionary_Release(&packetV1); +} + +void +testValidationSetV1_KeyId_KeyLocator_KeyId_Key(TestData *data, + bool (*set)(CCNxTlvDictionary *message, const PARCBuffer *keyid, + const CCNxKeyLocator *keyLocator), + bool (*test)(const CCNxTlvDictionary *message)) +{ + CCNxName *name = ccnxName_CreateFromCString("lci:/parc/validation/test"); + CCNxTlvDictionary *packetV1 = ccnxContentObject_CreateWithImplAndPayload(&CCNxContentObjectFacadeV1_Implementation, + name, + CCNxPayloadType_DATA, + NULL); + bool success = set(packetV1, data->keyid, data->locatorByKey); + assertTrue(success, "Failed to set on V1"); + + bool testResult = test(packetV1); + assertTrue(testResult, "Test function failed on V1 packet"); + + PARCBuffer *testKeyId = ccnxValidationFacadeV1_GetKeyId(packetV1); + assertTrue(parcBuffer_Equals(testKeyId, data->keyid), "keyid not equal"); + + PARCBuffer *testKey = ccnxValidationFacadeV1_GetPublicKey(packetV1); + assertTrue(parcBuffer_Equals(testKey, data->key), "keys not equal"); + + ccnxName_Release(&name); + ccnxTlvDictionary_Release(&packetV1); +} + +void +testValidationSetV1_KeyId_KeyLocator_KeyId_KeyName(TestData *data, + bool (*set)(CCNxTlvDictionary *message, const PARCBuffer *keyid, + const CCNxKeyLocator *keyLocator), + bool (*test)(const CCNxTlvDictionary *message)) +{ + CCNxName *name = ccnxName_CreateFromCString("lci:/parc/validation/test"); + CCNxTlvDictionary *packetV1 = ccnxContentObject_CreateWithImplAndPayload(&CCNxContentObjectFacadeV1_Implementation, + name, + CCNxPayloadType_DATA, + NULL); + bool success = set(packetV1, data->keyid, data->locatorByName); + assertTrue(success, "Failed to set on V1"); + + bool testResult = test(packetV1); + assertTrue(testResult, "Test function failed on V1 packet"); + + PARCBuffer *testKeyId = ccnxValidationFacadeV1_GetKeyId(packetV1); + assertTrue(parcBuffer_Equals(testKeyId, data->keyid), "keyid not equal"); + + // XXX: TODO: GetKeyName() returns a Link, so it should be GetLink(). + // It also creates a new object (the CCNxLink), so... needs thinking about. + // See BugzId: 3322 + + CCNxLink *testLink = ccnxValidationFacadeV1_GetKeyName(packetV1); + assertTrue(ccnxName_Equals(ccnxLink_GetName(testLink), data->keyname), "Keynames not equal"); + ccnxLink_Release(&testLink); + + ccnxName_Release(&name); + ccnxTlvDictionary_Release(&packetV1); +} + +// === General test for public key algs + +void +testValidationSet_KeyId_KeyLocator(TestData *data, bool (*set)(CCNxTlvDictionary *message, const PARCBuffer *keyid, const CCNxKeyLocator *keyLocator), bool (*test)(const CCNxTlvDictionary *message), bool v0ok, bool v1ok) +{ + if (v1ok) { + testValidationSetV1_KeyId_KeyLocator_Null_Null(data, set, test); + testValidationSetV1_KeyId_KeyLocator_KeyId_Null(data, set, test); + testValidationSetV1_KeyId_KeyLocator_KeyId_Key(data, set, test); + testValidationSetV1_KeyId_KeyLocator_KeyId_KeyName(data, set, test); + } +} + +void +testValidationSet_KeyId(TestData *data, bool (*set)(CCNxTlvDictionary *message, const PARCBuffer *keyid), bool (*test)(const CCNxTlvDictionary *message), bool v0ok, bool v1ok) +{ + if (v1ok) { + testValidationSetV1_KeyId_Null(data, set, test); + testValidationSetV1_KeyId_KeyId(data, set, test); + } +} + +void +testValidationSet_NoParam(TestData *data, bool (*set)(CCNxTlvDictionary *message), bool (*test)(const CCNxTlvDictionary *message), bool v0ok, bool v1ok) +{ + if (v1ok) { + testValidationSetV1_NoParam(data, set, test); + testValidationSetV1_NoParam(data, set, test); + } +} |