diff options
author | Michele Papalini <micpapal+fdio@cisco.com> | 2017-02-24 08:00:13 +0000 |
---|---|---|
committer | Gerrit Code Review <gerrit@fd.io> | 2017-02-24 08:00:13 +0000 |
commit | 6d4b6878ceff22f9ec8d8e9423214f9666007472 (patch) | |
tree | fa27e0c747676519cb87ff8448bfed62fce5009d /libccnx-common/ccnx/common/codec | |
parent | f28308bd99381ef5f1e178e2e1f870f245e35873 (diff) | |
parent | d18ae43123fcd7604d1c36a1ec8450dbe6071824 (diff) |
Merge "Initial commit: ccnxlibs." into ccnxlibs/master
Diffstat (limited to 'libccnx-common/ccnx/common/codec')
111 files changed, 24383 insertions, 0 deletions
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 |