/*
 * FreeRTOS Greengrass V2.0.1
 * Copyright (C) 2020 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
 */

/**
 * @file aws_greengrass_discovery.c
 * @brief API for Green grass Discovery. Provides API to Fetch and parse the
 * JSON file that contains the greenGrass code info.
 */


/* FreeRTOS includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"

/* Secure sockets includes. */
#include "iot_secure_sockets.h"

/* Greengrass includes. */
#include "aws_ggd_config.h"
#include "aws_ggd_config_defaults.h"
#include "aws_greengrass_discovery.h"
#include "aws_helper_secure_connect.h"
#include "jsmn.h"

/* Standard includes. */
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

/**
 * @brief Discovery: Strings for JSON file parsing.
 *
 * Below are JSON file sections used by the parsing tool to recover
 * the certificate and IP address.
 */
/** @{ */
#define ggdJSON_FILE_GROUPID         "GGGroupId"
#define ggdJSON_FILE_THING_ARN       "thingArn"
#define ggdJSON_FILE_HOST_ADDRESS    "HostAddress"
#define ggdJSON_FILE_CERTIFICATE     "CAs"
#define ggdJSON_FILE_PORT_NUMBER     "PortNumber"
/** @} */

/**
 * @brief HTTP command to retrieve JSON file from the Cloud.
 */
#define ggdCLOUD_DISCOVERY_ADDRESS      \
    "GET /greengrass/discover/thing/%s" \
    " HTTP/1.1\r\n\r\n"

#define ggJSON_CONVERTION_RADIX    10

/**
 * @brief HTTP field to get the length of the JSON file.
 *
 * The server respond with the JSON file encapsulated in
 * a HTTP header. The header is parsed bytes per bytes until
 * "content-length:" is found. then the length string is stored
 * into a temporary buffer of the ggJSON_PARSING_TMP_BUFFER_SIZE
 * size. Then a string to int conversion is performed to get the size
 * of the JSON file.
 */
/** @{ */
#define ggdHTTP_CONTENT_LENGTH_STRING     "content-length:"
#define ggJSON_PARSING_TMP_BUFFER_SIZE    10
/** @} */

/**
 * @brief Size of the IP address character string
 *
 * IP address is 15 character + the null termination.
 */
#define ggdJSON_IP_ADDRESS_SIZE    16

/**
 * @brief Loop back IP
 *
 * Discarded when Parsing JSON file as potential IP to connect to.
 */
#define ggdLOOP_BACK_IP            "127.0.0.1"

/**
 * @brief JSON parsing helper functions.
 *
 * The functions declared below are used to make parsing of JSON file a bit easier.
 * The code is broken down into tiny functions to limit code complexity and limit
 * duplicate code between auto connect and custom connect.
 */
/** @{ */
static BaseType_t prvGGDJsoneq( const char * pcJson,     /*lint !e971 can use char without signed/unsigned. */
                                const jsmntok_t * const pxTok,
                                const char * pcString ); /*lint !e971 can use char without signed/unsigned. */
static void prvCheckMatch( const char * pcJSONFile,      /*lint !e971 can use char without signed/unsigned. */
                           const jsmntok_t * pxTok,
                           const uint32_t ulTokenIndex,
                           BaseType_t * pxMatch,
                           const char * pcMatchCategory,   /*lint !e971 can use char without signuint32_t ulNbTokensed/unsigned. */
                           const char * pcMatchString,     /*lint !e971 can use char without signed/unsigned. */
                           const BaseType_t xAutoSelectFlag );
static BaseType_t prvGGDGetCertificate( char * pcJSONFile, /*lint !e971 can use char without signed/unsigned. */
                                        const HostParameters_t * pxHostParameters,
                                        const BaseType_t xAutoSelectFlag,
                                        const jsmntok_t * pxTok,
                                        const uint32_t ulNbTokens,
                                        GGD_HostAddressData_t * pxHostAddressData );
static BaseType_t prvGGDGetIPOnInterface( char * pcJSONFile, /*lint !e971 can use char without signed/unsigned. */
                                          const uint8_t ucTargetInterface,
                                          const jsmntok_t * pxTok,
                                          const uint32_t ulNbTokens,
                                          GGD_HostAddressData_t * pxHostAddressData,
                                          uint32_t * pulTokenIndex,
                                          uint8_t * pucCurrentInterface );
static BaseType_t prvGGDGetCore( const char * pcJSONFile, /*lint !e971 can use char without signed/unsigned. */
                                 const HostParameters_t * const pxHostParameters,
                                 const BaseType_t xAutoSelectFlag,
                                 const jsmntok_t * pxTok,
                                 const uint32_t ulNbTokens,
                                 uint32_t * pulTokenIndex );
static BaseType_t prvIsIPvalid( const char * pcIP,
                                uint32_t ulIPlength );
/** @} */

/**
 * @brief Search for length field in server HTTP response
 *
 * Small helper function that return true when the
 * length field is found in the html header.
 */
static BaseType_t prvCheckForContentLengthString( uint8_t * pucIndex,
                                                  const char cNewChar ); /*lint !e971 can use char without signed/unsigned. */

/*-----------------------------------------------------------*/

BaseType_t GGD_GetGGCIPandCertificate( const char * pcHostAddress,
                                       uint16_t usGGDPort,
                                       const char * pcThingName,
                                       char * pcBuffer, /*lint !e971 can use char without signed/unsigned. */
                                       const uint32_t ulBufferSize,
                                       GGD_HostAddressData_t * pxHostAddressData )
{
    Socket_t xSocket;
    uint32_t ulJSONFileSize = 0;
    BaseType_t xJSONFileRetrieveCompleted = pdFALSE;
    uint32_t ulByteRead = 0;
    BaseType_t xStatus;

    configASSERT( pxHostAddressData != NULL );
    configASSERT( pcBuffer != NULL );

    xStatus = GGD_JSONRequestStart( pcHostAddress, usGGDPort, pcThingName, &xSocket );

    if( xStatus == pdPASS )
    {
        xStatus = GGD_JSONRequestGetSize( &xSocket, &ulJSONFileSize );
    }

    if( xStatus == pdPASS )
    {
        /* Loop until the full JSON is retrieved. */
        do
        {
            xStatus = GGD_JSONRequestGetFile( &xSocket,
                                              &pcBuffer[ ulByteRead ],
                                              ulBufferSize - ulByteRead,
                                              &ulByteRead,
                                              &xJSONFileRetrieveCompleted,
                                              ulJSONFileSize ); /*lint !e644 ulJSONFileSize has been initialized if code reaches here. */
        }
        while( ( xStatus == pdPASS ) && ( xJSONFileRetrieveCompleted != pdTRUE ) && ( ulBufferSize - ulByteRead ) > 0 );

        /* If the JSON file was not completely received and there
         * is no space left in the buffer, it means that the buffer
         * is not large enough to hold the complete GreenGrass
         * Discovery document. The user should increase the size of
         * the buffer. */
        if( ( xJSONFileRetrieveCompleted == pdFALSE ) && ( ( ulBufferSize - ulByteRead ) == 0 ) )
        {
            ggdconfigPRINT( "[ERROR] The supplied buffer is not large enough to hold the GreenGrass discovery document. \r\n" );
            ggdconfigPRINT( "[ERROR] Consider increasing the size of the supplied buffer. \r\n" );
        }

        if( xSocket != SOCKETS_INVALID_SOCKET ) /* Check connection is closed. */
        {
            GGD_JSONRequestAbort( &xSocket );
            xStatus = pdFAIL;
        }

        if( xJSONFileRetrieveCompleted != pdTRUE )
        {
            xStatus = pdFAIL;
        }
    }

    if( xStatus == pdPASS )
    {
        xStatus = GGD_GetIPandCertificateFromJSON( pcBuffer,
                                                   ulJSONFileSize,
                                                   NULL,
                                                   pxHostAddressData,
                                                   pdTRUE );
    }

    return xStatus;
}
/*-----------------------------------------------------------*/

BaseType_t GGD_JSONRequestStart( const char * pcHostAddress,
                                 uint16_t usGGDPort,
                                 const char * pcThingName,
                                 Socket_t * pxSocket )
{
    GGD_HostAddressData_t xHostAddressData;
    char * pcHttpGetRequest = NULL;
    uint32_t ulHttpGetLength = 0;
    uint32_t ulCharsWritten = 0;
    BaseType_t xStatus;

    configASSERT( pcHostAddress != NULL );
    configASSERT( pcThingName != NULL );
    configASSERT( pxSocket != NULL );

    xHostAddressData.pcHostAddress = pcHostAddress; /*lint !e971 can use char without signed/unsigned. */
    xHostAddressData.pcCertificate = NULL;          /* Use default certificate. */
    xHostAddressData.ulCertificateSize = 0;
    xHostAddressData.usPort = usGGDPort;

    /* Establish secure connection. */
    xStatus = GGD_SecureConnect_Connect( &xHostAddressData,
                                         pxSocket,
                                         ggdconfigTCP_RECEIVE_TIMEOUT_MS,
                                         ggdconfigTCP_SEND_TIMEOUT_MS );

    if( xStatus == pdPASS )
    {
        /* Build the HTTP GET request string that is specific to this host. */
        ulHttpGetLength = 1 + strlen( ggdCLOUD_DISCOVERY_ADDRESS ) +
                          strlen( pcThingName );
        pcHttpGetRequest = pvPortMalloc( ulHttpGetLength );

        if( NULL == pcHttpGetRequest )
        {
            xStatus = pdFAIL;
        }
        else
        {
            ulCharsWritten = snprintf( pcHttpGetRequest,
                                       ulHttpGetLength,
                                       ggdCLOUD_DISCOVERY_ADDRESS,
                                       pcThingName );

            if( ulCharsWritten >= ulHttpGetLength )
            {
                xStatus = pdFAIL;
            }
            else
            {
                pcHttpGetRequest[ ulHttpGetLength - 1 ] = '\0';

                /* Send HTTP request over secure connection (HTTPS) to get the GGC JSON file. */
                xStatus = GGD_SecureConnect_Send( pcHttpGetRequest,
                                                  ulCharsWritten,
                                                  *pxSocket );

                if( xStatus == pdFAIL )
                {
                    /* Don't forget to close the connection. */
                    GGD_SecureConnect_Disconnect( pxSocket );
                    ggdconfigPRINT( "JSON request failed\r\n" );
                }
            }
        }
    }
    else
    {
        ggdconfigPRINT( "JSON request could not connect to end point\r\n" );
    }

    if( NULL != pcHttpGetRequest )
    {
        vPortFree( pcHttpGetRequest );
    }

    return xStatus;
}
/*-----------------------------------------------------------*/

BaseType_t GGD_JSONRequestGetSize( Socket_t * pxSocket,
                                   uint32_t * pulJSONFileSize )
{
    BaseType_t xStatus = pdFAIL;
    BaseType_t xReadStatus = pdFAIL;
    char cBuffer[ ggJSON_PARSING_TMP_BUFFER_SIZE ]; /*lint !e971 can use char without signed/unsigned. */
    char cReadChar;                                 /*lint !e971 can use char without signed/unsigned. */
    uint32_t ulReadSize;
    uint8_t ucContentLengthIndex = 0;
    uint8_t ucLengthStrIndex;


    /****** Parse the header until the begining of the content length string is found. *****/
    configASSERT( pxSocket != NULL );
    configASSERT( pulJSONFileSize != NULL );
    memset( cBuffer, 0, sizeof( cBuffer ) );

    do
    {
        xReadStatus = GGD_SecureConnect_Read( &cReadChar,
                                              ( uint32_t ) 1,
                                              *pxSocket,
                                              &ulReadSize );

        /** Check if we have found the end of header. */
        if( prvCheckForContentLengthString( &ucContentLengthIndex, cReadChar ) == pdTRUE )
        {
            xStatus = pdPASS;
            break;
        }
    }
    while( ( xReadStatus == pdPASS ) && ( ulReadSize == ( uint32_t ) 1 ) );

    if( xStatus == pdPASS )
    {
        /****** Get JSON file Size. *****/
        for( ucLengthStrIndex = ( uint8_t ) 0;
             ucLengthStrIndex < ( uint8_t ) ggJSON_PARSING_TMP_BUFFER_SIZE;
             ucLengthStrIndex++ )
        {
            xReadStatus = GGD_SecureConnect_Read( &cBuffer[ ucLengthStrIndex ],
                                                  ( uint32_t ) 1,
                                                  *pxSocket,
                                                  &ulReadSize );

            if( ( xReadStatus == pdFAIL ) || ( ulReadSize != ( uint32_t ) 1 ) )
            {
                ggdconfigPRINT( "JSON parsing could not get JSON file Size.\r\n" );
                xStatus = pdFAIL;
                break;
            }
        }
    }

    if( xStatus == pdPASS )
    {
        /* Add 1 because at the end of the JSON file the escape character '\0' will be added. */
        *pulJSONFileSize =
            ( uint32_t ) strtoul( cBuffer, NULL, ggJSON_CONVERTION_RADIX )
            + ( uint32_t ) 1; /*lint !e645 if code reaches here, cBuffer has been initalized. */

        /****** Go to the end of the header *****/
        xStatus = pdFAIL;

        do
        {
            xReadStatus = GGD_SecureConnect_Read( &cBuffer[ 3 ],
                                                  ( uint32_t ) 1,
                                                  *pxSocket,
                                                  &ulReadSize );

            /* Check if we have found the end of header. */
            if( ( cBuffer[ 0 ] == '\r' ) &&
                ( cBuffer[ 1 ] == '\n' ) &&
                ( cBuffer[ 2 ] == '\r' ) &&
                ( cBuffer[ 3 ] == '\n' ) )
            {
                xStatus = pdPASS;
                break;
            }

            cBuffer[ 0 ] = cBuffer[ 1 ];
            cBuffer[ 1 ] = cBuffer[ 2 ];
            cBuffer[ 2 ] = cBuffer[ 3 ];
        }
        while( ( xReadStatus == pdPASS ) && ( ulReadSize == ( uint32_t ) 1 ) );
    }

    if( xStatus == pdFAIL )
    {
        /* Don't forget to close the connection. */
        GGD_SecureConnect_Disconnect( pxSocket );
        ggdconfigPRINT( "JSON parsing failed\r\n" );
    }

    return xStatus;
}
/*-----------------------------------------------------------*/
BaseType_t GGD_JSONRequestGetFile( Socket_t * pxSocket,
                                   char * pcBuffer, /*lint !e971 can use char without signed/unsigned. */
                                   const uint32_t ulBufferSize,
                                   uint32_t * pulByteRead,
                                   BaseType_t * pxJSONFileRetrieveCompleted,
                                   const uint32_t ulJSONFileSize )
{
    BaseType_t xStatus;
    uint32_t ulDataSizeRead;

    configASSERT( pxSocket != NULL );
    configASSERT( pulByteRead != NULL );
    configASSERT( pxJSONFileRetrieveCompleted != NULL );
    configASSERT( pcBuffer != NULL );

    *pxJSONFileRetrieveCompleted = pdFALSE;

    xStatus = GGD_SecureConnect_Read( pcBuffer,
                                      ulBufferSize,
                                      *pxSocket,
                                      &ulDataSizeRead );

    if( xStatus == pdPASS )
    {
        *pulByteRead += ulDataSizeRead;

        /* We retrieved more than expected, this is failed. */
        if( *pulByteRead > ( ulJSONFileSize - ( uint32_t ) 1 ) )
        {
            ggdconfigPRINT( "JSON parsing - Received %ld, expected at most %ld \r\n",
                            *pulByteRead,
                            ulJSONFileSize - ( uint32_t ) 1 );
            xStatus = pdFAIL;
        }
    }

    if( xStatus == pdPASS )
    {
        /* We still have more to retrieve. */
        if( *pulByteRead < ( ulJSONFileSize - ( uint32_t ) 1 ) )
        {
            *pxJSONFileRetrieveCompleted = pdFALSE;
            ggdconfigPRINT( "JSON file retrieval incomplete, received %ld out of %ld bytes\r\n",
                            *pulByteRead,
                            ulJSONFileSize - ( uint32_t ) 1 );
        }
        else
        {
            /* Add the escape character. */
            pcBuffer[ ulJSONFileSize - ( uint32_t ) 1 ] = '\0';
            *pxJSONFileRetrieveCompleted = pdTRUE;
            ggdconfigPRINT( "JSON file retrieval completed\r\n" );

            /* Close the connection. */
            GGD_SecureConnect_Disconnect( pxSocket );
        }
    }
    else
    {
        ggdconfigPRINT( "JSON parsing - JSON file retrieval failed\r\n" );
        /* Don't forget to close the connection. */
        GGD_SecureConnect_Disconnect( pxSocket );
    }

    return xStatus;
}
/*-----------------------------------------------------------*/

void GGD_JSONRequestAbort( Socket_t * pxSocket )
{
    configASSERT( pxSocket != NULL );

    if( *pxSocket != SOCKETS_INVALID_SOCKET )
    {
        GGD_SecureConnect_Disconnect( pxSocket );
    }
}

/*-----------------------------------------------------------*/

BaseType_t GGD_GetIPandCertificateFromJSON( char * pcJSONFile, /*lint !e971 can use char without signed/unsigned. */
                                            const uint32_t ulJSONFileSize,
                                            const HostParameters_t * pxHostParameters,
                                            GGD_HostAddressData_t * pxHostAddressData,
                                            const BaseType_t xAutoSelectFlag )
{
    Socket_t xSocket;
    BaseType_t xStatus;
    jsmn_parser xParser;
    jsmntok_t pxTok[ ggdconfigJSON_MAX_TOKENS ];
    int32_t lNbTokens;
    uint32_t ulTokenIndex = 0;
    uint8_t ucCurrentInterface = 0, ucTargetInterface = 1;
    BaseType_t xFoundGGC = pdFALSE;
    BaseType_t xIsIPValid;

    configASSERT( pcJSONFile != NULL );
    configASSERT( pxHostAddressData != NULL );

    if( xAutoSelectFlag == pdFALSE )
    {
        configASSERT( pxHostParameters != NULL );
    }

    jsmn_init( &xParser );
    /* From jsmn, parse the JSON file. */
    lNbTokens = ( int32_t ) jsmn_parse( &xParser,
                                        pcJSONFile, /*lint !e971 can use char without signed/unsigned. */
                                        ( size_t ) ulJSONFileSize,
                                        pxTok,
                                        ( unsigned int ) ggdconfigJSON_MAX_TOKENS ); /*lint !e961 redundant casting only when int = int32_t. */

    /* Manage the case. */
    if( lNbTokens < 0 )
    {
        ggdconfigPRINT( "JSON parsing: Failed to parse JSON\r\n" );

        xStatus = pdFAIL;
    }
    else
    {
        xStatus = pdPASS;
    }

    if( xStatus == pdPASS )
    {
        /* Look for the green grass group certificate. */
        if( prvGGDGetCertificate( pcJSONFile,
                                  pxHostParameters,
                                  xAutoSelectFlag,
                                  pxTok,
                                  ( uint32_t ) lNbTokens, /*lint !e644 lNbTokens has been initialized if code reaches here. */
                                  pxHostAddressData ) == pdFAIL )
        {
            ggdconfigPRINT( "JSON parsing: Couldn't find certificate\r\n" );

            xStatus = pdFAIL;
        }
    }

    if( xStatus == pdPASS )
    {
        /* Look for the green grass core, green grass core position is returned in ulTokenIndex. */
        if( prvGGDGetCore( pcJSONFile,
                           pxHostParameters,
                           xAutoSelectFlag,
                           pxTok,
                           ( uint32_t ) lNbTokens,
                           &ulTokenIndex ) == pdFAIL )
        {
            ggdconfigPRINT( "JSON parsing: Couldn't find Green Grass Core\r\n" );

            xStatus = pdFAIL;
        }
    }

    if( xStatus == pdPASS )
    {
        /* If autoSelectFlag is set to true, the code below will try
         * to connect directly to the interface.
         * If autoSelectFlag is set to false, the code below will try
         * to connect to every interfaces. */
        if( xAutoSelectFlag == pdFALSE )
        {
            if( prvGGDGetIPOnInterface( pcJSONFile,
                                        pxHostParameters->ucInterface,
                                        pxTok,
                                        ( uint32_t ) lNbTokens,
                                        pxHostAddressData,
                                        &ulTokenIndex,
                                        &ucCurrentInterface ) == pdFAIL )
            {
                ggdconfigPRINT( "GGC - Can't find interface\r\n" );
            }
            else
            {
                xFoundGGC = pdTRUE;
            }
        }
        else
        {
            while( prvGGDGetIPOnInterface( pcJSONFile,
                                           ucTargetInterface,
                                           pxTok,
                                           ( uint32_t ) lNbTokens,
                                           pxHostAddressData,
                                           &ulTokenIndex,
                                           &ucCurrentInterface ) == pdPASS )
            {
                xIsIPValid = prvIsIPvalid( ( const char * ) pxHostAddressData->pcHostAddress,
                                           strlen( pxHostAddressData->pcHostAddress ) );

                if( xIsIPValid == pdTRUE )
                {
                    if( GGD_SecureConnect_Connect( pxHostAddressData,
                                                   &xSocket,
                                                   ggdconfigTCP_RECEIVE_TIMEOUT_MS,
                                                   ggdconfigTCP_SEND_TIMEOUT_MS )
                        == pdPASS )
                    {
                        xFoundGGC = pdTRUE;
                        /* Interface found, disconnect. */
                        GGD_SecureConnect_Disconnect( &xSocket );
                        break;
                    }
                }

                ucTargetInterface++;
            }
        }

        if( xFoundGGC != pdTRUE )
        {
            ggdconfigPRINT( "GGD - Can't connect to greengrass Core\r\n" );

            xStatus = pdFAIL;
        }
    }

    return xStatus;
}
/*-----------------------------------------------------------*/

/* Return true if the string " pcString" is found inside the token pxTok in JSON file pcJson. */
static BaseType_t prvGGDJsoneq( const char * pcJson,    /*lint !e971 can use char without signed/unsigned. */
                                const jsmntok_t * const pxTok,
                                const char * pcString ) /*lint !e971 can use char without signed/unsigned. */
{
    uint32_t ulStringSize = ( uint32_t ) pxTok->end - ( uint32_t ) pxTok->start;
    BaseType_t xStatus = pdFALSE;

    if( pxTok->type == JSMN_STRING )
    {
        if( ( uint32_t ) strlen( pcString ) == ulStringSize )
        {
            if( ( int16_t ) strncmp( &pcJson[ pxTok->start ],
                                     pcString,
                                     ulStringSize ) == 0 )
            {
                xStatus = pdTRUE;
            }
        }
    }

    return xStatus;
}
/*-----------------------------------------------------------*/


static void prvCheckMatch( const char * pcJSONFile, /*lint !e971 can use char without signed/unsigned. */
                           const jsmntok_t * pxTok,
                           const uint32_t ulTokenIndex,
                           BaseType_t * pxMatch,
                           const char * pcMatchCategory, /*lint !e971 can use char without signed/unsigned. */
                           const char * pcMatchString,   /*lint !e971 can use char without signed/unsigned. */
                           const BaseType_t xAutoSelectFlag )
{
    *pxMatch = prvGGDJsoneq( pcJSONFile, /*lint !e971 can use char without signed/unsigned. */
                             &pxTok[ ulTokenIndex ],
                             pcMatchCategory );

    if( *pxMatch == pdTRUE )
    {
        /* Is that the group we are looking for? */
        if( xAutoSelectFlag != pdTRUE )
        {
            if( ( prvGGDJsoneq( pcJSONFile, /*lint !e971 can use char without signed/unsigned. */
                                &pxTok[ ulTokenIndex + ( uint32_t ) 1 ],
                                pcMatchString ) == pdTRUE ) )
            {
                *pxMatch = pdTRUE;
            }
            else
            {
                *pxMatch = pdFALSE;
            }
        }
        else
        {
            *pxMatch = pdTRUE;
        }
    }
}
/*-----------------------------------------------------------*/

static BaseType_t prvGGDGetCore( const char * pcJSONFile, /*lint !e971 can use char without signed/unsigned. */
                                 const HostParameters_t * const pxHostParameters,
                                 const BaseType_t xAutoSelectFlag,
                                 const jsmntok_t * pxTok,
                                 const uint32_t ulNbTokens,
                                 uint32_t * pulTokenIndex )
{
    BaseType_t xStatus = pdFAIL;
    BaseType_t xMatchGroup = pdFALSE;
    BaseType_t xMatchCore = pdFALSE;

    /* Look for the green grass core inside the group. */
    for( *pulTokenIndex = 0; *pulTokenIndex < ulNbTokens; ( *pulTokenIndex )++ )
    {
        /* Check if group matches. */
        if( xAutoSelectFlag == pdTRUE )
        {
            xMatchGroup = pdTRUE;
            xMatchCore = pdTRUE;
        }
        else
        {
            if( xMatchGroup != pdTRUE )
            {
                prvCheckMatch( pcJSONFile,
                               pxTok,
                               *pulTokenIndex,
                               &xMatchGroup,
                               ggdJSON_FILE_GROUPID,
                               pxHostParameters->pcGroupName, /*lint !e971 can use char without signed/unsigned. */
                               xAutoSelectFlag );
            }
            else
            {
                /* Check if core matches. */
                prvCheckMatch( pcJSONFile,
                               pxTok,
                               *pulTokenIndex,
                               &xMatchCore,
                               ggdJSON_FILE_THING_ARN,
                               pxHostParameters->pcCoreAddress, /*lint !e971 can use char without signed/unsigned. */
                               xAutoSelectFlag );
            }
        }

        /* We have a match! correct core and correct group! */
        if( ( xMatchCore == pdTRUE ) && ( xMatchGroup == pdTRUE ) )
        {
            /* We have found the Core, now break and try to connect to the interface. */
            xStatus = pdPASS;
            break;
        }
    }

    /* Green grass core not found. */
    return xStatus;
}
/*-----------------------------------------------------------*/

static BaseType_t prvGGDGetCertificate( char * pcJSONFile, /*lint !e971 can use char without signed/unsigned. */
                                        const HostParameters_t * pxHostParameters,
                                        const BaseType_t xAutoSelectFlag,
                                        const jsmntok_t * pxTok,
                                        const uint32_t ulNbTokens,
                                        GGD_HostAddressData_t * pxHostAddressData )
{
    BaseType_t xMatchGroup = pdFALSE;
    uint32_t ulTokenIndex;
    uint32_t ulReadIndex = 0, ulWriteIndex = 0;
    BaseType_t xStatus = pdFAIL;

    /* Loop over all keys of the root object. */
    for( ulTokenIndex = 0; ulTokenIndex < ulNbTokens; ulTokenIndex++ )
    {
        /* Check if group matches. */
        if( xAutoSelectFlag == pdTRUE )
        {
            xMatchGroup = pdTRUE;
        }

        if( xMatchGroup != pdTRUE )
        {
            prvCheckMatch( pcJSONFile, pxTok,
                           ulTokenIndex,
                           &xMatchGroup,
                           ggdJSON_FILE_GROUPID,
                           pxHostParameters->pcGroupName,
                           xAutoSelectFlag );
        }
        else
        {
            if( prvGGDJsoneq( pcJSONFile, /*lint !e971 can use char without signed/unsigned. */
                              &pxTok[ ulTokenIndex ],
                              ggdJSON_FILE_CERTIFICATE ) == pdTRUE )
            {
                pxHostAddressData->pcCertificate =
                    &pcJSONFile[ pxTok[ ulTokenIndex + ( uint32_t ) 1 ].start ];
                /* Skip 2 brackets at the beginning that are not used in certificate. */
                pxHostAddressData->pcCertificate =
                    &pxHostAddressData->pcCertificate[ 2 ];

                /* Remove 2 that correspond to the skipped brackets. */
                pxHostAddressData->ulCertificateSize = ( ( uint32_t ) pxTok[ ulTokenIndex + ( uint32_t ) 1 ].end
                                                         - ( uint32_t ) pxTok[ ulTokenIndex + ( uint32_t ) 1 ].start )
                                                       - ( uint32_t ) 2;
                ulWriteIndex = 0;

                /* This section will convert the certificate from the JSON file into a certificate that can be
                 * given to the TLS service. For that all the \\ has to be replaced by \n. */
                ulReadIndex = 1;

                do
                {
                    if( ( pxHostAddressData->pcCertificate[ ulReadIndex - ( uint32_t ) 1 ] == '\\' ) &&
                        ( pxHostAddressData->pcCertificate[ ulReadIndex ] == 'n' ) )
                    {
                        pxHostAddressData->pcCertificate[ ulWriteIndex ] = '\n';
                        ulReadIndex++;
                    }
                    else
                    {
                        pxHostAddressData->pcCertificate[ ulWriteIndex ] =
                            pxHostAddressData->pcCertificate[ ulReadIndex - ( uint32_t ) 1 ];
                    }

                    ulReadIndex++;
                    ulWriteIndex++;
                }
                while( ulReadIndex < pxHostAddressData->ulCertificateSize );

                pxHostAddressData->ulCertificateSize = ulWriteIndex;
                pxHostAddressData->pcCertificate[ ulWriteIndex - ( uint32_t ) 1 ] = '\0';

                xStatus = pdPASS;
            }
        }
    }

    return xStatus;
}
/*-----------------------------------------------------------*/

static BaseType_t prvIsIPvalid( const char * pcIP,
                                uint32_t ulIPlength )
{
    BaseType_t xStatus;

    ( void ) ulIPlength;

    if( strcmp( ggdLOOP_BACK_IP, pcIP ) != 0 )
    {
        xStatus = pdTRUE;
    }
    else
    {
        xStatus = pdFALSE;
    }

    return xStatus;
}

/*-----------------------------------------------------------*/

static BaseType_t prvGGDGetIPOnInterface( char * pcJSONFile, /*lint !e971 can use char without signed/unsigned. */
                                          const uint8_t ucTargetInterface,
                                          const jsmntok_t * pxTok,
                                          const uint32_t ulNbTokens,
                                          GGD_HostAddressData_t * pxHostAddressData,
                                          uint32_t * pulTokenIndex,
                                          uint8_t * pucCurrentInterface )
{
    BaseType_t xStatus = pdFAIL;
    BaseType_t xFoundIP = pdFALSE;
    BaseType_t xFoundPort = pdFALSE;

    /* From the green grass core position. */
    for( ; *pulTokenIndex < ulNbTokens; ( *pulTokenIndex )++ )
    {
        if( prvGGDJsoneq( pcJSONFile, &pxTok[ *pulTokenIndex ], /*lint !e971 can use char without signed/unsigned. */
                          ggdJSON_FILE_HOST_ADDRESS ) == pdTRUE )
        {
            xFoundIP = pdTRUE;
            pxHostAddressData->pcHostAddress =
                &pcJSONFile[ pxTok[ *pulTokenIndex + ( uint32_t ) 1 ].start ]; /*lint !e971 can use char without signed/unsigned. */
            pcJSONFile[ pxTok[ *pulTokenIndex + ( uint32_t ) 1 ].end ] = '\0'; /* End with a null  character. */
        }

        if( prvGGDJsoneq( pcJSONFile, &pxTok[ *pulTokenIndex ], /*lint !e971 can use char without signed/unsigned. */
                          ggdJSON_FILE_PORT_NUMBER ) == pdTRUE )
        {
            pxHostAddressData->usPort = ( uint16_t ) strtoul( &pcJSONFile[ pxTok[ *pulTokenIndex + ( uint32_t ) 1 ].start ], NULL, ggJSON_CONVERTION_RADIX );
            xFoundPort = pdTRUE;
        }

        /* Get host IP address. */
        if( ( xFoundIP == pdTRUE ) && ( xFoundPort == pdTRUE ) )
        {
            xFoundIP = pdFALSE;
            xFoundPort = pdFALSE;
            ( *pucCurrentInterface )++;

            if( *pucCurrentInterface == ucTargetInterface )
            {
                xStatus = pdPASS;
                break;
            }
        }
    }

    ( *pulTokenIndex )++; /* Increase index to avoid avoid a match next time the function is called */
    return xStatus;
}
/*-----------------------------------------------------------*/

static BaseType_t prvCheckForContentLengthString( uint8_t * pucIndex,
                                                  const char cNewChar ) /*lint !e971 can use char without signed/unsigned. */
{
    BaseType_t xMatch = pdFALSE;

    if( cNewChar == ggdHTTP_CONTENT_LENGTH_STRING[ *pucIndex ] )
    {
        ( *pucIndex )++;

        if( *pucIndex == ( uint8_t ) ( sizeof( ggdHTTP_CONTENT_LENGTH_STRING ) - ( uint8_t ) 1 ) ) /*lint !e734 string length is not going to increase and is lower than 255 characters. */
        {
            xMatch = pdTRUE;
        }
    }
    else
    {
        *pucIndex = 0;
    }

    return xMatch;
}
/*-----------------------------------------------------------*/
/* Provide access to private members for testing. */
#ifdef AMAZON_FREERTOS_ENABLE_UNIT_TESTS
    #include "aws_greengrass_discovery_test_access_define.h"
#endif
