/* * Amazon FreeRTOS Device Defender Agent V1.0.2 * 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. * * http://aws.amazon.com/freertos * http://www.FreeRTOS.org */ #include "aws_cbor.h" #include "aws_cbor_alloc.h" #include "aws_cbor_print.h" #include "aws_defender_internals.h" #include "FreeRTOS.h" #include "aws_clientcredential.h" #include "aws_mqtt_agent.h" #include "task.h" typedef enum { eDefenderFalse = 0, eDefenderTrue = 1, } DEFENDERBool_t; /* Marks that the agent has been asked to stop by the application. */ static DEFENDERBool_t xDefenderKill; /* Endpoint used for connecting to the AWS IoT Device Defender service. */ static char const * const pcDefenderMQTTEndpoint = clientcredentialMQTT_BROKER_ENDPOINT; /* Status of last report sent. */ static DefenderReportStatus_t eDefenderReportStatus; /* Set of agent states. */ static DefenderState_t ( * DEFENDER_States[ eDefenderStateCount ] ) ( void ) = { 0 }; /* The current state. */ static DefenderState_t eDefenderState; /* Period between reports. */ static uint32_t ulDelayPeriodSec = 300; /* Handle for the MQTT agent. */ static MQTTAgentHandle_t xDefenderMQTTAgent; /* Handle for the agent task. */ static TaskHandle_t xDefenderTaskHandle = NULL; /* Timeout period for MQTT connections. */ static TickType_t xMQTTTimeoutPeriodTicks = pdMS_TO_TICKS( 10U * 1000U ); /** * @brief Publishes metrics report to service * * @param[in] xReport The metrics report * * @return Returns true if error occurred, false (0) on success */ static DEFENDERBool_t prvPublishCborToDevDef( CBORHandle_t xReport ); /** * @brief Subscribes to the report accept topic * * The DD service sends a messsage on the accept topic when a report is received * and accepted. * * @return Returns true if error occurred, false (0) on success */ static DEFENDERBool_t prvSubscribeToAcceptCbor( void ); /** * @brief Subscribes to the report reject topic * * The DD service sends a messsage on the reject topic when a report is received * and rejected. * * @return Returns true if error occurred, false (0) on success */ static DEFENDERBool_t prvSubscribeToRejectCbor( void ); /** * @brief Device Defender Agent Task * * @param params Required for a FreeRTOS task, but is ignored by the * agent. */ static void prvAgentLoop( void * params ); /** * @brief Report success when message recieved on the accept topic * * @param pxPvPublishCallbackContext Not used * @param pxPublishData Not used * * @return Always returns false */ static MQTTBool_t xAcceptCallback( void * pxPvPublishCallbackContext, MQTTPublishData_t const * pxPublishData ); /** * @brief Report failure when message recieved on the reject topic * * @param pxPvPublishCallbackContext Not used * @param pxPublishData Not used * * @return Always returns false */ static MQTTBool_t prvRejectCallback( void * pxPvPublishCallbackContext, MQTTPublishData_t const * pxPublishData ); static DefenderState_t prvStateInit( void ); static DefenderState_t prvStateNewMQTT( void ); static DefenderState_t prvStateConnectMqtt( void ); static DefenderState_t prvStateDisconnectMqtt( void ); static DefenderState_t prvStateSubscribe( void ); static DefenderState_t prvStateCreateReport( void ); static DefenderState_t prvStateDeleteMqtt( void ); static DefenderState_t prvStateSleep( void ); DefenderState_t StateGet( void ) { return eDefenderState; } char * StateAsString( DefenderState_t eState ) { switch( eState ) { case eDefenderStateInit: return "eDefenderStateInit"; case eDefenderStateStarted: return "eDefenderStateStarted"; case eDefenderStateNewMqttFailed: return "eDefenderStateNewMqttFailed"; case eDefenderStateNewMqttSuccess: return "eDefenderStateNewMqttSuccess"; case eDefenderStateConnectMqttFailed: return "eDefenderStateConnectMqttFailed"; case eDefenderStateConnectMqttSuccess: return "eDefenderStateConnectMqttSuccess"; case eDefenderStateSubscribeMqttFailed: return "eDefenderStateSubscribeMqttFailed"; case eDefenderStateSubscribeMqttSuccess: return "eDefenderStateSubscribeMqttSuccess"; case eDefenderStateSubmitReportFailed: return "eDefenderStateSubmitReportFailed"; case eDefenderStateSubmitReportSuccess: return "eDefenderStateSubmitReportSuccess"; case eDefenderStateDisconnectFailed: return "eDefenderStateDisconnectFailed"; case eDefenderStateDisconnected: return "eDefenderStateDisconnected"; case eDefenderStateDeleteFailed: return "eDefenderStateDeleteFailed"; case eDefenderStateSleep: return "eDefenderStateSleep"; case eDefenderStateCount: return "eDefenderStateCount"; } return "Invalid State Value"; } static void DEFENDER_StateFunctionSet( void ) { /* clang-format off */ DEFENDER_States[ eDefenderStateInit ] = prvStateInit; DEFENDER_States[ eDefenderStateStarted ] = prvStateNewMQTT; DEFENDER_States[ eDefenderStateNewMqttFailed ] = prvStateSleep; DEFENDER_States[ eDefenderStateNewMqttSuccess ] = prvStateConnectMqtt; DEFENDER_States[ eDefenderStateConnectMqttFailed ] = prvStateDeleteMqtt; DEFENDER_States[ eDefenderStateConnectMqttSuccess ] = prvStateSubscribe; DEFENDER_States[ eDefenderStateSubscribeMqttFailed ] = prvStateDisconnectMqtt; DEFENDER_States[ eDefenderStateSubscribeMqttSuccess ] = prvStateCreateReport; DEFENDER_States[ eDefenderStateSubmitReportFailed ] = prvStateDisconnectMqtt; DEFENDER_States[ eDefenderStateSubmitReportSuccess ] = prvStateDisconnectMqtt; DEFENDER_States[ eDefenderStateDisconnectFailed ] = prvStateDisconnectMqtt; DEFENDER_States[ eDefenderStateDisconnected ] = prvStateDeleteMqtt; DEFENDER_States[ eDefenderStateDeleteFailed ] = prvStateDeleteMqtt; DEFENDER_States[ eDefenderStateSleep ] = prvStateSleep; /* clang-format on */ } static DefenderState_t prvStateInit( void ) { return eDefenderStateStarted; } static DefenderState_t prvStateNewMQTT( void ) { MQTTAgentReturnCode_t xCreateResult = MQTT_AGENT_Create( &xDefenderMQTTAgent ); if( eMQTTAgentSuccess != xCreateResult ) { return eDefenderStateNewMqttFailed; } return eDefenderStateNewMqttSuccess; } static DefenderState_t prvStateConnectMqtt( void ) { /*Assign MQTT connection parameters*/ MQTTAgentConnectParams_t xDefenderConParams = { .pcURL = NULL, .xFlags = mqttagentREQUIRE_TLS, .xURLIsIPAddress = pdFALSE, .usPort = clientcredentialMQTT_BROKER_PORT, .pucClientId = ( const uint8_t * ) clientcredentialIOT_THING_NAME, .usClientIdLength = 0, .xSecuredConnection = pdTRUE, .pvUserData = NULL, .pxCallback = NULL, .pcCertificate = NULL, .ulCertificateSize = 0, }; MQTTAgentReturnCode_t xConnectResult = 0; /* Initialize non-static field values. */ xDefenderConParams.pcURL = pcDefenderMQTTEndpoint; xDefenderConParams.usClientIdLength = ( uint16_t ) strlen( clientcredentialIOT_THING_NAME ); /*Connect to the endpoint*/ xConnectResult = MQTT_AGENT_Connect( xDefenderMQTTAgent, &xDefenderConParams, xMQTTTimeoutPeriodTicks ); if( eMQTTAgentSuccess != xConnectResult ) { return eDefenderStateConnectMqttFailed; } return eDefenderStateConnectMqttSuccess; } static DefenderState_t prvStateSubscribe( void ) { DEFENDERBool_t xError = eDefenderFalse; xError |= prvSubscribeToAcceptCbor(); xError |= prvSubscribeToRejectCbor(); if( xError ) { return eDefenderStateSubscribeMqttFailed; } return eDefenderStateSubscribeMqttSuccess; } static DEFENDERBool_t prvSubscribeToAcceptCbor( void ) { uint8_t * pucTopic = ( uint8_t * ) "$aws/things/" clientcredentialIOT_THING_NAME "/defender/metrics/cbor/accepted"; MQTTAgentSubscribeParams_t xSubParams = { .pucTopic = NULL, .usTopicLength = 0, .xQoS = eMQTTQoS0, .pvPublishCallbackContext = NULL, .pxPublishCallback = xAcceptCallback, }; MQTTAgentReturnCode_t xSubResult = 0; /* Initialize non-static field values. */ xSubParams.pucTopic = pucTopic; xSubParams.usTopicLength = ( uint16_t ) strlen( ( char * ) pucTopic ); xSubResult = MQTT_AGENT_Subscribe( xDefenderMQTTAgent, &xSubParams, xMQTTTimeoutPeriodTicks ); DEFENDERBool_t xError = eMQTTAgentSuccess != xSubResult; return xError; } static MQTTBool_t xAcceptCallback( void * pxPvPublishCallbackContext, MQTTPublishData_t const * pxPublishData ) { ( void ) pxPvPublishCallbackContext; ( void ) pxPublishData; eDefenderReportStatus = eDefenderRepSuccess; return eMQTTFalse; } static DEFENDERBool_t prvSubscribeToRejectCbor( void ) { uint8_t * pucTopic = ( uint8_t * ) "$aws/things/" clientcredentialIOT_THING_NAME "/defender/metrics/cbor/rejected"; MQTTAgentSubscribeParams_t xSubParams = { .pucTopic = NULL, .usTopicLength = 0, .xQoS = eMQTTQoS0, .pvPublishCallbackContext = NULL, .pxPublishCallback = prvRejectCallback, }; MQTTAgentReturnCode_t xSubResult = 0; /* Initialize non-static field values. */ xSubParams.pucTopic = pucTopic; xSubParams.usTopicLength = ( uint16_t ) strlen( ( char * ) pucTopic ); xSubResult = MQTT_AGENT_Subscribe( xDefenderMQTTAgent, &xSubParams, xMQTTTimeoutPeriodTicks ); DEFENDERBool_t xError = eMQTTAgentSuccess != xSubResult; return xError; } static MQTTBool_t prvRejectCallback( void * pxPvPublishCallbackContext, MQTTPublishData_t const * pxPublishData ) { ( void ) pxPvPublishCallbackContext; ( void ) pxPublishData; eDefenderReportStatus = eDefenderRepRejected; return eMQTTFalse; } static DefenderState_t prvStateCreateReport( void ) { CBORHandle_t xReport; xReport = CreateReport(); if( NULL == xReport ) { return eDefenderStateSubmitReportFailed; } DEFENDERBool_t xError = prvPublishCborToDevDef( xReport ); CBOR_Delete( &xReport ); /* Wait for ack from service */ vTaskDelay( pdMS_TO_TICKS( 10000 ) ); if( true == xError ) { return eDefenderStateSubmitReportFailed; } return eDefenderStateSubmitReportSuccess; } static DEFENDERBool_t prvPublishCborToDevDef( CBORHandle_t xReport ) { MQTTAgentPublishParams_t xPubRecParams = { .pucTopic = NULL, .usTopicLength = 0, .xQoS = eMQTTQoS0, .pvData = NULL, .ulDataLength = 0, }; uint8_t * pucTopic = ( uint8_t * ) "$aws/things/" clientcredentialIOT_THING_NAME "/defender/metrics/cbor"; uint8_t const * pucBuffer = CBOR_GetRawBuffer( xReport ); int32_t lBufLen = CBOR_GetBufferSize( xReport ); MQTTAgentReturnCode_t xPublishResult = 0; /* Initialize non-static field values. */ xPubRecParams.pucTopic = pucTopic; xPubRecParams.usTopicLength = ( uint16_t ) strlen( ( char * ) pucTopic ); xPubRecParams.pvData = pucBuffer; xPubRecParams.ulDataLength = lBufLen; xPublishResult = MQTT_AGENT_Publish( xDefenderMQTTAgent, &xPubRecParams, xMQTTTimeoutPeriodTicks ); if( eMQTTAgentSuccess != xPublishResult ) { eDefenderReportStatus = eDefenderRepNotSent; return true; } eDefenderReportStatus = eDefenderRepNoAck; return eDefenderFalse; } static DefenderState_t prvStateDisconnectMqtt( void ) { if( eMQTTAgentSuccess != MQTT_AGENT_Disconnect( xDefenderMQTTAgent, xMQTTTimeoutPeriodTicks ) ) { return eDefenderStateDisconnectFailed; } return eDefenderStateDisconnected; } static DefenderState_t prvStateDeleteMqtt( void ) { if( eMQTTAgentSuccess != MQTT_AGENT_Delete( xDefenderMQTTAgent ) ) { return eDefenderStateDeleteFailed; } return eDefenderStateSleep; } static DefenderState_t prvStateSleep( void ) { static TickType_t xWakeTick; TickType_t const xCurrentTick = xTaskGetTickCount(); TickType_t const xActivePeriod = ( xCurrentTick - xWakeTick ); uint32_t const ulPeriodMS = ( ulDelayPeriodSec * 1000 ); TickType_t const xPeriodTicks = pdMS_TO_TICKS( ulPeriodMS ); uint32_t ulSleepPeriod = ( xPeriodTicks - xActivePeriod ); if( ulSleepPeriod > ulDelayPeriodSec ) { ulSleepPeriod = 1; } vTaskDelay( ulSleepPeriod ); xWakeTick = xTaskGetTickCount(); return eDefenderStateStarted; } static DefenderState_t DEFENDER_StateFunction( DefenderState_t xCurrentState ) { if( xCurrentState == eDefenderStateInit ) { DEFENDER_StateFunctionSet(); } DefenderState_t xNextState = DEFENDER_States[ xCurrentState ](); return xNextState; } DefenderErr_t DEFENDER_ReportPeriodSet( uint32_t ulPeriodSec ) { ulDelayPeriodSec = ulPeriodSec; return eDefenderErrSuccess; } DefenderErr_t DEFENDER_ConnectionTimeoutSet( uint32_t ulTimeoutMs ) { xMQTTTimeoutPeriodTicks = pdMS_TO_TICKS( ulTimeoutMs ); return eDefenderErrSuccess; } DefenderReportStatus_t DEFENDER_ReportStatusGet( void ) { DefenderReportStatus_t xReportStatus; do { xReportStatus = eDefenderReportStatus; } while( xReportStatus != eDefenderReportStatus ); return xReportStatus; } DefenderErr_t DEFENDER_Start( void ) { if( NULL != xDefenderTaskHandle ) { return eDefenderErrAlreadyStarted; } xDefenderKill = eDefenderFalse; /* Returns pdTrue (1) on success. */ BaseType_t xSuccess = xTaskCreate( prvAgentLoop, "DD_Agent", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY, &xDefenderTaskHandle ); if( 1 != xSuccess ) { return eDefenderErrFailedToCreateTask; } return eDefenderErrSuccess; } DefenderErr_t DEFENDER_Stop( void ) { if( NULL == xDefenderTaskHandle ) { return eDefenderErrNotStarted; } xDefenderKill = true; return eDefenderErrSuccess; } static void prvAgentLoop( void * params ) { /* Void pointer is a required parameter for a FreeRTOS task */ /* Silence warning about unused parameters */ ( void ) params; for( ; ; ) { /* Check if a kill was requested before the report was started */ if( xDefenderKill ) { break; /* TODO: Appropriately clean up resources on kill request */ } eDefenderState = DEFENDER_StateFunction( eDefenderState ); int32_t lStatePeriodMS = 5; vTaskDelay( pdMS_TO_TICKS( lStatePeriodMS ) ); } TaskHandle_t xTaskHandle = xDefenderTaskHandle; xDefenderTaskHandle = NULL; vTaskDelete( xTaskHandle ); } char const * DEFENDER_ErrAsString( DefenderErr_t eErrNum ) { /* *INDENT-OFF* */ switch( eErrNum ) { case eDefenderErrSuccess: return "eDefenderErrSuccess"; case eDefenderErrTooManyMetrics: return "eDefenderErrTooManyMetrics"; case eDefenderErrPeriodTooShort: return "eDefenderErrPeriodTooShort"; case eDefenderErrFailedToCreateTask: return "eDefenderErrFailedToCreateTask"; case eDefenderErrAlreadyStarted: return "eDefenderErrAlreadyStarted"; case eDefenderErrNotStarted: return "eDefenderErrNotStarted"; case eDefenderErrOther: return "eDefenderErrOther"; } /* *INDENT-ON* */ return "Invalid value"; } char const * DEFENDER_ReportStatusAsString( DefenderReportStatus_t eStatusNum ) { /* *INDENT-OFF* */ switch( eStatusNum ) { case eDefenderRepSuccess: return "eDefenderRepSuccess"; case eDefenderRepInit: return "eDefenderRepInit"; case eDefenderRepRejected: return "eDefenderRepRejected"; case eDefenderRepNoAck: return "eDefenderRepNoAck"; case eDefenderRepNotSent: return "eDefenderRepNotSent"; } /* *INDENT-ON* */ return "Invalid value"; }