Files
gaoyang3513 a345df017b [修改] 增加freeRTOS
1. 版本FreeRTOSv202212.01,命名为kernel;
2023-05-06 16:43:01 +00:00

767 lines
27 KiB
C

/*
* AWS IoT Fleet Provisioning v1.1.0
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file fleet_provisioning.c
* @brief Implementation of the AWS IoT Fleet Provisioning Library.
*/
/* Standard includes. */
#include <assert.h>
#include <stddef.h>
#include <string.h>
/* Fleet Provisioning API include. */
#include "fleet_provisioning.h"
/**
* @brief Identifier for which of the topic suffixes for a given format and
* Fleet Provisioning MQTT API.
*/
typedef enum
{
TopicPublish,
TopicAccepted,
TopicRejected,
TopicInvalidSuffix
} TopicSuffix_t;
/**
* @brief Identifier for which of the topics in each Fleet Provisioning MQTT API.
*/
typedef enum
{
TopicJsonPublish,
TopicJsonAccepted,
TopicJsonRejected,
TopicCborPublish,
TopicCborAccepted,
TopicCborRejected,
TopicInvalidFormatSuffix
} TopicFormatSuffix_t;
/**
* @brief Get the topic length for a given RegisterThing topic.
*
* @param[in] templateNameLength the length of the template name registered with
* AWS IoT.
* @param[in] format The RegisterThing API format to use.
* @param[in] topic The RegisterThing API format to use.
*
* @return the template length for the given RegisterThing topic.
*/
static uint16_t getRegisterThingTopicLength( uint16_t templateNameLength,
FleetProvisioningFormat_t format,
FleetProvisioningApiTopics_t topic );
/**
* @brief Write the given piece of the topic to the remaining buffer and advance
* the remaining buffer pointer.
*
* The caller is responsible for assuring that there is enough space remaining
* in the buffer to write the given string.
*
* @param[in,out] pBufferCursor Pointer to the remaining buffer.
* @param[in] fragment The piece of the topic string to write.
* @param[in] length The length of @p fragment.
*/
static void writeTopicFragmentAndAdvance( char ** pBufferCursor,
const char * fragment,
uint16_t length );
/**
* @brief Check the parameters for FleetProvisioning_GetRegisterThingTopic().
*
* @param[in] pTopicBuffer The buffer to write the topic string into.
* @param[in] format The desired RegisterThing format.
* @param[in] topic The desired RegisterThing topic.
* @param[in] pTemplateName The name of the provisioning template configured
* with AWS IoT.
* @param[in] templateNameLength The length of @p pTemplateName.
* @param[in] pOutLength The length of the topic string written to
* the buffer.
*
* @return FleetProvisioningSuccess if no errors are found with the parameters;
* FleetProvisioningBadParameter otherwise.
*/
static FleetProvisioningStatus_t GetRegisterThingTopicCheckParams( const char * pTopicBuffer,
FleetProvisioningFormat_t format,
FleetProvisioningApiTopics_t topic,
const char * pTemplateName,
uint16_t templateNameLength,
const uint16_t * pOutLength );
/**
* @brief Match the suffix from the remaining topic string and return the
* corresponding suffix.
*
* Suffix: empty, /accepted, or /rejected.
*
* @param[in] pRemainingTopic The remaining portion of the topic.
* @param[in] remainingLength The remaining length of the topic.
*
* @return The matching #TopicSuffix_t.
*/
static TopicSuffix_t parseTopicSuffix( const char * pRemainingTopic,
uint16_t remainingLength );
/**
* @brief Match the format and suffix from the remaining topic string and
* return the corresponding format and suffix.
*
* Format: json or cbor.
* Suffix: empty, /accepted, or /rejected.
*
* @param[in] pRemainingTopic The remaining portion of the topic.
* @param[in] remainingLength The remaining length of the topic.
*
* @return The matching #TopicFormatSuffix_t.
*/
static TopicFormatSuffix_t parseTopicFormatSuffix( const char * pRemainingTopic,
uint16_t remainingLength );
/**
* @brief Match a topic string with the CreateCertificateFromCsr topics.
*
* @param[in] pTopic The topic string to match.
* @param[in] topicLength The length of the topic string.
*
* @return The matching #FleetProvisioningTopic_t if the topic string is a
* Fleet Provisioning CreateCertificateFromCsr topic, else
* FleetProvisioningInvalidTopic.
*/
static FleetProvisioningTopic_t parseCreateCertificateFromCsrTopic( const char * pTopic,
uint16_t topicLength );
/**
* @brief Match a topic string with the CreateKeysAndCertificate topics.
*
* @param[in] pTopic The topic string to match.
* @param[in] topicLength The length of the topic string.
*
* @return The matching FleetProvisioningTopic_t if the topic string is a
* Fleet Provisioning CreateKeysAndCertificate topic, else
* FleetProvisioningInvalidTopic.
*/
static FleetProvisioningTopic_t parseCreateKeysAndCertificateTopic( const char * pTopic,
uint16_t topicLength );
/**
* @brief Match a topic string with the RegisterThing topics.
*
* @param[in] pTopic The topic string to match.
* @param[in] topicLength The length of the topic string.
*
* @return The matching #FleetProvisioningTopic_t if the topic string is a
* Fleet Provisioning RegisterThing topic, else
* FleetProvisioningInvalidTopic.
*/
static FleetProvisioningTopic_t parseRegisterThingTopic( const char * pTopic,
uint16_t topicLength );
/**
* @brief Check if the remaining buffer starts with a specified string. If so,
* moves the remaining buffer pointer past the matched section and updates the
* remaining length.
*
* @param[in,out] pBufferCursor Pointer to the remaining portion of the buffer.
* @param[in,out] pRemainingLength The remaining length of the buffer.
* @param[in] matchString The string to match against.
* @param[in] matchLength The length of @p matchString.
*
* @return FleetProvisioningSuccess if the string matches and is skipped over;
* FleetProvisioningNoMatch otherwise.
*/
static FleetProvisioningStatus_t consumeIfMatch( const char ** pBufferCursor,
uint16_t * pRemainingLength,
const char * matchString,
uint16_t matchLength );
/**
* @brief Move the remaining topic pointer past the template name in the
* unparsed topic so far, and update the remaining topic length.
*
* The end of thing name is marked by a forward slash. A zero length thing name
* is not valid.
*
* This function extracts the same template name from the following topic strings:
* - $aws/provisioning-templates/TEMPLATE_NAME/provision/json/accepted
* - $aws/provisioning-templates/TEMPLATE_NAME
* The second topic is not a valid Fleet Provisioning topic and the matching
* will fail when we try to match the bridge part.
*
* @param[in,out] pTopicCursor Pointer to the remaining topic string.
* @param[in,out] pRemainingLength Pointer to the length of the remaining topic string.
*
* @return FleetProvisioningSuccess if a valid template name is skipped over;
* FleetProvisioningNoMatch otherwise.
*/
static FleetProvisioningStatus_t consumeTemplateName( const char ** pTopicCursor,
uint16_t * pRemainingLength );
/*-----------------------------------------------------------*/
static uint16_t getRegisterThingTopicLength( uint16_t templateNameLength,
FleetProvisioningFormat_t format,
FleetProvisioningApiTopics_t topic )
{
uint16_t topicLength = 0U;
assert( ( templateNameLength != 0U ) &&
( templateNameLength <= FP_TEMPLATENAME_MAX_LENGTH ) );
assert( ( format == FleetProvisioningJson ) || ( format == FleetProvisioningCbor ) );
assert( ( topic >= FleetProvisioningPublish ) && ( topic <= FleetProvisioningRejected ) );
topicLength = FP_REGISTER_API_LENGTH_PREFIX +
templateNameLength +
FP_REGISTER_API_LENGTH_BRIDGE;
if( format == FleetProvisioningJson )
{
topicLength += FP_API_LENGTH_JSON_FORMAT;
}
if( format == FleetProvisioningCbor )
{
topicLength += FP_API_LENGTH_CBOR_FORMAT;
}
if( topic == FleetProvisioningAccepted )
{
topicLength += FP_API_LENGTH_ACCEPTED_SUFFIX;
}
if( topic == FleetProvisioningRejected )
{
topicLength += FP_API_LENGTH_REJECTED_SUFFIX;
}
return topicLength;
}
/*-----------------------------------------------------------*/
static void writeTopicFragmentAndAdvance( char ** pBufferCursor,
const char * fragment,
uint16_t length )
{
assert( pBufferCursor != NULL );
assert( *pBufferCursor != NULL );
assert( fragment != NULL );
( void ) memcpy( ( void * ) *pBufferCursor,
( const void * ) fragment,
( size_t ) length );
*pBufferCursor += length;
}
/*-----------------------------------------------------------*/
static FleetProvisioningStatus_t GetRegisterThingTopicCheckParams( const char * pTopicBuffer,
FleetProvisioningFormat_t format,
FleetProvisioningApiTopics_t topic,
const char * pTemplateName,
uint16_t templateNameLength,
const uint16_t * pOutLength )
{
FleetProvisioningStatus_t ret = FleetProvisioningError;
if( ( pTopicBuffer == NULL ) ||
( format < FleetProvisioningJson ) ||
( format > FleetProvisioningCbor ) ||
( topic < FleetProvisioningPublish ) ||
( topic > FleetProvisioningRejected ) ||
( pTemplateName == NULL ) ||
( templateNameLength == 0U ) ||
( templateNameLength > FP_TEMPLATENAME_MAX_LENGTH ) ||
( pOutLength == NULL ) )
{
ret = FleetProvisioningBadParameter;
LogError( ( "Invalid input parameter. pTopicBuffer: %p, format: %d, topic: %d,"
" pTemplateName: %p, templateNameLength: %u, pOutLength: %p.",
( const void * ) pTopicBuffer,
( int ) format,
( int ) topic,
( const void * ) pTemplateName,
( unsigned int ) templateNameLength,
( const void * ) pOutLength ) );
}
else
{
ret = FleetProvisioningSuccess;
}
return ret;
}
/*-----------------------------------------------------------*/
static TopicSuffix_t parseTopicSuffix( const char * pRemainingTopic,
uint16_t remainingLength )
{
TopicSuffix_t ret = TopicInvalidSuffix;
FleetProvisioningStatus_t status = FleetProvisioningNoMatch;
const char * pTopicCursor = pRemainingTopic;
uint16_t cursorLength = remainingLength;
assert( pRemainingTopic != NULL );
/* Check if publish topic */
if( cursorLength == 0U )
{
ret = TopicPublish;
status = FleetProvisioningSuccess;
}
/* Check if accepted topic */
if( status == FleetProvisioningNoMatch )
{
status = consumeIfMatch( &pTopicCursor,
&cursorLength,
FP_API_ACCEPTED_SUFFIX,
FP_API_LENGTH_ACCEPTED_SUFFIX );
if( status == FleetProvisioningSuccess )
{
if( cursorLength == 0U )
{
ret = TopicAccepted;
}
else
{
status = FleetProvisioningError;
}
}
}
/* Check if rejected topic */
if( status == FleetProvisioningNoMatch )
{
status = consumeIfMatch( &pTopicCursor,
&cursorLength,
FP_API_REJECTED_SUFFIX,
FP_API_LENGTH_REJECTED_SUFFIX );
if( status == FleetProvisioningSuccess )
{
if( cursorLength == 0U )
{
ret = TopicRejected;
}
}
}
return ret;
}
/*-----------------------------------------------------------*/
static TopicFormatSuffix_t parseTopicFormatSuffix( const char * pRemainingTopic,
uint16_t remainingLength )
{
/* Table of JSON format and suffixes in same order as TopicSuffix_t. */
static const TopicFormatSuffix_t jsonSuffixes[] =
{
TopicJsonPublish,
TopicJsonAccepted,
TopicJsonRejected,
TopicInvalidFormatSuffix
};
/* Table of CBOR format and suffixes in same order as TopicSuffix_t. */
static const TopicFormatSuffix_t cborSuffixes[] =
{
TopicCborPublish,
TopicCborAccepted,
TopicCborRejected,
TopicInvalidFormatSuffix
};
TopicFormatSuffix_t ret = TopicInvalidFormatSuffix;
TopicSuffix_t suffix = TopicInvalidSuffix;
FleetProvisioningStatus_t status = FleetProvisioningNoMatch;
const char * pTopicCursor = pRemainingTopic;
uint16_t cursorLength = remainingLength;
assert( pRemainingTopic != NULL );
/* Check if JSON format */
status = consumeIfMatch( &pTopicCursor,
&cursorLength,
FP_API_JSON_FORMAT,
FP_API_LENGTH_JSON_FORMAT );
if( status == FleetProvisioningSuccess )
{
/* Match suffix */
suffix = parseTopicSuffix( pTopicCursor, cursorLength );
ret = jsonSuffixes[ suffix ];
}
if( status == FleetProvisioningNoMatch )
{
/* Check if CBOR format */
status = consumeIfMatch( &pTopicCursor,
&cursorLength,
FP_API_CBOR_FORMAT,
FP_API_LENGTH_CBOR_FORMAT );
if( status == FleetProvisioningSuccess )
{
/* Match suffix */
suffix = parseTopicSuffix( pTopicCursor, cursorLength );
ret = cborSuffixes[ suffix ];
}
}
return ret;
}
/*-----------------------------------------------------------*/
static FleetProvisioningTopic_t parseCreateCertificateFromCsrTopic( const char * pTopic,
uint16_t topicLength )
{
/* Table of topics in the same order as TopicFormatSuffix_t. */
static const FleetProvisioningTopic_t createCertificateFromCsrApi[] =
{
FleetProvJsonCreateCertFromCsrPublish,
FleetProvJsonCreateCertFromCsrAccepted,
FleetProvJsonCreateCertFromCsrRejected,
FleetProvCborCreateCertFromCsrPublish,
FleetProvCborCreateCertFromCsrAccepted,
FleetProvCborCreateCertFromCsrRejected,
FleetProvisioningInvalidTopic
};
FleetProvisioningTopic_t ret = FleetProvisioningInvalidTopic;
FleetProvisioningStatus_t status = FleetProvisioningError;
TopicFormatSuffix_t rest = TopicInvalidFormatSuffix;
const char * pTopicCursor = pTopic;
uint16_t cursorLength = topicLength;
assert( pTopic != NULL );
/* Check if prefix matches */
status = consumeIfMatch( &pTopicCursor,
&cursorLength,
FP_CREATE_CERT_API_PREFIX,
FP_CREATE_CERT_API_LENGTH_PREFIX );
if( status == FleetProvisioningSuccess )
{
/* Match format and suffix */
rest = parseTopicFormatSuffix( pTopicCursor, cursorLength );
ret = createCertificateFromCsrApi[ rest ];
}
return ret;
}
/*-----------------------------------------------------------*/
static FleetProvisioningTopic_t parseCreateKeysAndCertificateTopic( const char * pTopic,
uint16_t topicLength )
{
/* Table of topics in the same order as TopicFormatSuffix_t. */
static const FleetProvisioningTopic_t createKeysAndCertificateApi[] =
{
FleetProvJsonCreateKeysAndCertPublish,
FleetProvJsonCreateKeysAndCertAccepted,
FleetProvJsonCreateKeysAndCertRejected,
FleetProvCborCreateKeysAndCertPublish,
FleetProvCborCreateKeysAndCertAccepted,
FleetProvCborCreateKeysAndCertRejected,
FleetProvisioningInvalidTopic
};
FleetProvisioningTopic_t ret = FleetProvisioningInvalidTopic;
FleetProvisioningStatus_t status = FleetProvisioningError;
TopicFormatSuffix_t rest = TopicInvalidFormatSuffix;
const char * pTopicCursor = pTopic;
uint16_t cursorLength = topicLength;
assert( pTopic != NULL );
/* Check if prefix matches */
status = consumeIfMatch( &pTopicCursor,
&cursorLength,
FP_CREATE_KEYS_API_PREFIX,
FP_CREATE_KEYS_API_LENGTH_PREFIX );
if( status == FleetProvisioningSuccess )
{
/* Match format and suffix */
rest = parseTopicFormatSuffix( pTopicCursor, cursorLength );
ret = createKeysAndCertificateApi[ rest ];
}
return ret;
}
/*-----------------------------------------------------------*/
static FleetProvisioningTopic_t parseRegisterThingTopic( const char * pTopic,
uint16_t topicLength )
{
/* Table of topics in the same order as TopicFormatSuffix_t. */
static const FleetProvisioningTopic_t registerThingApi[] =
{
FleetProvJsonRegisterThingPublish,
FleetProvJsonRegisterThingAccepted,
FleetProvJsonRegisterThingRejected,
FleetProvCborRegisterThingPublish,
FleetProvCborRegisterThingAccepted,
FleetProvCborRegisterThingRejected,
FleetProvisioningInvalidTopic
};
FleetProvisioningTopic_t ret = FleetProvisioningInvalidTopic;
FleetProvisioningStatus_t status = FleetProvisioningError;
TopicFormatSuffix_t rest = TopicInvalidFormatSuffix;
const char * pTopicCursor = pTopic;
uint16_t cursorLength = topicLength;
assert( pTopic != NULL );
/* Check if prefix matches */
status = consumeIfMatch( &pTopicCursor,
&cursorLength,
FP_REGISTER_API_PREFIX,
FP_REGISTER_API_LENGTH_PREFIX );
if( status == FleetProvisioningSuccess )
{
/* Skip template name */
status = consumeTemplateName( &pTopicCursor,
&cursorLength );
}
if( status == FleetProvisioningSuccess )
{
/* Check if bridge matches */
status = consumeIfMatch( &pTopicCursor,
&cursorLength,
FP_REGISTER_API_BRIDGE,
FP_REGISTER_API_LENGTH_BRIDGE );
}
if( status == FleetProvisioningSuccess )
{
/* Match format and suffix */
rest = parseTopicFormatSuffix( pTopicCursor, cursorLength );
ret = registerThingApi[ rest ];
}
return ret;
}
/*-----------------------------------------------------------*/
static FleetProvisioningStatus_t consumeIfMatch( const char ** pBufferCursor,
uint16_t * pRemainingLength,
const char * matchString,
uint16_t matchLength )
{
FleetProvisioningStatus_t status = FleetProvisioningError;
int32_t cmpVal = -1;
assert( pBufferCursor != NULL );
assert( *pBufferCursor != NULL );
assert( pRemainingLength != NULL );
assert( matchString != NULL );
if( *pRemainingLength < matchLength )
{
status = FleetProvisioningNoMatch;
}
else
{
cmpVal = strncmp( *pBufferCursor,
matchString,
( size_t ) matchLength );
if( cmpVal != 0 )
{
status = FleetProvisioningNoMatch;
}
else
{
status = FleetProvisioningSuccess;
*pBufferCursor += matchLength;
*pRemainingLength -= matchLength;
}
}
return status;
}
/*-----------------------------------------------------------*/
static FleetProvisioningStatus_t consumeTemplateName( const char ** pTopicCursor,
uint16_t * pRemainingLength )
{
FleetProvisioningStatus_t ret = FleetProvisioningNoMatch;
uint16_t i = 0U;
assert( pTopicCursor != NULL );
assert( *pTopicCursor != NULL );
assert( pRemainingLength != NULL );
/* Find the first forward slash. It marks the end of the template name. */
for( i = 0U; i < *pRemainingLength; i++ )
{
if( ( *pTopicCursor )[ i ] == '/' )
{
break;
}
}
/* Zero length template name is not valid. */
if( i > 0U )
{
ret = FleetProvisioningSuccess;
*pTopicCursor += i;
*pRemainingLength -= i;
}
return ret;
}
/*-----------------------------------------------------------*/
FleetProvisioningStatus_t FleetProvisioning_GetRegisterThingTopic( char * pTopicBuffer,
uint16_t bufferLength,
FleetProvisioningFormat_t format,
FleetProvisioningApiTopics_t topic,
const char * pTemplateName,
uint16_t templateNameLength,
uint16_t * pOutLength )
{
FleetProvisioningStatus_t status = FleetProvisioningError;
uint16_t topicLength = 0U;
char * pBufferCursor = pTopicBuffer;
status = GetRegisterThingTopicCheckParams( pTopicBuffer,
format,
topic,
pTemplateName,
templateNameLength,
pOutLength );
if( status == FleetProvisioningSuccess )
{
topicLength = getRegisterThingTopicLength( templateNameLength, format, topic );
if( bufferLength < topicLength )
{
status = FleetProvisioningBufferTooSmall;
LogError( ( "The buffer is too small to hold the topic string. "
"Provided buffer size: %u, Required buffer size: %u.",
( unsigned int ) bufferLength,
( unsigned int ) topicLength ) );
}
}
if( status == FleetProvisioningSuccess )
{
/* At this point, it is certain that we have a large enough buffer to
* write the topic string into. */
/* Write prefix first. */
writeTopicFragmentAndAdvance( &pBufferCursor,
FP_REGISTER_API_PREFIX,
FP_REGISTER_API_LENGTH_PREFIX );
/* Write template name next. */
writeTopicFragmentAndAdvance( &pBufferCursor,
pTemplateName,
templateNameLength );
/* Write bridge next. */
writeTopicFragmentAndAdvance( &pBufferCursor,
FP_REGISTER_API_BRIDGE,
FP_REGISTER_API_LENGTH_BRIDGE );
/* Write report format. */
if( format == FleetProvisioningJson )
{
writeTopicFragmentAndAdvance( &pBufferCursor,
FP_API_JSON_FORMAT,
FP_API_LENGTH_JSON_FORMAT );
}
if( format == FleetProvisioningCbor )
{
writeTopicFragmentAndAdvance( &pBufferCursor,
FP_API_CBOR_FORMAT,
FP_API_LENGTH_CBOR_FORMAT );
}
/* Write report suffix. */
if( topic == FleetProvisioningAccepted )
{
writeTopicFragmentAndAdvance( &pBufferCursor,
FP_API_ACCEPTED_SUFFIX,
FP_API_LENGTH_ACCEPTED_SUFFIX );
}
if( topic == FleetProvisioningRejected )
{
writeTopicFragmentAndAdvance( &pBufferCursor,
FP_API_REJECTED_SUFFIX,
FP_API_LENGTH_REJECTED_SUFFIX );
}
*pOutLength = topicLength;
}
return status;
}
/*-----------------------------------------------------------*/
FleetProvisioningStatus_t FleetProvisioning_MatchTopic( const char * pTopic,
uint16_t topicLength,
FleetProvisioningTopic_t * pOutApi )
{
FleetProvisioningStatus_t ret = FleetProvisioningNoMatch;
if( ( pTopic == NULL ) || ( pOutApi == NULL ) )
{
ret = FleetProvisioningBadParameter;
LogError( ( "Invalid input parameter. pTopic: %p, pOutApi: %p.",
( const void * ) pTopic,
( void * ) pOutApi ) );
}
else
{
*pOutApi = parseCreateCertificateFromCsrTopic( pTopic, topicLength );
if( *pOutApi == FleetProvisioningInvalidTopic )
{
*pOutApi = parseCreateKeysAndCertificateTopic( pTopic, topicLength );
}
if( *pOutApi == FleetProvisioningInvalidTopic )
{
*pOutApi = parseRegisterThingTopic( pTopic, topicLength );
}
if( *pOutApi != FleetProvisioningInvalidTopic )
{
ret = FleetProvisioningSuccess;
}
}
return ret;
}
/*-----------------------------------------------------------*/