blob: 0ebed63e49be34e72853ac539f681c702661d9cb [file] [log] [blame]
/*
* AWS IoT Common V1.0.0
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* 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 aws_iot_parser.c
* @brief Parses topics for Thing Name and status.
*/
/* The config header is always included first. */
#include "iot_config.h"
/* Standard includes. */
#include <string.h>
/* AWS IoT include. */
#include "aws_iot.h"
/* Error handling include. */
#include "iot_error.h"
/* AWS Parser include. */
#include "aws_iot_doc_parser.h"
/**
* @brief Minimum allowed topic length for an AWS IoT status topic.
*
* Topics must contain at least:
* - The common prefix
* - The suffix "/accepted" or "/rejected"
* - 1 character for the Thing Name
* - 2 characters for the operation name and the enclosing slashes
*/
#define MINIMUM_TOPIC_NAME_LENGTH \
( uint16_t ) ( AWS_IOT_TOPIC_PREFIX_LENGTH + \
AWS_IOT_ACCEPTED_SUFFIX_LENGTH + \
1 + 2 )
/**
* @brief The longest client token accepted by AWS IoT service, per AWS IoT
* service limits.
*/
#define MAX_CLIENT_TOKEN_LENGTH ( 64 )
/*-----------------------------------------------------------*/
bool AwsIot_GetClientToken( const char * pJsonDocument,
size_t jsonDocumentLength,
const char ** pClientToken,
size_t * pClientTokenLength )
{
/* Extract the client token from the JSON document. */
bool status = AwsIotDocParser_FindValue( pJsonDocument,
jsonDocumentLength,
AWS_IOT_CLIENT_TOKEN_KEY,
AWS_IOT_CLIENT_TOKEN_KEY_LENGTH,
pClientToken,
pClientTokenLength );
if( status == true )
{
/* Check that the length of the client token is valid. */
if( ( *pClientTokenLength < 2 ) ||
( *pClientTokenLength > MAX_CLIENT_TOKEN_LENGTH ) )
{
status = false;
}
}
return status;
}
/*-----------------------------------------------------------*/
bool AwsIot_ParseThingName( const char * pTopicName,
uint16_t topicNameLength,
const char ** pThingName,
size_t * pThingNameLength )
{
IOT_FUNCTION_ENTRY( bool, true );
const char * pThingNameStart = NULL;
size_t thingNameLength = 0;
/* Check that the topic name is at least as long as the minimum allowed. */
if( topicNameLength < MINIMUM_TOPIC_NAME_LENGTH )
{
IOT_SET_AND_GOTO_CLEANUP( false );
}
/* Check that the given topic starts with the common prefix. */
if( strncmp( AWS_IOT_TOPIC_PREFIX,
pTopicName,
AWS_IOT_TOPIC_PREFIX_LENGTH ) != 0 )
{
IOT_SET_AND_GOTO_CLEANUP( false );
}
/* The Thing Name starts immediately after the topic prefix. */
pThingNameStart = pTopicName + AWS_IOT_TOPIC_PREFIX_LENGTH;
/* Calculate the length of the Thing Name, which is terminated with a '/'. */
while( ( thingNameLength + AWS_IOT_TOPIC_PREFIX_LENGTH < ( size_t ) topicNameLength ) &&
( pThingNameStart[ thingNameLength ] != '/' ) )
{
thingNameLength++;
}
/* The end of the topic name was reached without finding a '/'. The topic
* name is invalid. */
if( thingNameLength + AWS_IOT_TOPIC_PREFIX_LENGTH >= ( size_t ) topicNameLength )
{
IOT_SET_AND_GOTO_CLEANUP( false );
}
/* Set the output parameters. */
*pThingName = pThingNameStart;
*pThingNameLength = thingNameLength;
IOT_FUNCTION_EXIT_NO_CLEANUP();
}
/*-----------------------------------------------------------*/
AwsIotStatus_t AwsIot_ParseStatus( const char * pTopicName,
uint16_t topicNameLength )
{
IOT_FUNCTION_ENTRY( AwsIotStatus_t, AWS_IOT_UNKNOWN );
const char * pSuffixStart = NULL;
/* Both 'accepted' and 'rejected' topics are of the same length
* The below is a defensive check at run time to ensure that.
*/
Iot_DefaultAssert( AWS_IOT_ACCEPTED_SUFFIX_LENGTH == AWS_IOT_REJECTED_SUFFIX_LENGTH );
/* Check that the status topic name is at least as long as the
* "accepted" suffix. This length check will be good for rejected also
* as both are of 8 characters in length. */
if( topicNameLength > AWS_IOT_ACCEPTED_SUFFIX_LENGTH )
{
/* Calculate where the "accepted" suffix should start. */
pSuffixStart = pTopicName + topicNameLength - AWS_IOT_ACCEPTED_SUFFIX_LENGTH;
/* Check if the end of the status topic name is "/accepted". */
if( strncmp( pSuffixStart,
AWS_IOT_ACCEPTED_SUFFIX,
AWS_IOT_ACCEPTED_SUFFIX_LENGTH ) == 0 )
{
IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_ACCEPTED );
}
/* Check if the end of the status topic name is "/rejected". */
if( strncmp( pSuffixStart,
AWS_IOT_REJECTED_SUFFIX,
AWS_IOT_REJECTED_SUFFIX_LENGTH ) == 0 )
{
IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_REJECTED );
}
}
IOT_FUNCTION_EXIT_NO_CLEANUP();
}
/*-----------------------------------------------------------*/