mirror of
https://github.com/FreeRTOS/FreeRTOS-Kernel.git
synced 2025-12-18 17:45:15 -05:00
Add the Labs projects provided in the V10.2.1_191129 zip file.
This commit is contained in:
parent
46e5937529
commit
e5708b38e9
801 changed files with 356576 additions and 0 deletions
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,884 @@
|
|||
/*
|
||||
* IoT MQTT V2.1.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 iot_mqtt_network.c
|
||||
* @brief Implements functions involving transport layer connections.
|
||||
*/
|
||||
|
||||
/* The config header is always included first. */
|
||||
#include "iot_config.h"
|
||||
|
||||
/* Standard includes. */
|
||||
#include <string.h>
|
||||
|
||||
/* Error handling include. */
|
||||
#include "iot_error.h"
|
||||
|
||||
/* MQTT internal include. */
|
||||
#include "private/iot_mqtt_internal.h"
|
||||
|
||||
/* Platform layer includes. */
|
||||
#include "platform/iot_threads.h"
|
||||
|
||||
/* Atomics include. */
|
||||
#include "iot_atomic.h"
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* @brief Check if an incoming packet type is valid.
|
||||
*
|
||||
* @param[in] packetType The packet type to check.
|
||||
*
|
||||
* @return `true` if the packet type is valid; `false` otherwise.
|
||||
*/
|
||||
static bool _incomingPacketValid( uint8_t packetType );
|
||||
|
||||
/**
|
||||
* @brief Get an incoming MQTT packet from the network.
|
||||
*
|
||||
* @param[in] pNetworkConnection Network connection to use for receive, which
|
||||
* may be different from the network connection associated with the MQTT connection.
|
||||
* @param[in] pMqttConnection The associated MQTT connection.
|
||||
* @param[out] pIncomingPacket Output parameter for the incoming packet.
|
||||
*
|
||||
* @return #IOT_MQTT_SUCCESS, #IOT_MQTT_NO_MEMORY or #IOT_MQTT_BAD_RESPONSE.
|
||||
*/
|
||||
static IotMqttError_t _getIncomingPacket( void * pNetworkConnection,
|
||||
const _mqttConnection_t * pMqttConnection,
|
||||
_mqttPacket_t * pIncomingPacket );
|
||||
|
||||
/**
|
||||
* @brief Deserialize a packet received from the network.
|
||||
*
|
||||
* @param[in] pMqttConnection The associated MQTT connection.
|
||||
* @param[in] pIncomingPacket The packet received from the network.
|
||||
*
|
||||
* @return #IOT_MQTT_SUCCESS, #IOT_MQTT_NO_MEMORY, #IOT_MQTT_NETWORK_ERROR,
|
||||
* #IOT_MQTT_SCHEDULING_ERROR, #IOT_MQTT_BAD_RESPONSE, or #IOT_MQTT_SERVER_REFUSED.
|
||||
*/
|
||||
static IotMqttError_t _deserializeIncomingPacket( _mqttConnection_t * pMqttConnection,
|
||||
_mqttPacket_t * pIncomingPacket );
|
||||
|
||||
/**
|
||||
* @brief Send a PUBACK for a received QoS 1 PUBLISH packet.
|
||||
*
|
||||
* @param[in] pMqttConnection Which connection the PUBACK should be sent over.
|
||||
* @param[in] packetIdentifier Which packet identifier to include in PUBACK.
|
||||
*/
|
||||
static void _sendPuback( _mqttConnection_t * pMqttConnection,
|
||||
uint16_t packetIdentifier );
|
||||
|
||||
/**
|
||||
* @brief Flush a packet from the stream of incoming data.
|
||||
*
|
||||
* This function is called when memory for a packet cannot be allocated. The
|
||||
* packet is flushed from the stream of incoming data so that the next packet
|
||||
* may be read.
|
||||
*
|
||||
* @param[in] pNetworkConnection Network connection to use for receive, which
|
||||
* may be different from the network connection associated with the MQTT connection.
|
||||
* @param[in] pMqttConnection The associated MQTT connection.
|
||||
* @param[in] length The length of the packet to flush.
|
||||
*/
|
||||
static void _flushPacket( void * pNetworkConnection,
|
||||
const _mqttConnection_t * pMqttConnection,
|
||||
size_t length );
|
||||
|
||||
/**
|
||||
* @cond DOXYGEN_IGNORE
|
||||
* Doxygen should ignore this section.
|
||||
*
|
||||
* Declaration of local MQTT serializer override selectors
|
||||
*/
|
||||
#if IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES == 1
|
||||
_SERIALIZER_OVERRIDE_SELECTOR( IotMqttGetPacketType_t,
|
||||
_getPacketTypeFunc,
|
||||
_IotMqtt_GetPacketType,
|
||||
getPacketType )
|
||||
_SERIALIZER_OVERRIDE_SELECTOR( IotMqttGetRemainingLength_t,
|
||||
_getRemainingLengthFunc,
|
||||
_IotMqtt_GetRemainingLength,
|
||||
getRemainingLength )
|
||||
_SERIALIZER_OVERRIDE_SELECTOR( IotMqttDeserialize_t,
|
||||
_getConnackDeserializer,
|
||||
_IotMqtt_DeserializeConnack,
|
||||
deserialize.connack )
|
||||
_SERIALIZER_OVERRIDE_SELECTOR( IotMqttDeserialize_t,
|
||||
_getPublishDeserializer,
|
||||
_IotMqtt_DeserializePublish,
|
||||
deserialize.publish )
|
||||
_SERIALIZER_OVERRIDE_SELECTOR( IotMqttDeserialize_t,
|
||||
_getPubackDeserializer,
|
||||
_IotMqtt_DeserializePuback,
|
||||
deserialize.puback )
|
||||
_SERIALIZER_OVERRIDE_SELECTOR( IotMqttDeserialize_t,
|
||||
_getSubackDeserializer,
|
||||
_IotMqtt_DeserializeSuback,
|
||||
deserialize.suback )
|
||||
_SERIALIZER_OVERRIDE_SELECTOR( IotMqttDeserialize_t,
|
||||
_getUnsubackDeserializer,
|
||||
_IotMqtt_DeserializeUnsuback,
|
||||
deserialize.unsuback )
|
||||
_SERIALIZER_OVERRIDE_SELECTOR( IotMqttDeserialize_t,
|
||||
_getPingrespDeserializer,
|
||||
_IotMqtt_DeserializePingresp,
|
||||
deserialize.pingresp )
|
||||
_SERIALIZER_OVERRIDE_SELECTOR( IotMqttSerializePuback_t,
|
||||
_getMqttPubackSerializer,
|
||||
_IotMqtt_SerializePuback,
|
||||
serialize.puback )
|
||||
_SERIALIZER_OVERRIDE_SELECTOR( IotMqttFreePacket_t,
|
||||
_getMqttFreePacketFunc,
|
||||
_IotMqtt_FreePacket,
|
||||
freePacket )
|
||||
#else /* if IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES == 1 */
|
||||
#define _getPacketTypeFunc( pSerializer ) _IotMqtt_GetPacketType
|
||||
#define _getRemainingLengthFunc( pSerializer ) _IotMqtt_GetRemainingLength
|
||||
#define _getConnackDeserializer( pSerializer ) _IotMqtt_DeserializeConnack
|
||||
#define _getPublishDeserializer( pSerializer ) _IotMqtt_DeserializePublish
|
||||
#define _getPubackDeserializer( pSerializer ) _IotMqtt_DeserializePuback
|
||||
#define _getSubackDeserializer( pSerializer ) _IotMqtt_DeserializeSuback
|
||||
#define _getUnsubackDeserializer( pSerializer ) _IotMqtt_DeserializeUnsuback
|
||||
#define _getPingrespDeserializer( pSerializer ) _IotMqtt_DeserializePingresp
|
||||
#define _getMqttPubackSerializer( pSerializer ) _IotMqtt_SerializePuback
|
||||
#define _getMqttFreePacketFunc( pSerializer ) _IotMqtt_FreePacket
|
||||
#endif /* if IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES == 1 */
|
||||
/** @endcond */
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
static bool _incomingPacketValid( uint8_t packetType )
|
||||
{
|
||||
bool status = true;
|
||||
|
||||
/* Check packet type. Mask out lower bits to ignore flags. */
|
||||
switch( packetType & 0xf0 )
|
||||
{
|
||||
/* Valid incoming packet types. */
|
||||
case MQTT_PACKET_TYPE_CONNACK:
|
||||
case MQTT_PACKET_TYPE_PUBLISH:
|
||||
case MQTT_PACKET_TYPE_PUBACK:
|
||||
case MQTT_PACKET_TYPE_SUBACK:
|
||||
case MQTT_PACKET_TYPE_UNSUBACK:
|
||||
case MQTT_PACKET_TYPE_PINGRESP:
|
||||
break;
|
||||
|
||||
/* Any other packet type is invalid. */
|
||||
default:
|
||||
status = false;
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
static IotMqttError_t _getIncomingPacket( void * pNetworkConnection,
|
||||
const _mqttConnection_t * pMqttConnection,
|
||||
_mqttPacket_t * pIncomingPacket )
|
||||
{
|
||||
IOT_FUNCTION_ENTRY( IotMqttError_t, IOT_MQTT_SUCCESS );
|
||||
size_t dataBytesRead = 0;
|
||||
|
||||
/* No buffer for remaining data should be allocated. */
|
||||
IotMqtt_Assert( pIncomingPacket->pRemainingData == NULL );
|
||||
IotMqtt_Assert( pIncomingPacket->remainingLength == 0 );
|
||||
|
||||
/* Read the packet type, which is the first byte available. */
|
||||
pIncomingPacket->type = _getPacketTypeFunc( pMqttConnection->pSerializer )( pNetworkConnection,
|
||||
pMqttConnection->pNetworkInterface );
|
||||
|
||||
/* Check that the incoming packet type is valid. */
|
||||
if( _incomingPacketValid( pIncomingPacket->type ) == false )
|
||||
{
|
||||
IotLogError( "(MQTT connection %p) Unknown packet type %02x received.",
|
||||
pMqttConnection,
|
||||
pIncomingPacket->type );
|
||||
|
||||
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_RESPONSE );
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
|
||||
/* Read the remaining length. */
|
||||
pIncomingPacket->remainingLength = _getRemainingLengthFunc( pMqttConnection->pSerializer )( pNetworkConnection,
|
||||
pMqttConnection->pNetworkInterface );
|
||||
|
||||
if( pIncomingPacket->remainingLength == MQTT_REMAINING_LENGTH_INVALID )
|
||||
{
|
||||
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_RESPONSE );
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
|
||||
/* Allocate a buffer for the remaining data and read the data. */
|
||||
if( pIncomingPacket->remainingLength > 0 )
|
||||
{
|
||||
pIncomingPacket->pRemainingData = IotMqtt_MallocMessage( pIncomingPacket->remainingLength );
|
||||
|
||||
if( pIncomingPacket->pRemainingData == NULL )
|
||||
{
|
||||
IotLogError( "(MQTT connection %p) Failed to allocate buffer of length "
|
||||
"%lu for incoming packet type %lu.",
|
||||
pMqttConnection,
|
||||
( unsigned long ) pIncomingPacket->remainingLength,
|
||||
( unsigned long ) pIncomingPacket->type );
|
||||
|
||||
_flushPacket( pNetworkConnection, pMqttConnection, pIncomingPacket->remainingLength );
|
||||
|
||||
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_NO_MEMORY );
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
|
||||
dataBytesRead = pMqttConnection->pNetworkInterface->receive( pNetworkConnection,
|
||||
pIncomingPacket->pRemainingData,
|
||||
pIncomingPacket->remainingLength );
|
||||
|
||||
if( dataBytesRead != pIncomingPacket->remainingLength )
|
||||
{
|
||||
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_RESPONSE );
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
|
||||
/* Clean up on error. */
|
||||
IOT_FUNCTION_CLEANUP_BEGIN();
|
||||
|
||||
if( status != IOT_MQTT_SUCCESS )
|
||||
{
|
||||
if( pIncomingPacket->pRemainingData != NULL )
|
||||
{
|
||||
IotMqtt_FreeMessage( pIncomingPacket->pRemainingData );
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
|
||||
IOT_FUNCTION_CLEANUP_END();
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
static IotMqttError_t _deserializeIncomingPacket( _mqttConnection_t * pMqttConnection,
|
||||
_mqttPacket_t * pIncomingPacket )
|
||||
{
|
||||
IotMqttError_t status = IOT_MQTT_STATUS_PENDING;
|
||||
_mqttOperation_t * pOperation = NULL;
|
||||
|
||||
/* A buffer for remaining data must be allocated if remaining length is not 0. */
|
||||
IotMqtt_Assert( ( pIncomingPacket->remainingLength > 0 ) ==
|
||||
( pIncomingPacket->pRemainingData != NULL ) );
|
||||
|
||||
/* Only valid packets should be given to this function. */
|
||||
IotMqtt_Assert( _incomingPacketValid( pIncomingPacket->type ) == true );
|
||||
|
||||
/* Mask out the low bits of packet type to ignore flags. */
|
||||
switch( ( pIncomingPacket->type & 0xf0 ) )
|
||||
{
|
||||
case MQTT_PACKET_TYPE_CONNACK:
|
||||
IotLogDebug( "(MQTT connection %p) CONNACK in data stream.", pMqttConnection );
|
||||
|
||||
/* Deserialize CONNACK and notify of result. */
|
||||
status = _getConnackDeserializer( pMqttConnection->pSerializer )( pIncomingPacket );
|
||||
|
||||
pOperation = _IotMqtt_FindOperation( pMqttConnection,
|
||||
IOT_MQTT_CONNECT,
|
||||
NULL );
|
||||
|
||||
if( pOperation != NULL )
|
||||
{
|
||||
pOperation->u.operation.status = status;
|
||||
_IotMqtt_Notify( pOperation );
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case MQTT_PACKET_TYPE_PUBLISH:
|
||||
IotLogDebug( "(MQTT connection %p) PUBLISH in data stream.", pMqttConnection );
|
||||
|
||||
/* Allocate memory to handle the incoming PUBLISH. */
|
||||
pOperation = IotMqtt_MallocOperation( sizeof( _mqttOperation_t ) );
|
||||
|
||||
if( pOperation == NULL )
|
||||
{
|
||||
IotLogWarn( "Failed to allocate memory for incoming PUBLISH." );
|
||||
status = IOT_MQTT_NO_MEMORY;
|
||||
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Set the members of the incoming PUBLISH operation. */
|
||||
( void ) memset( pOperation, 0x00, sizeof( _mqttOperation_t ) );
|
||||
pOperation->incomingPublish = true;
|
||||
pOperation->pMqttConnection = pMqttConnection;
|
||||
pIncomingPacket->u.pIncomingPublish = pOperation;
|
||||
}
|
||||
|
||||
/* Deserialize incoming PUBLISH. */
|
||||
status = _getPublishDeserializer( pMqttConnection->pSerializer )( pIncomingPacket );
|
||||
|
||||
if( status == IOT_MQTT_SUCCESS )
|
||||
{
|
||||
/* Send a PUBACK for QoS 1 PUBLISH. */
|
||||
if( pOperation->u.publish.publishInfo.qos == IOT_MQTT_QOS_1 )
|
||||
{
|
||||
_sendPuback( pMqttConnection, pIncomingPacket->packetIdentifier );
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
|
||||
/* Transfer ownership of the received MQTT packet to the PUBLISH operation. */
|
||||
pOperation->u.publish.pReceivedData = pIncomingPacket->pRemainingData;
|
||||
pIncomingPacket->pRemainingData = NULL;
|
||||
|
||||
/* Add the PUBLISH to the list of operations pending processing. */
|
||||
IotMutex_Lock( &( pMqttConnection->referencesMutex ) );
|
||||
IotListDouble_InsertHead( &( pMqttConnection->pendingProcessing ),
|
||||
&( pOperation->link ) );
|
||||
IotMutex_Unlock( &( pMqttConnection->referencesMutex ) );
|
||||
|
||||
/* Increment the MQTT connection reference count before scheduling an
|
||||
* incoming PUBLISH. */
|
||||
if( _IotMqtt_IncrementConnectionReferences( pMqttConnection ) == true )
|
||||
{
|
||||
/* Schedule PUBLISH for callback invocation. */
|
||||
status = _IotMqtt_ScheduleOperation( pOperation, _IotMqtt_ProcessIncomingPublish, 0 );
|
||||
}
|
||||
else
|
||||
{
|
||||
status = IOT_MQTT_NETWORK_ERROR;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
|
||||
/* Free PUBLISH operation on error. */
|
||||
if( status != IOT_MQTT_SUCCESS )
|
||||
{
|
||||
/* Check ownership of the received MQTT packet. */
|
||||
if( pOperation->u.publish.pReceivedData != NULL )
|
||||
{
|
||||
/* Retrieve the pointer MQTT packet pointer so it may be freed later. */
|
||||
IotMqtt_Assert( pIncomingPacket->pRemainingData == NULL );
|
||||
pIncomingPacket->pRemainingData = ( uint8_t * ) pOperation->u.publish.pReceivedData;
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
|
||||
/* Remove operation from pending processing list. */
|
||||
IotMutex_Lock( &( pMqttConnection->referencesMutex ) );
|
||||
|
||||
if( IotLink_IsLinked( &( pOperation->link ) ) == true )
|
||||
{
|
||||
IotListDouble_Remove( &( pOperation->link ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
|
||||
IotMutex_Unlock( &( pMqttConnection->referencesMutex ) );
|
||||
|
||||
IotMqtt_Assert( pOperation != NULL );
|
||||
IotMqtt_FreeOperation( pOperation );
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case MQTT_PACKET_TYPE_PUBACK:
|
||||
IotLogDebug( "(MQTT connection %p) PUBACK in data stream.", pMqttConnection );
|
||||
|
||||
/* Deserialize PUBACK and notify of result. */
|
||||
status = _getPubackDeserializer( pMqttConnection->pSerializer )( pIncomingPacket );
|
||||
|
||||
pOperation = _IotMqtt_FindOperation( pMqttConnection,
|
||||
IOT_MQTT_PUBLISH_TO_SERVER,
|
||||
&( pIncomingPacket->packetIdentifier ) );
|
||||
|
||||
if( pOperation != NULL )
|
||||
{
|
||||
pOperation->u.operation.status = status;
|
||||
_IotMqtt_Notify( pOperation );
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case MQTT_PACKET_TYPE_SUBACK:
|
||||
IotLogDebug( "(MQTT connection %p) SUBACK in data stream.", pMqttConnection );
|
||||
|
||||
/* Deserialize SUBACK and notify of result. */
|
||||
pIncomingPacket->u.pMqttConnection = pMqttConnection;
|
||||
|
||||
status = _getSubackDeserializer( pMqttConnection->pSerializer )( pIncomingPacket );
|
||||
|
||||
pOperation = _IotMqtt_FindOperation( pMqttConnection,
|
||||
IOT_MQTT_SUBSCRIBE,
|
||||
&( pIncomingPacket->packetIdentifier ) );
|
||||
|
||||
if( pOperation != NULL )
|
||||
{
|
||||
pOperation->u.operation.status = status;
|
||||
_IotMqtt_Notify( pOperation );
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case MQTT_PACKET_TYPE_UNSUBACK:
|
||||
IotLogDebug( "(MQTT connection %p) UNSUBACK in data stream.", pMqttConnection );
|
||||
|
||||
/* Deserialize UNSUBACK and notify of result. */
|
||||
status = _getUnsubackDeserializer( pMqttConnection->pSerializer )( pIncomingPacket );
|
||||
|
||||
pOperation = _IotMqtt_FindOperation( pMqttConnection,
|
||||
IOT_MQTT_UNSUBSCRIBE,
|
||||
&( pIncomingPacket->packetIdentifier ) );
|
||||
|
||||
if( pOperation != NULL )
|
||||
{
|
||||
pOperation->u.operation.status = status;
|
||||
_IotMqtt_Notify( pOperation );
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
/* The only remaining valid type is PINGRESP. */
|
||||
IotMqtt_Assert( ( pIncomingPacket->type & 0xf0 ) == MQTT_PACKET_TYPE_PINGRESP );
|
||||
|
||||
IotLogDebug( "(MQTT connection %p) PINGRESP in data stream.", pMqttConnection );
|
||||
|
||||
/* Deserialize PINGRESP. */
|
||||
status = _getPingrespDeserializer( pMqttConnection->pSerializer )( pIncomingPacket );
|
||||
|
||||
if( status == IOT_MQTT_SUCCESS )
|
||||
{
|
||||
if( Atomic_CompareAndSwap_u32( &( pMqttConnection->pingreq.u.operation.periodic.ping.failure ),
|
||||
0,
|
||||
1 ) == 1 )
|
||||
{
|
||||
IotLogDebug( "(MQTT connection %p) PINGRESP successfully parsed.",
|
||||
pMqttConnection );
|
||||
}
|
||||
else
|
||||
{
|
||||
IotLogWarn( "(MQTT connection %p) Unexpected PINGRESP received.",
|
||||
pMqttConnection );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if( status != IOT_MQTT_SUCCESS )
|
||||
{
|
||||
IotLogError( "(MQTT connection %p) Packet parser status %s.",
|
||||
pMqttConnection,
|
||||
IotMqtt_strerror( status ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
static void _sendPuback( _mqttConnection_t * pMqttConnection,
|
||||
uint16_t packetIdentifier )
|
||||
{
|
||||
IotMqttError_t status = IOT_MQTT_STATUS_PENDING;
|
||||
_mqttOperation_t * pPubackOperation = NULL;
|
||||
|
||||
IotLogDebug( "(MQTT connection %p) Sending PUBACK for received PUBLISH %hu.",
|
||||
pMqttConnection,
|
||||
packetIdentifier );
|
||||
|
||||
/* Create a PUBACK operation. */
|
||||
status = _IotMqtt_CreateOperation( pMqttConnection,
|
||||
0,
|
||||
NULL,
|
||||
&pPubackOperation );
|
||||
|
||||
if( status != IOT_MQTT_SUCCESS )
|
||||
{
|
||||
IOT_GOTO_CLEANUP();
|
||||
}
|
||||
|
||||
/* Set the operation type. */
|
||||
pPubackOperation->u.operation.type = IOT_MQTT_PUBACK;
|
||||
|
||||
/* Generate a PUBACK packet from the packet identifier. */
|
||||
status = _getMqttPubackSerializer( pMqttConnection->pSerializer )( packetIdentifier,
|
||||
&( pPubackOperation->u.operation.pMqttPacket ),
|
||||
&( pPubackOperation->u.operation.packetSize ) );
|
||||
|
||||
if( status != IOT_MQTT_SUCCESS )
|
||||
{
|
||||
IOT_GOTO_CLEANUP();
|
||||
}
|
||||
|
||||
/* Add the PUBACK operation to the send queue for network transmission. */
|
||||
status = _IotMqtt_ScheduleOperation( pPubackOperation,
|
||||
_IotMqtt_ProcessSend,
|
||||
0 );
|
||||
|
||||
if( status != IOT_MQTT_SUCCESS )
|
||||
{
|
||||
IotLogError( "(MQTT connection %p) Failed to enqueue PUBACK for sending.",
|
||||
pMqttConnection );
|
||||
|
||||
IOT_GOTO_CLEANUP();
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
|
||||
/* Clean up on error. */
|
||||
IOT_FUNCTION_CLEANUP_BEGIN();
|
||||
|
||||
if( status != IOT_MQTT_SUCCESS )
|
||||
{
|
||||
if( pPubackOperation != NULL )
|
||||
{
|
||||
_IotMqtt_DestroyOperation( pPubackOperation );
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
static void _flushPacket( void * pNetworkConnection,
|
||||
const _mqttConnection_t * pMqttConnection,
|
||||
size_t length )
|
||||
{
|
||||
size_t bytesFlushed = 0;
|
||||
uint8_t receivedByte = 0;
|
||||
|
||||
for( bytesFlushed = 0; bytesFlushed < length; bytesFlushed++ )
|
||||
{
|
||||
( void ) _IotMqtt_GetNextByte( pNetworkConnection,
|
||||
pMqttConnection->pNetworkInterface,
|
||||
&receivedByte );
|
||||
}
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
bool _IotMqtt_GetNextByte( void * pNetworkConnection,
|
||||
const IotNetworkInterface_t * pNetworkInterface,
|
||||
uint8_t * pIncomingByte )
|
||||
{
|
||||
bool status = false;
|
||||
uint8_t incomingByte = 0;
|
||||
size_t bytesReceived = 0;
|
||||
|
||||
/* Attempt to read 1 byte. */
|
||||
bytesReceived = pNetworkInterface->receive( pNetworkConnection,
|
||||
&incomingByte,
|
||||
1 );
|
||||
|
||||
/* Set the output parameter and return success if 1 byte was read. */
|
||||
if( bytesReceived == 1 )
|
||||
{
|
||||
*pIncomingByte = incomingByte;
|
||||
status = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Network receive must return 0 on failure. */
|
||||
IotMqtt_Assert( bytesReceived == 0 );
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
void _IotMqtt_CloseNetworkConnection( IotMqttDisconnectReason_t disconnectReason,
|
||||
_mqttConnection_t * pMqttConnection )
|
||||
{
|
||||
IotTaskPoolError_t taskPoolStatus = IOT_TASKPOOL_SUCCESS;
|
||||
IotNetworkError_t closeStatus = IOT_NETWORK_SUCCESS;
|
||||
IotMqttCallbackParam_t callbackParam = { .u.message = { 0 } };
|
||||
void * pNetworkConnection = NULL, * pDisconnectCallbackContext = NULL;
|
||||
|
||||
/* Disconnect callback function. */
|
||||
void ( * disconnectCallback )( void *,
|
||||
IotMqttCallbackParam_t * ) = NULL;
|
||||
|
||||
/* Network close function. */
|
||||
IotNetworkError_t ( * closeConnection) ( IotNetworkConnection_t ) = NULL;
|
||||
|
||||
/* Mark the MQTT connection as disconnected and the keep-alive as failed. */
|
||||
IotMutex_Lock( &( pMqttConnection->referencesMutex ) );
|
||||
pMqttConnection->disconnected = true;
|
||||
|
||||
if( pMqttConnection->pingreq.u.operation.periodic.ping.keepAliveMs != 0 )
|
||||
{
|
||||
/* Keep-alive must have a PINGREQ allocated. */
|
||||
IotMqtt_Assert( pMqttConnection->pingreq.u.operation.pMqttPacket != NULL );
|
||||
IotMqtt_Assert( pMqttConnection->pingreq.u.operation.packetSize != 0 );
|
||||
|
||||
/* PINGREQ provides a reference to the connection, so reference count must
|
||||
* be nonzero. */
|
||||
IotMqtt_Assert( pMqttConnection->references > 0 );
|
||||
|
||||
/* Attempt to cancel the keep-alive job. */
|
||||
taskPoolStatus = IotTaskPool_TryCancel( IOT_SYSTEM_TASKPOOL,
|
||||
pMqttConnection->pingreq.job,
|
||||
NULL );
|
||||
|
||||
/* Clean up keep-alive if its job was successfully canceled. Otherwise,
|
||||
* the executing keep-alive job will clean up itself. */
|
||||
if( taskPoolStatus == IOT_TASKPOOL_SUCCESS )
|
||||
{
|
||||
/* Free the packet */
|
||||
_getMqttFreePacketFunc( pMqttConnection->pSerializer )( pMqttConnection->pingreq.u.operation.pMqttPacket );
|
||||
|
||||
/* Clear data about the keep-alive. */
|
||||
pMqttConnection->pingreq.u.operation.periodic.ping.keepAliveMs = 0;
|
||||
pMqttConnection->pingreq.u.operation.pMqttPacket = NULL;
|
||||
pMqttConnection->pingreq.u.operation.packetSize = 0;
|
||||
|
||||
/* Keep-alive is cleaned up; decrement reference count. Since this
|
||||
* function must be followed with a call to DISCONNECT, a check to
|
||||
* destroy the connection is not done here. */
|
||||
pMqttConnection->references--;
|
||||
|
||||
IotLogDebug( "(MQTT connection %p) Keep-alive job canceled and cleaned up.",
|
||||
pMqttConnection );
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
|
||||
/* Copy the function pointers and contexts, as the MQTT connection may be
|
||||
* modified after the mutex is released. */
|
||||
disconnectCallback = pMqttConnection->disconnectCallback.function;
|
||||
pDisconnectCallbackContext = pMqttConnection->disconnectCallback.pCallbackContext;
|
||||
|
||||
closeConnection = pMqttConnection->pNetworkInterface->close;
|
||||
pNetworkConnection = pMqttConnection->pNetworkConnection;
|
||||
|
||||
IotMutex_Unlock( &( pMqttConnection->referencesMutex ) );
|
||||
|
||||
/* Close the network connection. */
|
||||
if( closeConnection != NULL )
|
||||
{
|
||||
closeStatus = closeConnection( pNetworkConnection );
|
||||
|
||||
if( closeStatus == IOT_NETWORK_SUCCESS )
|
||||
{
|
||||
IotLogInfo( "(MQTT connection %p) Network connection closed.", pMqttConnection );
|
||||
}
|
||||
else
|
||||
{
|
||||
IotLogWarn( "(MQTT connection %p) Failed to close network connection, error %d.",
|
||||
pMqttConnection,
|
||||
closeStatus );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
IotLogWarn( "(MQTT connection %p) No network close function was set. Network connection"
|
||||
" not closed.", pMqttConnection );
|
||||
}
|
||||
|
||||
/* Invoke the disconnect callback. */
|
||||
if( disconnectCallback != NULL )
|
||||
{
|
||||
/* Set the members of the callback parameter. */
|
||||
callbackParam.mqttConnection = pMqttConnection;
|
||||
callbackParam.u.disconnectReason = disconnectReason;
|
||||
|
||||
disconnectCallback( pDisconnectCallbackContext,
|
||||
&callbackParam );
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
void IotMqtt_ReceiveCallback( IotNetworkConnection_t pNetworkConnection,
|
||||
void * pReceiveContext )
|
||||
{
|
||||
IotMqttError_t status = IOT_MQTT_SUCCESS;
|
||||
_mqttPacket_t incomingPacket = { .u.pMqttConnection = NULL };
|
||||
|
||||
/* Cast context to correct type. */
|
||||
_mqttConnection_t * pMqttConnection = ( _mqttConnection_t * ) pReceiveContext;
|
||||
|
||||
/* Read an MQTT packet from the network. */
|
||||
status = _getIncomingPacket( pNetworkConnection,
|
||||
pMqttConnection,
|
||||
&incomingPacket );
|
||||
|
||||
if( status == IOT_MQTT_SUCCESS )
|
||||
{
|
||||
/* Deserialize the received packet. */
|
||||
status = _deserializeIncomingPacket( pMqttConnection,
|
||||
&incomingPacket );
|
||||
|
||||
/* Free any buffers allocated for the MQTT packet. */
|
||||
if( incomingPacket.pRemainingData != NULL )
|
||||
{
|
||||
IotMqtt_FreeMessage( incomingPacket.pRemainingData );
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
|
||||
/* Close the network connection on a bad response. */
|
||||
if( status == IOT_MQTT_BAD_RESPONSE )
|
||||
{
|
||||
IotLogError( "(MQTT connection %p) Error processing incoming data. Closing connection.",
|
||||
pMqttConnection );
|
||||
|
||||
_IotMqtt_CloseNetworkConnection( IOT_MQTT_BAD_PACKET_RECEIVED,
|
||||
pMqttConnection );
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
IotMqttError_t IotMqtt_GetIncomingMQTTPacketTypeAndLength( IotMqttPacketInfo_t * pIncomingPacket,
|
||||
IotMqttGetNextByte_t getNextByte,
|
||||
void * pNetworkConnection )
|
||||
{
|
||||
IotMqttError_t status = IOT_MQTT_SUCCESS;
|
||||
|
||||
/* Read the packet type, which is the first byte available. */
|
||||
if( getNextByte( pNetworkConnection, &( pIncomingPacket->type ) ) == IOT_MQTT_SUCCESS )
|
||||
{
|
||||
/* Check that the incoming packet type is valid. */
|
||||
if( _incomingPacketValid( pIncomingPacket->type ) == false )
|
||||
{
|
||||
IotLogError( "(MQTT connection) Unknown packet type %02x received.",
|
||||
pIncomingPacket->type );
|
||||
|
||||
status = IOT_MQTT_BAD_RESPONSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Read the remaining length. */
|
||||
pIncomingPacket->remainingLength = _IotMqtt_GetRemainingLength_Generic( pNetworkConnection,
|
||||
getNextByte );
|
||||
|
||||
if( pIncomingPacket->remainingLength == MQTT_REMAINING_LENGTH_INVALID )
|
||||
{
|
||||
status = IOT_MQTT_BAD_RESPONSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
status = IOT_MQTT_NETWORK_ERROR;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,645 @@
|
|||
/*
|
||||
* IoT MQTT V2.1.0
|
||||
* Copyright (C) 2018 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 iot_mqtt_subscription.c
|
||||
* @brief Implements functions that manage subscriptions for an MQTT connection.
|
||||
*/
|
||||
|
||||
/* The config header is always included first. */
|
||||
#include "iot_config.h"
|
||||
|
||||
/* Standard includes. */
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Error handling include. */
|
||||
#include "iot_error.h"
|
||||
|
||||
/* MQTT internal include. */
|
||||
#include "private/iot_mqtt_internal.h"
|
||||
|
||||
/* Platform layer includes. */
|
||||
#include "platform/iot_threads.h"
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* @brief First parameter to #_topicMatch.
|
||||
*/
|
||||
typedef struct _topicMatchParams
|
||||
{
|
||||
const char * pTopicName; /**< @brief The topic name to parse. */
|
||||
uint16_t topicNameLength; /**< @brief Length of #_topicMatchParams_t.pTopicName. */
|
||||
bool exactMatchOnly; /**< @brief Whether to allow wildcards or require exact matches. */
|
||||
} _topicMatchParams_t;
|
||||
|
||||
/**
|
||||
* @brief First parameter to #_packetMatch.
|
||||
*/
|
||||
typedef struct _packetMatchParams
|
||||
{
|
||||
uint16_t packetIdentifier; /**< Packet identifier to match. */
|
||||
int32_t order; /**< Order to match. Set to #MQTT_REMOVE_ALL_SUBSCRIPTIONS to ignore. */
|
||||
} _packetMatchParams_t;
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* @brief Matches a topic name (from a publish) with a topic filter (from a
|
||||
* subscription).
|
||||
*
|
||||
* @param[in] pSubscriptionLink Pointer to the link member of an #_mqttSubscription_t.
|
||||
* @param[in] pMatch Pointer to a #_topicMatchParams_t.
|
||||
*
|
||||
* @return `true` if the arguments match the subscription topic filter; `false`
|
||||
* otherwise.
|
||||
*/
|
||||
static bool _topicMatch( const IotLink_t * pSubscriptionLink,
|
||||
void * pMatch );
|
||||
|
||||
/**
|
||||
* @brief Matches a packet identifier and order.
|
||||
*
|
||||
* @param[in] pSubscriptionLink Pointer to the link member of an #_mqttSubscription_t.
|
||||
* @param[in] pMatch Pointer to a #_packetMatchParams_t.
|
||||
*
|
||||
* @return `true` if the arguments match the subscription's packet info; `false`
|
||||
* otherwise.
|
||||
*/
|
||||
static bool _packetMatch( const IotLink_t * pSubscriptionLink,
|
||||
void * pMatch );
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
static bool _topicMatch( const IotLink_t * pSubscriptionLink,
|
||||
void * pMatch )
|
||||
{
|
||||
IOT_FUNCTION_ENTRY( bool, false );
|
||||
uint16_t nameIndex = 0, filterIndex = 0;
|
||||
|
||||
/* Because this function is called from a container function, the given link
|
||||
* must never be NULL. */
|
||||
IotMqtt_Assert( pSubscriptionLink != NULL );
|
||||
|
||||
_mqttSubscription_t * pSubscription = IotLink_Container( _mqttSubscription_t,
|
||||
pSubscriptionLink,
|
||||
link );
|
||||
_topicMatchParams_t * pParam = ( _topicMatchParams_t * ) pMatch;
|
||||
|
||||
/* Extract the relevant strings and lengths from parameters. */
|
||||
const char * pTopicName = pParam->pTopicName;
|
||||
const char * pTopicFilter = pSubscription->pTopicFilter;
|
||||
const uint16_t topicNameLength = pParam->topicNameLength;
|
||||
const uint16_t topicFilterLength = pSubscription->topicFilterLength;
|
||||
|
||||
/* Check for an exact match. */
|
||||
if( topicNameLength == topicFilterLength )
|
||||
{
|
||||
status = ( strncmp( pTopicName, pTopicFilter, topicNameLength ) == 0 );
|
||||
|
||||
IOT_GOTO_CLEANUP();
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
|
||||
/* If the topic lengths are different but an exact match is required, return
|
||||
* false. */
|
||||
if( pParam->exactMatchOnly == true )
|
||||
{
|
||||
IOT_SET_AND_GOTO_CLEANUP( false );
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
|
||||
while( ( nameIndex < topicNameLength ) && ( filterIndex < topicFilterLength ) )
|
||||
{
|
||||
/* Check if the character in the topic name matches the corresponding
|
||||
* character in the topic filter string. */
|
||||
if( pTopicName[ nameIndex ] == pTopicFilter[ filterIndex ] )
|
||||
{
|
||||
/* Handle special corner cases as documented by the MQTT protocol spec. */
|
||||
|
||||
/* Filter "sport/#" also matches "sport" since # includes the parent level. */
|
||||
if( nameIndex == topicNameLength - 1 )
|
||||
{
|
||||
if( filterIndex == topicFilterLength - 3 )
|
||||
{
|
||||
if( pTopicFilter[ filterIndex + 1 ] == '/' )
|
||||
{
|
||||
if( pTopicFilter[ filterIndex + 2 ] == '#' )
|
||||
{
|
||||
IOT_SET_AND_GOTO_CLEANUP( true );
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
|
||||
/* Filter "sport/+" also matches the "sport/" but not "sport". */
|
||||
if( nameIndex == topicNameLength - 1 )
|
||||
{
|
||||
if( filterIndex == topicFilterLength - 2 )
|
||||
{
|
||||
if( pTopicFilter[ filterIndex + 1 ] == '+' )
|
||||
{
|
||||
IOT_SET_AND_GOTO_CLEANUP( true );
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Check for wildcards. */
|
||||
if( pTopicFilter[ filterIndex ] == '+' )
|
||||
{
|
||||
/* Move topic name index to the end of the current level.
|
||||
* This is identified by '/'. */
|
||||
while( nameIndex < topicNameLength && pTopicName[ nameIndex ] != '/' )
|
||||
{
|
||||
nameIndex++;
|
||||
}
|
||||
|
||||
/* Increment filter index to skip '/'. */
|
||||
filterIndex++;
|
||||
continue;
|
||||
}
|
||||
else if( pTopicFilter[ filterIndex ] == '#' )
|
||||
{
|
||||
/* Subsequent characters don't need to be checked if the for the
|
||||
* multi-level wildcard. */
|
||||
IOT_SET_AND_GOTO_CLEANUP( true );
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Any character mismatch other than '+' or '#' means the topic
|
||||
* name does not match the topic filter. */
|
||||
IOT_SET_AND_GOTO_CLEANUP( false );
|
||||
}
|
||||
}
|
||||
|
||||
/* Increment indexes. */
|
||||
nameIndex++;
|
||||
filterIndex++;
|
||||
}
|
||||
|
||||
/* If the end of both strings has been reached, they match. */
|
||||
if( ( nameIndex == topicNameLength ) && ( filterIndex == topicFilterLength ) )
|
||||
{
|
||||
IOT_SET_AND_GOTO_CLEANUP( true );
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
|
||||
IOT_FUNCTION_EXIT_NO_CLEANUP();
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
static bool _packetMatch( const IotLink_t * pSubscriptionLink,
|
||||
void * pMatch )
|
||||
{
|
||||
bool match = false;
|
||||
|
||||
/* Because this function is called from a container function, the given link
|
||||
* must never be NULL. */
|
||||
IotMqtt_Assert( pSubscriptionLink != NULL );
|
||||
|
||||
_mqttSubscription_t * pSubscription = IotLink_Container( _mqttSubscription_t,
|
||||
pSubscriptionLink,
|
||||
link );
|
||||
_packetMatchParams_t * pParam = ( _packetMatchParams_t * ) pMatch;
|
||||
|
||||
/* Compare packet identifiers. */
|
||||
if( pParam->packetIdentifier == pSubscription->packetInfo.identifier )
|
||||
{
|
||||
/* Compare orders if order is not MQTT_REMOVE_ALL_SUBSCRIPTIONS. */
|
||||
if( pParam->order == MQTT_REMOVE_ALL_SUBSCRIPTIONS )
|
||||
{
|
||||
match = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
match = ( ( size_t ) pParam->order ) == pSubscription->packetInfo.order;
|
||||
}
|
||||
}
|
||||
|
||||
/* If this subscription should be removed, check the reference count. */
|
||||
if( match == true )
|
||||
{
|
||||
/* Reference count must not be negative. */
|
||||
IotMqtt_Assert( pSubscription->references >= 0 );
|
||||
|
||||
/* If the reference count is positive, this subscription cannot be
|
||||
* removed yet because there are subscription callbacks using it. */
|
||||
if( pSubscription->references > 0 )
|
||||
{
|
||||
match = false;
|
||||
|
||||
/* Set the unsubscribed flag. The last active subscription callback
|
||||
* will remove and clean up this subscription. */
|
||||
pSubscription->unsubscribed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
|
||||
return match;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
IotMqttError_t _IotMqtt_AddSubscriptions( _mqttConnection_t * pMqttConnection,
|
||||
uint16_t subscribePacketIdentifier,
|
||||
const IotMqttSubscription_t * pSubscriptionList,
|
||||
size_t subscriptionCount )
|
||||
{
|
||||
IotMqttError_t status = IOT_MQTT_SUCCESS;
|
||||
size_t i = 0;
|
||||
_mqttSubscription_t * pNewSubscription = NULL;
|
||||
IotLink_t * pSubscriptionLink = NULL;
|
||||
_topicMatchParams_t topicMatchParams = { .exactMatchOnly = true };
|
||||
|
||||
IotMutex_Lock( &( pMqttConnection->subscriptionMutex ) );
|
||||
|
||||
for( i = 0; i < subscriptionCount; i++ )
|
||||
{
|
||||
/* Check if this topic filter is already registered. */
|
||||
topicMatchParams.pTopicName = pSubscriptionList[ i ].pTopicFilter;
|
||||
topicMatchParams.topicNameLength = pSubscriptionList[ i ].topicFilterLength;
|
||||
pSubscriptionLink = IotListDouble_FindFirstMatch( &( pMqttConnection->subscriptionList ),
|
||||
NULL,
|
||||
_topicMatch,
|
||||
&topicMatchParams );
|
||||
|
||||
if( pSubscriptionLink != NULL )
|
||||
{
|
||||
pNewSubscription = IotLink_Container( _mqttSubscription_t, pSubscriptionLink, link );
|
||||
|
||||
/* The lengths of exactly matching topic filters must match. */
|
||||
IotMqtt_Assert( pNewSubscription->topicFilterLength == pSubscriptionList[ i ].topicFilterLength );
|
||||
|
||||
/* Replace the callback and packet info with the new parameters. */
|
||||
pNewSubscription->callback = pSubscriptionList[ i ].callback;
|
||||
pNewSubscription->packetInfo.identifier = subscribePacketIdentifier;
|
||||
pNewSubscription->packetInfo.order = i;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Allocate memory for a new subscription. */
|
||||
pNewSubscription = IotMqtt_MallocSubscription( sizeof( _mqttSubscription_t ) +
|
||||
pSubscriptionList[ i ].topicFilterLength );
|
||||
|
||||
if( pNewSubscription == NULL )
|
||||
{
|
||||
status = IOT_MQTT_NO_MEMORY;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Clear the new subscription. */
|
||||
( void ) memset( pNewSubscription,
|
||||
0x00,
|
||||
sizeof( _mqttSubscription_t ) + pSubscriptionList[ i ].topicFilterLength );
|
||||
|
||||
/* Set the members of the new subscription and add it to the list. */
|
||||
pNewSubscription->packetInfo.identifier = subscribePacketIdentifier;
|
||||
pNewSubscription->packetInfo.order = i;
|
||||
pNewSubscription->callback = pSubscriptionList[ i ].callback;
|
||||
pNewSubscription->topicFilterLength = pSubscriptionList[ i ].topicFilterLength;
|
||||
( void ) memcpy( pNewSubscription->pTopicFilter,
|
||||
pSubscriptionList[ i ].pTopicFilter,
|
||||
( size_t ) ( pSubscriptionList[ i ].topicFilterLength ) );
|
||||
|
||||
IotListDouble_InsertHead( &( pMqttConnection->subscriptionList ),
|
||||
&( pNewSubscription->link ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IotMutex_Unlock( &( pMqttConnection->subscriptionMutex ) );
|
||||
|
||||
/* If memory allocation failed, remove all previously added subscriptions. */
|
||||
if( status != IOT_MQTT_SUCCESS )
|
||||
{
|
||||
_IotMqtt_RemoveSubscriptionByTopicFilter( pMqttConnection,
|
||||
pSubscriptionList,
|
||||
i );
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
void _IotMqtt_InvokeSubscriptionCallback( _mqttConnection_t * pMqttConnection,
|
||||
IotMqttCallbackParam_t * pCallbackParam )
|
||||
{
|
||||
_mqttSubscription_t * pSubscription = NULL;
|
||||
IotLink_t * pCurrentLink = NULL, * pNextLink = NULL;
|
||||
void * pCallbackContext = NULL;
|
||||
|
||||
void ( * callbackFunction )( void *,
|
||||
IotMqttCallbackParam_t * ) = NULL;
|
||||
_topicMatchParams_t topicMatchParams = { 0 };
|
||||
|
||||
/* Set the members of the search parameter. */
|
||||
topicMatchParams.pTopicName = pCallbackParam->u.message.info.pTopicName;
|
||||
topicMatchParams.topicNameLength = pCallbackParam->u.message.info.topicNameLength;
|
||||
topicMatchParams.exactMatchOnly = false;
|
||||
|
||||
/* Prevent any other thread from modifying the subscription list while this
|
||||
* function is searching. */
|
||||
IotMutex_Lock( &( pMqttConnection->subscriptionMutex ) );
|
||||
|
||||
/* Search the subscription list for all matching subscriptions starting at
|
||||
* the list head. */
|
||||
while( true )
|
||||
{
|
||||
pCurrentLink = IotListDouble_FindFirstMatch( &( pMqttConnection->subscriptionList ),
|
||||
pCurrentLink,
|
||||
_topicMatch,
|
||||
&topicMatchParams );
|
||||
|
||||
/* No subscription found. Exit loop. */
|
||||
if( pCurrentLink == NULL )
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
|
||||
/* Subscription found. Calculate pointer to subscription object. */
|
||||
pSubscription = IotLink_Container( _mqttSubscription_t, pCurrentLink, link );
|
||||
|
||||
/* Subscription validation should not have allowed a NULL callback function. */
|
||||
IotMqtt_Assert( pSubscription->callback.function != NULL );
|
||||
|
||||
/* Increment the subscription's reference count. */
|
||||
( pSubscription->references )++;
|
||||
|
||||
/* Copy the necessary members of the subscription before releasing the
|
||||
* subscription list mutex. */
|
||||
pCallbackContext = pSubscription->callback.pCallbackContext;
|
||||
callbackFunction = pSubscription->callback.function;
|
||||
|
||||
/* Unlock the subscription list mutex. */
|
||||
IotMutex_Unlock( &( pMqttConnection->subscriptionMutex ) );
|
||||
|
||||
/* Set the members of the callback parameter. */
|
||||
pCallbackParam->mqttConnection = pMqttConnection;
|
||||
pCallbackParam->u.message.pTopicFilter = pSubscription->pTopicFilter;
|
||||
pCallbackParam->u.message.topicFilterLength = pSubscription->topicFilterLength;
|
||||
|
||||
/* Invoke the subscription callback. */
|
||||
callbackFunction( pCallbackContext, pCallbackParam );
|
||||
|
||||
/* Lock the subscription list mutex to decrement the reference count. */
|
||||
IotMutex_Lock( &( pMqttConnection->subscriptionMutex ) );
|
||||
|
||||
/* Decrement the reference count. It must still be positive. */
|
||||
( pSubscription->references )--;
|
||||
IotMqtt_Assert( pSubscription->references >= 0 );
|
||||
|
||||
/* Save the pointer to the next link in case this subscription is freed. */
|
||||
pNextLink = pCurrentLink->pNext;
|
||||
|
||||
/* Remove this subscription if it has no references and the unsubscribed
|
||||
* flag is set. */
|
||||
if( pSubscription->unsubscribed == true )
|
||||
{
|
||||
/* An unsubscribed subscription should have been removed from the list. */
|
||||
IotMqtt_Assert( IotLink_IsLinked( &( pSubscription->link ) ) == false );
|
||||
|
||||
/* Free subscriptions with no references. */
|
||||
if( pSubscription->references == 0 )
|
||||
{
|
||||
IotMqtt_FreeSubscription( pSubscription );
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
|
||||
/* Move current link pointer. */
|
||||
pCurrentLink = pNextLink;
|
||||
}
|
||||
|
||||
IotMutex_Unlock( &( pMqttConnection->subscriptionMutex ) );
|
||||
|
||||
_IotMqtt_DecrementConnectionReferences( pMqttConnection );
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
void _IotMqtt_RemoveSubscriptionByPacket( _mqttConnection_t * pMqttConnection,
|
||||
uint16_t packetIdentifier,
|
||||
int32_t order )
|
||||
{
|
||||
_packetMatchParams_t packetMatchParams = { 0 };
|
||||
|
||||
/* Set the members of the search parameter. */
|
||||
packetMatchParams.packetIdentifier = packetIdentifier;
|
||||
packetMatchParams.order = order;
|
||||
|
||||
IotMutex_Lock( &( pMqttConnection->subscriptionMutex ) );
|
||||
IotListDouble_RemoveAllMatches( &( pMqttConnection->subscriptionList ),
|
||||
_packetMatch,
|
||||
( void * ) ( &packetMatchParams ),
|
||||
IotMqtt_FreeSubscription,
|
||||
offsetof( _mqttSubscription_t, link ) );
|
||||
IotMutex_Unlock( &( pMqttConnection->subscriptionMutex ) );
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
void _IotMqtt_RemoveSubscriptionByTopicFilter( _mqttConnection_t * pMqttConnection,
|
||||
const IotMqttSubscription_t * pSubscriptionList,
|
||||
size_t subscriptionCount )
|
||||
{
|
||||
size_t i = 0;
|
||||
_mqttSubscription_t * pSubscription = NULL;
|
||||
IotLink_t * pSubscriptionLink = NULL;
|
||||
_topicMatchParams_t topicMatchParams = { 0 };
|
||||
|
||||
/* Prevent any other thread from modifying the subscription list while this
|
||||
* function is running. */
|
||||
IotMutex_Lock( &( pMqttConnection->subscriptionMutex ) );
|
||||
|
||||
/* Find and remove each topic filter from the list. */
|
||||
for( i = 0; i < subscriptionCount; i++ )
|
||||
{
|
||||
topicMatchParams.pTopicName = pSubscriptionList[ i ].pTopicFilter;
|
||||
topicMatchParams.topicNameLength = pSubscriptionList[ i ].topicFilterLength;
|
||||
topicMatchParams.exactMatchOnly = true;
|
||||
|
||||
pSubscriptionLink = IotListDouble_FindFirstMatch( &( pMqttConnection->subscriptionList ),
|
||||
NULL,
|
||||
_topicMatch,
|
||||
&topicMatchParams );
|
||||
|
||||
if( pSubscriptionLink != NULL )
|
||||
{
|
||||
pSubscription = IotLink_Container( _mqttSubscription_t, pSubscriptionLink, link );
|
||||
|
||||
/* Reference count must not be negative. */
|
||||
IotMqtt_Assert( pSubscription->references >= 0 );
|
||||
|
||||
/* Remove subscription from list. */
|
||||
IotListDouble_Remove( pSubscriptionLink );
|
||||
|
||||
/* Check the reference count. This subscription cannot be removed if
|
||||
* there are subscription callbacks using it. */
|
||||
if( pSubscription->references > 0 )
|
||||
{
|
||||
/* Set the unsubscribed flag. The last active subscription callback
|
||||
* will remove and clean up this subscription. */
|
||||
pSubscription->unsubscribed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Free a subscription with no references. */
|
||||
IotMqtt_FreeSubscription( pSubscription );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
}
|
||||
|
||||
IotMutex_Unlock( &( pMqttConnection->subscriptionMutex ) );
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
bool IotMqtt_IsSubscribed( IotMqttConnection_t mqttConnection,
|
||||
const char * pTopicFilter,
|
||||
uint16_t topicFilterLength,
|
||||
IotMqttSubscription_t * const pCurrentSubscription )
|
||||
{
|
||||
bool status = false;
|
||||
_mqttSubscription_t * pSubscription = NULL;
|
||||
IotLink_t * pSubscriptionLink = NULL;
|
||||
_topicMatchParams_t topicMatchParams = { 0 };
|
||||
|
||||
/* Set the members of the search parameter. */
|
||||
topicMatchParams.pTopicName = pTopicFilter;
|
||||
topicMatchParams.topicNameLength = topicFilterLength;
|
||||
topicMatchParams.exactMatchOnly = true;
|
||||
|
||||
/* Prevent any other thread from modifying the subscription list while this
|
||||
* function is running. */
|
||||
IotMutex_Lock( &( mqttConnection->subscriptionMutex ) );
|
||||
|
||||
/* Search for a matching subscription. */
|
||||
pSubscriptionLink = IotListDouble_FindFirstMatch( &( mqttConnection->subscriptionList ),
|
||||
NULL,
|
||||
_topicMatch,
|
||||
&topicMatchParams );
|
||||
|
||||
/* Check if a matching subscription was found. */
|
||||
if( pSubscriptionLink != NULL )
|
||||
{
|
||||
pSubscription = IotLink_Container( _mqttSubscription_t, pSubscriptionLink, link );
|
||||
|
||||
/* Copy the matching subscription to the output parameter. */
|
||||
if( pCurrentSubscription != NULL )
|
||||
{
|
||||
pCurrentSubscription->pTopicFilter = pTopicFilter;
|
||||
pCurrentSubscription->topicFilterLength = topicFilterLength;
|
||||
pCurrentSubscription->qos = IOT_MQTT_QOS_0;
|
||||
pCurrentSubscription->callback = pSubscription->callback;
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
|
||||
status = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
|
||||
IotMutex_Unlock( &( mqttConnection->subscriptionMutex ) );
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/* Provide access to internal functions and variables if testing. */
|
||||
#if IOT_BUILD_TESTS == 1
|
||||
#include "iot_test_access_mqtt_subscription.c"
|
||||
#endif
|
||||
|
|
@ -0,0 +1,637 @@
|
|||
/*
|
||||
* IoT MQTT V2.1.0
|
||||
* Copyright (C) 2018 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 iot_mqtt_validate.c
|
||||
* @brief Implements functions that validate the structs of the MQTT library.
|
||||
*/
|
||||
|
||||
/* The config header is always included first. */
|
||||
#include "iot_config.h"
|
||||
|
||||
/* Error handling include. */
|
||||
#include "iot_error.h"
|
||||
|
||||
/* MQTT internal include. */
|
||||
#include "private/iot_mqtt_internal.h"
|
||||
|
||||
/**
|
||||
* @brief Check that an #IotMqttPublishInfo_t is valid.
|
||||
*
|
||||
* @param[in] awsIotMqttMode Specifies if this PUBLISH packet is being sent to
|
||||
* an AWS IoT MQTT server.
|
||||
* @param[in] maximumPayloadLength Maximum payload length.
|
||||
* @param[in] pPublishTypeDescription String describing the publish type.
|
||||
* @param[in] pPublishInfo The #IotMqttPublishInfo_t to validate.
|
||||
*
|
||||
* @return `true` if `pPublishInfo` is valid; `false` otherwise.
|
||||
*/
|
||||
static bool _validatePublish( bool awsIotMqttMode,
|
||||
size_t maximumPayloadLength,
|
||||
const char * pPublishTypeDescription,
|
||||
const IotMqttPublishInfo_t * pPublishInfo );
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
bool _IotMqtt_ValidateConnect( const IotMqttConnectInfo_t * pConnectInfo )
|
||||
{
|
||||
IOT_FUNCTION_ENTRY( bool, true );
|
||||
uint16_t maxClientIdLength = MQTT_SERVER_MAX_CLIENTID_LENGTH;
|
||||
bool enforceMaxClientIdLength = false;
|
||||
|
||||
/* Check for NULL. */
|
||||
if( pConnectInfo == NULL )
|
||||
{
|
||||
IotLogError( "MQTT connection information cannot be NULL." );
|
||||
|
||||
IOT_SET_AND_GOTO_CLEANUP( false );
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
|
||||
/* Check that a client identifier was set. */
|
||||
if( pConnectInfo->pClientIdentifier == NULL )
|
||||
{
|
||||
IotLogError( "Client identifier must be set." );
|
||||
|
||||
IOT_SET_AND_GOTO_CLEANUP( false );
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
|
||||
/* Check for a zero-length client identifier. Zero-length client identifiers
|
||||
* are not allowed with clean sessions. */
|
||||
if( pConnectInfo->clientIdentifierLength == 0 )
|
||||
{
|
||||
IotLogWarn( "A zero-length client identifier was provided." );
|
||||
|
||||
if( pConnectInfo->cleanSession == true )
|
||||
{
|
||||
IotLogError( "A zero-length client identifier cannot be used with a clean session." );
|
||||
|
||||
IOT_SET_AND_GOTO_CLEANUP( false );
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
|
||||
/* Check that the number of persistent session subscriptions is valid. */
|
||||
if( pConnectInfo->pPreviousSubscriptions != NULL )
|
||||
{
|
||||
if( _IotMqtt_ValidateSubscriptionList( IOT_MQTT_SUBSCRIBE,
|
||||
pConnectInfo->awsIotMqttMode,
|
||||
pConnectInfo->pPreviousSubscriptions,
|
||||
pConnectInfo->previousSubscriptionCount ) == false )
|
||||
{
|
||||
IOT_SET_AND_GOTO_CLEANUP( false );
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
|
||||
/* If will info is provided, check that it is valid. */
|
||||
if( pConnectInfo->pWillInfo != NULL )
|
||||
{
|
||||
if( _IotMqtt_ValidateLwtPublish( pConnectInfo->awsIotMqttMode,
|
||||
pConnectInfo->pWillInfo ) == false )
|
||||
{
|
||||
IOT_SET_AND_GOTO_CLEANUP( false );
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
|
||||
/* The AWS IoT MQTT service enforces a client ID length limit. */
|
||||
if( pConnectInfo->awsIotMqttMode == true )
|
||||
{
|
||||
maxClientIdLength = AWS_IOT_MQTT_SERVER_MAX_CLIENTID_LENGTH;
|
||||
enforceMaxClientIdLength = true;
|
||||
}
|
||||
|
||||
if( pConnectInfo->clientIdentifierLength > maxClientIdLength )
|
||||
{
|
||||
if( enforceMaxClientIdLength == false )
|
||||
{
|
||||
IotLogWarn( "A client identifier length of %hu is longer than %hu, "
|
||||
"which is "
|
||||
"the longest client identifier a server must accept.",
|
||||
pConnectInfo->clientIdentifierLength,
|
||||
maxClientIdLength );
|
||||
}
|
||||
else
|
||||
{
|
||||
IotLogError( "A client identifier length of %hu exceeds the "
|
||||
"maximum supported length of %hu.",
|
||||
pConnectInfo->clientIdentifierLength,
|
||||
maxClientIdLength );
|
||||
|
||||
IOT_SET_AND_GOTO_CLEANUP( false );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
|
||||
IOT_FUNCTION_EXIT_NO_CLEANUP();
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
static bool _validatePublish( bool awsIotMqttMode,
|
||||
size_t maximumPayloadLength,
|
||||
const char * pPublishTypeDescription,
|
||||
const IotMqttPublishInfo_t * pPublishInfo )
|
||||
{
|
||||
IOT_FUNCTION_ENTRY( bool, true );
|
||||
|
||||
/* Check for NULL. */
|
||||
if( pPublishInfo == NULL )
|
||||
{
|
||||
IotLogError( "Publish information cannot be NULL." );
|
||||
|
||||
IOT_SET_AND_GOTO_CLEANUP( false );
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
|
||||
/* Check topic name for NULL or zero-length. */
|
||||
if( pPublishInfo->pTopicName == NULL )
|
||||
{
|
||||
IotLogError( "Publish topic name must be set." );
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
|
||||
if( pPublishInfo->topicNameLength == 0 )
|
||||
{
|
||||
IotLogError( "Publish topic name length cannot be 0." );
|
||||
|
||||
IOT_SET_AND_GOTO_CLEANUP( false );
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
|
||||
if( pPublishInfo->payloadLength != 0 )
|
||||
{
|
||||
if( pPublishInfo->payloadLength > maximumPayloadLength )
|
||||
{
|
||||
IotLogError( "%s payload size of %zu exceeds maximum length of %zu.",
|
||||
pPublishTypeDescription,
|
||||
pPublishInfo->payloadLength,
|
||||
maximumPayloadLength );
|
||||
|
||||
IOT_SET_AND_GOTO_CLEANUP( false );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( pPublishInfo->pPayload == NULL )
|
||||
{
|
||||
IotLogError( "Nonzero payload length cannot have a NULL payload." );
|
||||
|
||||
IOT_SET_AND_GOTO_CLEANUP( false );
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
|
||||
/* Check for a valid QoS. */
|
||||
if( pPublishInfo->qos != IOT_MQTT_QOS_0 )
|
||||
{
|
||||
if( pPublishInfo->qos != IOT_MQTT_QOS_1 )
|
||||
{
|
||||
IotLogError( "Publish QoS must be either 0 or 1." );
|
||||
|
||||
IOT_SET_AND_GOTO_CLEANUP( false );
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
|
||||
/* Check the retry parameters. */
|
||||
if( pPublishInfo->retryLimit > 0 )
|
||||
{
|
||||
if( pPublishInfo->retryMs == 0 )
|
||||
{
|
||||
IotLogError( "Publish retry time must be positive." );
|
||||
|
||||
IOT_SET_AND_GOTO_CLEANUP( false );
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
|
||||
/* Check for compatibility with AWS IoT MQTT server. */
|
||||
if( awsIotMqttMode == true )
|
||||
{
|
||||
/* Check for retained message. */
|
||||
if( pPublishInfo->retain == true )
|
||||
{
|
||||
IotLogError( "AWS IoT does not support retained publish messages." );
|
||||
|
||||
IOT_SET_AND_GOTO_CLEANUP( false );
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
|
||||
/* Check topic name length. */
|
||||
if( pPublishInfo->topicNameLength > AWS_IOT_MQTT_SERVER_MAX_TOPIC_LENGTH )
|
||||
{
|
||||
IotLogError( "AWS IoT does not support topic names longer than %d bytes.",
|
||||
AWS_IOT_MQTT_SERVER_MAX_TOPIC_LENGTH );
|
||||
|
||||
IOT_SET_AND_GOTO_CLEANUP( false );
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
|
||||
IOT_FUNCTION_EXIT_NO_CLEANUP();
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
bool _IotMqtt_ValidatePublish( bool awsIotMqttMode,
|
||||
const IotMqttPublishInfo_t * pPublishInfo )
|
||||
{
|
||||
size_t maximumPayloadLength = MQTT_SERVER_MAX_PUBLISH_PAYLOAD_LENGTH;
|
||||
|
||||
if( awsIotMqttMode == true )
|
||||
{
|
||||
maximumPayloadLength = AWS_IOT_MQTT_SERVER_MAX_PUBLISH_PAYLOAD_LENGTH;
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
|
||||
return _validatePublish( awsIotMqttMode,
|
||||
maximumPayloadLength,
|
||||
"Publish",
|
||||
pPublishInfo );
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
bool _IotMqtt_ValidateLwtPublish( bool awsIotMqttMode,
|
||||
const IotMqttPublishInfo_t * pLwtPublishInfo )
|
||||
{
|
||||
return _validatePublish( awsIotMqttMode,
|
||||
MQTT_SERVER_MAX_LWT_PAYLOAD_LENGTH,
|
||||
"LWT",
|
||||
pLwtPublishInfo );
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
bool _IotMqtt_ValidateOperation( IotMqttOperation_t operation )
|
||||
{
|
||||
IOT_FUNCTION_ENTRY( bool, true );
|
||||
|
||||
/* Check for NULL. */
|
||||
if( operation == NULL )
|
||||
{
|
||||
IotLogError( "Operation reference cannot be NULL." );
|
||||
|
||||
IOT_SET_AND_GOTO_CLEANUP( false );
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
|
||||
/* Check that reference is waitable. */
|
||||
if( ( operation->u.operation.flags & IOT_MQTT_FLAG_WAITABLE ) != IOT_MQTT_FLAG_WAITABLE )
|
||||
{
|
||||
IotLogError( "Operation is not waitable." );
|
||||
|
||||
IOT_SET_AND_GOTO_CLEANUP( false );
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
|
||||
IOT_FUNCTION_EXIT_NO_CLEANUP();
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
bool _IotMqtt_ValidateSubscriptionList( IotMqttOperationType_t operation,
|
||||
bool awsIotMqttMode,
|
||||
const IotMqttSubscription_t * pListStart,
|
||||
size_t listSize )
|
||||
{
|
||||
IOT_FUNCTION_ENTRY( bool, true );
|
||||
size_t i = 0;
|
||||
uint16_t j = 0;
|
||||
const IotMqttSubscription_t * pListElement = NULL;
|
||||
|
||||
/* Operation must be either subscribe or unsubscribe. */
|
||||
IotMqtt_Assert( ( operation == IOT_MQTT_SUBSCRIBE ) ||
|
||||
( operation == IOT_MQTT_UNSUBSCRIBE ) );
|
||||
|
||||
/* Check for empty list. */
|
||||
if( pListStart == NULL )
|
||||
{
|
||||
IotLogError( "Subscription list pointer cannot be NULL." );
|
||||
|
||||
IOT_SET_AND_GOTO_CLEANUP( false );
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
|
||||
if( listSize == 0 )
|
||||
{
|
||||
IotLogError( "Empty subscription list." );
|
||||
|
||||
IOT_SET_AND_GOTO_CLEANUP( false );
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
|
||||
/* AWS IoT supports at most 8 topic filters in a single SUBSCRIBE packet. */
|
||||
if( awsIotMqttMode == true )
|
||||
{
|
||||
if( listSize > AWS_IOT_MQTT_SERVER_MAX_TOPIC_FILTERS_PER_SUBSCRIBE )
|
||||
{
|
||||
IotLogError( "AWS IoT does not support more than %d topic filters per "
|
||||
"subscription request.",
|
||||
AWS_IOT_MQTT_SERVER_MAX_TOPIC_FILTERS_PER_SUBSCRIBE );
|
||||
|
||||
IOT_SET_AND_GOTO_CLEANUP( false );
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
|
||||
for( i = 0; i < listSize; i++ )
|
||||
{
|
||||
pListElement = &( pListStart[ i ] );
|
||||
|
||||
/* Check for a valid QoS and callback function when subscribing. */
|
||||
if( operation == IOT_MQTT_SUBSCRIBE )
|
||||
{
|
||||
if( pListElement->qos != IOT_MQTT_QOS_0 )
|
||||
{
|
||||
if( pListElement->qos != IOT_MQTT_QOS_1 )
|
||||
{
|
||||
IotLogError( "Subscription QoS must be either 0 or 1." );
|
||||
|
||||
IOT_SET_AND_GOTO_CLEANUP( false );
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
|
||||
if( pListElement->callback.function == NULL )
|
||||
{
|
||||
IotLogError( "Callback function must be set." );
|
||||
|
||||
IOT_SET_AND_GOTO_CLEANUP( false );
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
|
||||
/* Check subscription topic filter. */
|
||||
if( pListElement->pTopicFilter == NULL )
|
||||
{
|
||||
IotLogError( "Subscription topic filter cannot be NULL." );
|
||||
|
||||
IOT_SET_AND_GOTO_CLEANUP( false );
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
|
||||
if( pListElement->topicFilterLength == 0 )
|
||||
{
|
||||
IotLogError( "Subscription topic filter length cannot be 0." );
|
||||
|
||||
IOT_SET_AND_GOTO_CLEANUP( false );
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
|
||||
/* Check for compatibility with AWS IoT MQTT server. */
|
||||
if( awsIotMqttMode == true )
|
||||
{
|
||||
/* Check topic filter length. */
|
||||
if( pListElement->topicFilterLength > AWS_IOT_MQTT_SERVER_MAX_TOPIC_LENGTH )
|
||||
{
|
||||
IotLogError( "AWS IoT does not support topic filters longer than %d bytes.",
|
||||
AWS_IOT_MQTT_SERVER_MAX_TOPIC_LENGTH );
|
||||
|
||||
IOT_SET_AND_GOTO_CLEANUP( false );
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
|
||||
/* Check that the wildcards '+' and '#' are being used correctly. */
|
||||
for( j = 0; j < pListElement->topicFilterLength; j++ )
|
||||
{
|
||||
switch( pListElement->pTopicFilter[ j ] )
|
||||
{
|
||||
/* Check that the single level wildcard '+' is used correctly. */
|
||||
case '+':
|
||||
|
||||
/* Unless '+' is the first character in the filter, it must be preceded by '/'. */
|
||||
if( j > 0 )
|
||||
{
|
||||
if( pListElement->pTopicFilter[ j - 1 ] != '/' )
|
||||
{
|
||||
IotLogError( "Invalid topic filter %.*s -- '+' must be preceded by '/'.",
|
||||
pListElement->topicFilterLength,
|
||||
pListElement->pTopicFilter );
|
||||
|
||||
IOT_SET_AND_GOTO_CLEANUP( false );
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
|
||||
/* Unless '+' is the last character in the filter, it must be succeeded by '/'. */
|
||||
if( j < pListElement->topicFilterLength - 1 )
|
||||
{
|
||||
if( pListElement->pTopicFilter[ j + 1 ] != '/' )
|
||||
{
|
||||
IotLogError( "Invalid topic filter %.*s -- '+' must be succeeded by '/'.",
|
||||
pListElement->topicFilterLength,
|
||||
pListElement->pTopicFilter );
|
||||
|
||||
IOT_SET_AND_GOTO_CLEANUP( false );
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
/* Check that the multi-level wildcard '#' is used correctly. */
|
||||
case '#':
|
||||
|
||||
/* '#' must be the last character in the filter. */
|
||||
if( j != pListElement->topicFilterLength - 1 )
|
||||
{
|
||||
IotLogError( "Invalid topic filter %.*s -- '#' must be the last character.",
|
||||
pListElement->topicFilterLength,
|
||||
pListElement->pTopicFilter );
|
||||
|
||||
IOT_SET_AND_GOTO_CLEANUP( false );
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
|
||||
/* Unless '#' is standalone, it must be preceded by '/'. */
|
||||
if( pListElement->topicFilterLength > 1 )
|
||||
{
|
||||
if( pListElement->pTopicFilter[ j - 1 ] != '/' )
|
||||
{
|
||||
IotLogError( "Invalid topic filter %.*s -- '#' must be preceded by '/'.",
|
||||
pListElement->topicFilterLength,
|
||||
pListElement->pTopicFilter );
|
||||
|
||||
IOT_SET_AND_GOTO_CLEANUP( false );
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EMPTY_ELSE_MARKER;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IOT_FUNCTION_EXIT_NO_CLEANUP();
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue