/* * Copyright (c) 2014-2015, Xerox Corporation (Xerox) and Palo Alto Research Center, Inc (PARC) * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL XEROX OR PARC BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * ################################################################################ * # * # PATENT NOTICE * # * # This software is distributed under the BSD 2-clause License (see LICENSE * # file). This BSD License does not make any patent claims and as such, does * # not act as a patent grant. The purpose of this section is for each contributor * # to define their intentions with respect to intellectual property. * # * # Each contributor to this source code is encouraged to state their patent * # claims and licensing mechanisms for any contributions made. At the end of * # this section contributors may each make their own statements. Contributor's * # claims and grants only apply to the pieces (source code, programs, text, * # media, etc) that they have contributed directly to this software. * # * # There is no guarantee that this section is complete, up to date or accurate. It * # is up to the contributors to maintain their portion of this section and up to * # the user of the software to verify any claims herein. * # * # Do not remove this header notification. The contents of this section must be * # present in all distributions of the software. You may only modify your own * # intellectual property statements. Please provide contact information. * * - Palo Alto Research Center, Inc * This software distribution does not grant any rights to patents owned by Palo * Alto Research Center, Inc (PARC). Rights to these patents are available via * various mechanisms. As of January 2016 PARC has committed to FRAND licensing any * intellectual property used by its contributions to this software. You may * contact PARC at cipo@parc.com for more information or visit http://www.ccnx.org */ /** * @author Marc Mosko, Palo Alto Research Center (Xerox PARC) * @copyright (c) 2014-2015, Xerox Corporation (Xerox) and Palo Alto Research Center, Inc (PARC). All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // ===================================================== // 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; }