wolfssl-freertos/FreeRTOS-AWS/lib/greengrass/aws_helper_secure_connect.c

423 lines
13 KiB
C
Executable File

/*
* Amazon FreeRTOS Greengrass Discovery V1.0.2
* Copyright (C) 2017 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_helper_secure_connect.c
* @brief Secure connection wrapper
*
* API used by green grass discovery to help establishing
* secure connect with a given host
*/
/* SOCKETS includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
/* Helper interface includes. */
#include "aws_helper_secure_connect.h"
#include "aws_ggd_config.h"
#include "aws_ggd_config_defaults.h"
/* Standard includes. */
#include <string.h>
#define helperMAX_IP_ADDRESS_OCTETS 4u
/**
* @brief This function return non 0 if it is an IP and 0 if it isn't
*/
static uint32_t prvIsIPaddress( const char * pcIPAddress );
/*-----------------------------------------------------------*/
BaseType_t GGD_SecureConnect_Connect( const GGD_HostAddressData_t * pxHostAddressData,
Socket_t * pxSocket,
uint32_t ulReceiveTimeOut,
uint32_t ulSendTimeOut )
{
const TickType_t xReceiveTimeOut = pdMS_TO_TICKS( ulReceiveTimeOut );
const TickType_t xSendTimeOut = pdMS_TO_TICKS( ulSendTimeOut );
SocketsSockaddr_t xServerAddress;
size_t xURLLength;
BaseType_t xIsIPAddress;
BaseType_t xStatus;
configASSERT( pxHostAddressData != NULL );
configASSERT( pxSocket != NULL );
/* Calculate the length of the supplied URL. */
xURLLength = strlen( pxHostAddressData->pcHostAddress );
/* Ensure that the length of the specified URL is
* within the permitted limits. */
if( xURLLength <= ( size_t ) securesocketsMAX_DNS_NAME_LENGTH )
{
/* Create the socket. */
*pxSocket = SOCKETS_Socket( SOCKETS_AF_INET,
SOCKETS_SOCK_STREAM,
SOCKETS_IPPROTO_TCP );
if( *pxSocket == SOCKETS_INVALID_SOCKET )
{
xStatus = pdFAIL;
}
else
{
xStatus = pdPASS;
}
if( xStatus == pdPASS )
{
if( prvIsIPaddress( pxHostAddressData->pcHostAddress ) == ( uint32_t ) 0 )
{
xIsIPAddress = pdFALSE;
}
else
{
xIsIPAddress = pdTRUE;
}
xServerAddress.ucLength = sizeof( SocketsSockaddr_t );
xServerAddress.usPort = SOCKETS_htons( pxHostAddressData->usPort );
xServerAddress.ulAddress =
SOCKETS_GetHostByName( pxHostAddressData->pcHostAddress );
xServerAddress.ucSocketDomain = SOCKETS_AF_INET;
/* Set send timeout for the socket. */
( void ) SOCKETS_SetSockOpt( *pxSocket,
0,
SOCKETS_SO_SNDTIMEO,
&xSendTimeOut,
sizeof( xSendTimeOut ) );
/* Set receive timeout for the socket. */
( void ) SOCKETS_SetSockOpt( *pxSocket,
0,
SOCKETS_SO_RCVTIMEO,
&xReceiveTimeOut,
sizeof( xReceiveTimeOut ) );
/* Set secure connection. */
( void ) SOCKETS_SetSockOpt( *pxSocket,
0,
SOCKETS_SO_REQUIRE_TLS,
NULL,
( size_t ) 0 );
if( pxHostAddressData->pcCertificate != NULL )
{
if( SOCKETS_SetSockOpt( *pxSocket,
0,
SOCKETS_SO_TRUSTED_SERVER_CERTIFICATE,
pxHostAddressData->pcCertificate,
( size_t ) pxHostAddressData->ulCertificateSize )
!= SOCKETS_ERROR_NONE )
{
xStatus = pdFAIL;
}
}
if( xIsIPAddress == pdFALSE )
{
if( SOCKETS_SetSockOpt( *pxSocket,
0,
SOCKETS_SO_SERVER_NAME_INDICATION,
pxHostAddressData->pcHostAddress,
( size_t ) 1 + xURLLength )
!= SOCKETS_ERROR_NONE )
{
xStatus = pdFAIL;
}
}
/* Establish the TCP connection. */
if( pdPASS == xStatus )
{
if( ( SOCKETS_Connect( *pxSocket,
&xServerAddress,
( uint32_t ) sizeof( xServerAddress ) )
!= SOCKETS_ERROR_NONE ) )
{
GGD_SecureConnect_Disconnect( pxSocket );
xStatus = pdFAIL;
}
}
}
}
else
{
ggdconfigPRINT( "Malformed URL\r\n" );
xStatus = pdFAIL;
}
return xStatus;
}
/*-----------------------------------------------------------*/
void GGD_SecureConnect_Disconnect( Socket_t * pxSocket )
{
const TickType_t xShortDelay = pdMS_TO_TICKS( 10 );
TickType_t xTicksToWait = xShortDelay * ( TickType_t ) 100;
TimeOut_t xTimeOut;
char cBuf; /*lint !e971 can use char without signed/unsigned. */
configASSERT( pxSocket != NULL );
configASSERT( *pxSocket != SOCKETS_INVALID_SOCKET );
ggdconfigPRINT( "About to close socket.\r\n" );
/* Initialize xTimeOut. This records the time at which this function was
* entered. */
vTaskSetTimeOutState( &xTimeOut );
/* Shutdown the connection. */
( void ) SOCKETS_Shutdown( *pxSocket, ( uint32_t ) SOCKETS_SHUT_RDWR );
/* Wait for the socket to disconnect gracefully (indicated by a
* SOCKETS_EINVAL error) before closing the socket. */
while( SOCKETS_Recv( *pxSocket, &cBuf, sizeof( cBuf ), ( uint32_t ) 0 ) >= 0 )
{
vTaskDelay( xShortDelay );
if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) != pdFALSE )
{
/* Timed out before the wanted number of bytes were available, exit
* the loop. */
break;
}
}
/* Close the socket. */
( void ) SOCKETS_Close( *pxSocket );
*pxSocket = SOCKETS_INVALID_SOCKET;
ggdconfigPRINT( "Socket closed.\r\n" );
#if ( INCLUDE_uxTaskGetStackHighWaterMark == 1 )
{
configPRINTF( (
"Stack high watermark for discovery helper task: %u.\r\n",
uxTaskGetStackHighWaterMark( NULL ) ) );
}
#endif
}
/*-----------------------------------------------------------*/
BaseType_t GGD_SecureConnect_Send( const char * pcPayload, /*lint !e971 can use char without signed/unsigned. */
const uint32_t ulPayloadSize,
const Socket_t xSocket )
{
BaseType_t xStatus = pdFAIL;
uint32_t ulBytesSent = 0;
int32_t lSendRetVal;
configASSERT( pcPayload != NULL );
/* Keep re-trying until timeout or any error
* other than SOCKETS_EWOULDBLOCK occurs. */
if( ulPayloadSize > 0 )
{
while( ulBytesSent < ulPayloadSize )
{
/* Try sending the remaining data. */
lSendRetVal = SOCKETS_Send( xSocket,
( const unsigned char * ) &pcPayload[ ulBytesSent ],
( size_t ) ( ulPayloadSize - ulBytesSent ),
( uint32_t ) 0 );
/* A negative return value from SOCKETS_Send
* means some error occurred. */
if( lSendRetVal < 0 )
{
break;
}
else
{
/* Update the count of sent bytes. */
ulBytesSent += ( uint32_t ) lSendRetVal;
}
}
if( ulBytesSent == ulPayloadSize )
{
xStatus = pdPASS;
}
else
{
ggdconfigPRINT( "SecureConnect - error sending secure data to network\r\n" );
xStatus = pdFAIL;
}
}
else
{
xStatus = pdPASS;
}
return xStatus;
}
/*-----------------------------------------------------------*/
BaseType_t GGD_SecureConnect_Read( char * pcBuffer, /*lint !e971 can use char without signed/unsigned. */
const uint32_t ulBufferSize,
const Socket_t xSocket,
uint32_t * pulDataRecvSize )
{
int32_t lTmpStatus = 0;
BaseType_t xStatus = pdFAIL;
uint16_t usNbRetry;
configASSERT( pulDataRecvSize != NULL );
configASSERT( pcBuffer != NULL );
for( usNbRetry = 0; usNbRetry < ( uint16_t ) ggdconfigTCP_RECEIVE_RETRY; usNbRetry++ )
{
lTmpStatus = SOCKETS_Recv( xSocket, ( unsigned char * ) pcBuffer,
( uint32_t ) ulBufferSize,
( uint32_t ) 0 );
/* Check if it is a Timeout. */
if( lTmpStatus != 0 )
{
if( lTmpStatus < 0 )
{
ggdconfigPRINT( "SecureConnect - recv error, %d\r\n", lTmpStatus );
xStatus = pdFAIL;
}
else
{
xStatus = pdPASS;
}
/* If it is not a timeout, break. */
break;
}
else
{
/* It is a timeout, retry. */
ggdconfigPRINT( "SecureConnect - recv Timeout\r\n" );
}
}
if( usNbRetry == ( uint16_t ) ggdconfigTCP_RECEIVE_RETRY )
{
ggdconfigPRINT( "SecureConnect - recv number of Timeout exceeded\r\n" );
xStatus = pdFAIL;
}
*pulDataRecvSize = ( uint32_t ) lTmpStatus;
return xStatus;
}
/*-----------------------------------------------------------*/
static uint32_t prvIsIPaddress( const char * pcIPAddress )
{
const uint32_t ulDecimalBase = 10u;
uint8_t ucOctet[ helperMAX_IP_ADDRESS_OCTETS ];
const char * pcPointerOnEntering;
uint32_t ulReturn = 0UL, ulValue;
UBaseType_t uxOctetNumber;
BaseType_t xResult = pdPASS;
for( uxOctetNumber = 0u; uxOctetNumber < helperMAX_IP_ADDRESS_OCTETS; uxOctetNumber++ )
{
ulValue = 0UL;
pcPointerOnEntering = pcIPAddress;
while( ( *pcIPAddress >= '0' ) && ( *pcIPAddress <= '9' ) )
{
/* Move previous read characters into the next decimal
* position. */
ulValue *= ulDecimalBase;
/* Add the binary value of the ascii character. */
ulValue += ( ( uint32_t ) ( *pcIPAddress ) - ( uint32_t ) '0' );
/* Move to next character in the string. */
pcIPAddress++;
}
/* Check characters were read. */
if( pcIPAddress == pcPointerOnEntering )
{
xResult = pdFAIL;
}
/* Check the value fits in an 8-bit number. */
if( ulValue > 0xffUL )
{
xResult = pdFAIL;
}
else
{
ucOctet[ uxOctetNumber ] = ( uint8_t ) ulValue;
/* Check the next character is as expected. */
if( uxOctetNumber < ( helperMAX_IP_ADDRESS_OCTETS - 1u ) )
{
if( *pcIPAddress != '.' )
{
xResult = pdFAIL;
}
else
{
/* Move past the dot. */
pcIPAddress++;
}
}
}
if( xResult == pdFAIL )
{
/* No point going on. */
break;
}
}
if( *pcIPAddress != ( char ) 0 )
{
/* Expected the end of the string. */
xResult = pdFAIL;
}
if( uxOctetNumber != helperMAX_IP_ADDRESS_OCTETS )
{
/* Didn't read enough octets. */
xResult = pdFAIL;
}
if( xResult == pdPASS )
{
ulReturn = SOCKETS_inet_addr_quick( ucOctet[ 0 ], ucOctet[ 1 ], ucOctet[ 2 ], ucOctet[ 3 ] );
}
return ulReturn;
}