blob: 50ea1df0264dbf8f76bcc21d9a9831513c92c9e1 [file] [log] [blame]
/*
* Amazon FreeRTOS Platform V1.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.
*
* http://aws.amazon.com/freertos
* http://www.FreeRTOS.org
*/
/**
* @file iot_network_freertos.c
* @brief Implementation of the network-related functions from iot_network_freertos.h
* for FreeRTOS+TCP sockets.
*/
/* The config header is always included first. */
#include "iot_config.h"
/* Standard includes. */
#include <string.h>
/* FreeRTOS includes. */
#include "FreeRTOS.h"
#include "atomic.h"
#include "semphr.h"
/* FreeRTOS+TCP includes. */
#include "FreeRTOS_IP.h"
#include "FreeRTOS_Sockets.h"
/* FreeRTOS-IoT-Libraries includes. */
#include "iot_error.h"
#include "platform/iot_network_freertos.h"
#if ( IOT_NETWORK_ENABLE_TLS == 1 )
/* mbed TLS includes. */
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/entropy.h"
#include "mbedtls/ssl.h"
#include "mbedtls/threading.h"
#include "mbedtls/x509.h"
#endif
/* Configure logs for the functions in this file. */
#ifdef IOT_LOG_LEVEL_NETWORK
#define LIBRARY_LOG_LEVEL IOT_LOG_LEVEL_NETWORK
#else
#ifdef IOT_LOG_LEVEL_GLOBAL
#define LIBRARY_LOG_LEVEL IOT_LOG_LEVEL_GLOBAL
#else
#define LIBRARY_LOG_LEVEL IOT_LOG_NONE
#endif
#endif
#define LIBRARY_LOG_NAME ( "NET" )
#include "iot_logging_setup.h"
/* Provide a default value for socket timeout and network task parameters. */
#ifndef IOT_NETWORK_SOCKET_TIMEOUT_MS
#define IOT_NETWORK_SOCKET_TIMEOUT_MS ( 5000 )
#endif
#ifndef IOT_NETWORK_TASK_STACK_SIZE
#define IOT_NETWORK_TASK_STACK_SIZE ( 2048 )
#endif
#ifndef IOT_NETWORK_TASK_PRIORITY
#define IOT_NETWORK_TASK_PRIORITY ( tskIDLE_PRIORITY )
#endif
/* Maximum number of simultaneous socket receive callbacks. */
#ifndef IOT_NETWORK_MAX_RECEIVE_CALLBACKS
#define IOT_NETWORK_MAX_RECEIVE_CALLBACKS ( 2 )
#endif
/**
* @brief Maximum length of a DNS name.
*
* Per https://tools.ietf.org/html/rfc1035, 253 is the maximum string length
* of a DNS name.
*/
#define MAX_DNS_NAME_LENGTH ( 253 )
/*-----------------------------------------------------------*/
/**
* @brief Internal network context.
*/
typedef struct _networkConnection
{
Socket_t socket; /**< @brief FreeRTOS+TCP sockets handle. */
SemaphoreHandle_t socketMutex; /**< @brief Prevents concurrent threads from using a socket. */
StaticSemaphore_t socketMutexStorage; /**< @brief Storage space for socketMutex. */
IotNetworkReceiveCallback_t receiveCallback; /**< @brief Network receive callback, if any. */
void * pReceiveContext; /**< @brief The context for the receive callback. */
#if ( IOT_NETWORK_ENABLE_TLS == 1 )
BaseType_t secured; /**< @brief Flag that marks a connection as secured. */
/**
* @brief Secured connection context. Valid if `secured` is `pdTRUE`.
*/
struct
{
mbedtls_ssl_config config; /**< @brief SSL connection configuration. */
mbedtls_ssl_context context; /**< @brief SSL connection context */
mbedtls_x509_crt_profile certProfile; /**< @brief Certificate security profile for this connection. */
mbedtls_x509_crt rootCa; /**< @brief Root CA certificate context. */
mbedtls_x509_crt clientCert; /**< @brief Client certificate context. */
mbedtls_pk_context privKey; /**< @brief Client private key context. */
} ssl;
#endif /* if ( IOT_NETWORK_ENABLE_TLS == 1 ) */
} _networkConnection_t;
/*-----------------------------------------------------------*/
#if ( IOT_NETWORK_ENABLE_TLS == 1 )
/**
* @brief mbed TLS entropy context for generation of random numbers.
*/
static mbedtls_entropy_context _entropyContext;
/**
* @brief mbed TLS CTR DRBG context for generation of random numbers.
*/
static mbedtls_ctr_drbg_context _ctrDrgbContext;
#endif
/**
* @brief Handle of the network task.
*/
static TaskHandle_t _networkTaskHandle;
/**
* @brief Socket set for the network task.
*/
static SocketSet_t _socketSet;
/**
* @brief Connections in _socketSet.
*/
static _networkConnection_t * _connections[ IOT_NETWORK_MAX_RECEIVE_CALLBACKS ];
/**
* @brief An #IotNetworkInterface_t that uses the functions in this file.
*/
const IotNetworkInterface_t IotNetworkFreeRTOS =
{
.create = IotNetworkFreeRTOS_Create,
.setReceiveCallback = IotNetworkFreeRTOS_SetReceiveCallback,
.send = IotNetworkFreeRTOS_Send,
.receive = IotNetworkFreeRTOS_Receive,
.receiveUpto = IotNetworkFreeRTOS_ReceiveUpto,
.close = IotNetworkFreeRTOS_Close,
.destroy = IotNetworkFreeRTOS_Destroy
};
/*-----------------------------------------------------------*/
#if ( IOT_NETWORK_ENABLE_TLS == 1 )
/**
* @brief Initialize the mbed TLS structures in a network connection.
*
* @param[in] pNetworkConnection The network connection to initialize.
*/
static void _sslContextInit( _networkConnection_t * pNetworkConnection )
{
mbedtls_ssl_config_init( &( pNetworkConnection->ssl.config ) );
mbedtls_x509_crt_init( &( pNetworkConnection->ssl.rootCa ) );
mbedtls_pk_init( &( pNetworkConnection->ssl.privKey ) );
mbedtls_x509_crt_init( &( pNetworkConnection->ssl.clientCert ) );
mbedtls_ssl_init( &( pNetworkConnection->ssl.context ) );
}
/*-----------------------------------------------------------*/
/**
* @brief Free the mbed TLS structures in a network connection.
*
* @param[in] pNetworkConnection The network connection with the contexts to free.
*/
static void _sslContextFree( _networkConnection_t * pNetworkConnection )
{
mbedtls_ssl_free( &( pNetworkConnection->ssl.context ) );
mbedtls_x509_crt_free( &( pNetworkConnection->ssl.rootCa ) );
mbedtls_x509_crt_free( &( pNetworkConnection->ssl.clientCert ) );
mbedtls_pk_free( &( pNetworkConnection->ssl.privKey ) );
mbedtls_ssl_config_free( &( pNetworkConnection->ssl.config ) );
}
/*-----------------------------------------------------------*/
/**
* @brief Set up TLS on a TCP connection.
*
* @param[in] pNetworkConnection An established TCP connection.
* @param[in] pServerName Remote host name, used for server name indication.
* @param[in] pCredentials TLS setup parameters.
*
* @return #IOT_NETWORK_SUCCESS, #IOT_NETWORK_FAILURE, #IOT_NETWORK_NO_MEMORY,
* or #IOT_NETWORK_SYSTEM_ERROR.
*/
static IotNetworkError_t _tlsSetup( _networkConnection_t * pNetworkConnection,
const char * pServerName,
IotNetworkCredentials_t pCredentials )
{
IOT_FUNCTION_ENTRY( IotNetworkError_t, IOT_NETWORK_SUCCESS );
int mbedtlsError = 0;
/* Initialize the mbed TLS context structures. */
_sslContextInit( pNetworkConnection );
mbedtlsError = mbedtls_ssl_config_defaults( &( pNetworkConnection->ssl.config ),
MBEDTLS_SSL_IS_CLIENT,
MBEDTLS_SSL_TRANSPORT_STREAM,
MBEDTLS_SSL_PRESET_DEFAULT );
if( mbedtlsError != 0 )
{
IotLogError( "Failed to set default SSL configuration, error %d.", mbedtlsError );
/* Per mbed TLS docs, mbedtls_ssl_config_defaults only fails on memory allocation. */
IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_NO_MEMORY );
}
/* Set up the certificate security profile, starting from the default value. */
pNetworkConnection->ssl.certProfile = mbedtls_x509_crt_profile_default;
/* test.mosquitto.org only provides a 1024-bit RSA certificate, which is
* not acceptable by the default mbed TLS certificate security profile.
* For the purposes of this demo, allow the use of 1024-bit RSA certificates.
* This block should be removed otherwise. */
if( strncmp( pServerName, "test.mosquitto.org", strlen( pServerName ) ) == 0 )
{
pNetworkConnection->ssl.certProfile.rsa_min_bitlen = 1024;
}
/* Set SSL authmode and the RNG context. */
mbedtls_ssl_conf_authmode( &( pNetworkConnection->ssl.config ),
MBEDTLS_SSL_VERIFY_REQUIRED );
mbedtls_ssl_conf_rng( &( pNetworkConnection->ssl.config ),
mbedtls_ctr_drbg_random,
&_ctrDrgbContext );
mbedtls_ssl_conf_cert_profile( &( pNetworkConnection->ssl.config ),
&( pNetworkConnection->ssl.certProfile ) );
/* Parse the server root CA certificate into the SSL context. */
mbedtlsError = mbedtls_x509_crt_parse( &( pNetworkConnection->ssl.rootCa ),
( const unsigned char * ) pCredentials->pRootCa,
pCredentials->rootCaSize );
if( mbedtlsError != 0 )
{
IotLogError( "Failed to parse server root CA certificate, error %d.",
mbedtlsError );
IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_SYSTEM_ERROR );
}
mbedtls_ssl_conf_ca_chain( &( pNetworkConnection->ssl.config ),
&( pNetworkConnection->ssl.rootCa ),
NULL );
if( ( pCredentials->pPrivateKey != NULL ) && ( pCredentials->pClientCert != NULL ) )
{
/* Setup the client private key. */
mbedtlsError = mbedtls_pk_parse_key( &( pNetworkConnection->ssl.privKey ),
( const unsigned char * ) pCredentials->pPrivateKey,
pCredentials->privateKeySize,
0,
0 );
if( mbedtlsError != 0 )
{
IotLogError( "Failed to parse client certificate, error %d.",
mbedtlsError );
IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_SYSTEM_ERROR );
}
/* Setup the client certificate. */
mbedtlsError = mbedtls_x509_crt_parse( &( pNetworkConnection->ssl.clientCert ),
( const unsigned char * ) pCredentials->pClientCert,
pCredentials->clientCertSize );
if( mbedtlsError != 0 )
{
IotLogError( "Failed to parse the client private key, error %d.",
mbedtlsError );
IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_SYSTEM_ERROR );
}
mbedtls_ssl_conf_own_cert( &( pNetworkConnection->ssl.config ),
&( pNetworkConnection->ssl.clientCert ),
&( pNetworkConnection->ssl.privKey ) );
}
/* Initialize the mbed TLS secured connection context. */
mbedtlsError = mbedtls_ssl_setup( &( pNetworkConnection->ssl.context ),
&( pNetworkConnection->ssl.config ) );
if( mbedtlsError != 0 )
{
IotLogError( "Failed to set up mbed TLS SSL context, error %d.",
mbedtlsError );
IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_SYSTEM_ERROR );
}
/* Set the underlying IO for the TLS connection. */
mbedtls_ssl_set_bio( &( pNetworkConnection->ssl.context ),
pNetworkConnection->socket,
mbedtls_platform_send,
mbedtls_platform_recv,
NULL );
/* Enable SNI if requested. */
if( pCredentials->disableSni == false )
{
mbedtlsError = mbedtls_ssl_set_hostname( &( pNetworkConnection->ssl.context ),
pServerName );
if( mbedtlsError != 0 )
{
IotLogError( "Failed to set server name, error %d.", mbedtlsError );
IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_SYSTEM_ERROR );
}
}
/* Perform the TLS handshake. */
do
{
mbedtlsError = mbedtls_ssl_handshake( &( pNetworkConnection->ssl.context ) );
} while( ( mbedtlsError == MBEDTLS_ERR_SSL_WANT_READ ) ||
( mbedtlsError == MBEDTLS_ERR_SSL_WANT_WRITE ) );
if( mbedtlsError != 0 )
{
IotLogError( "Failed to perform TLS handshake, error %d.", mbedtlsError );
IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_FAILURE );
}
/* Clean up on error. */
IOT_FUNCTION_CLEANUP_BEGIN();
if( status != IOT_NETWORK_SUCCESS )
{
_sslContextFree( pNetworkConnection );
}
else
{
pNetworkConnection->secured = pdTRUE;
IotLogInfo( "(Network connection %p) TLS handshake successful.",
pNetworkConnection );
}
IOT_FUNCTION_CLEANUP_END();
}
#endif /* if ( IOT_NETWORK_ENABLE_TLS == 1 ) */
/*-----------------------------------------------------------*/
static void _networkTask( void * pvParameters )
{
_networkConnection_t * pConnection = NULL;
BaseType_t socketEvents = 0, i = 0, socketStatus = 0;
SocketSet_t socketSet = pvParameters;
while( true )
{
socketEvents = FreeRTOS_select( socketSet, IOT_NETWORK_SOCKET_TIMEOUT_MS );
if( socketEvents > 0 )
{
for( i = 0; i < IOT_NETWORK_MAX_RECEIVE_CALLBACKS; i++ )
{
pConnection = _connections[ i ];
if( pConnection != NULL )
{
socketStatus = FreeRTOS_FD_ISSET( pConnection->socket, socketSet );
if( socketStatus & eSELECT_READ )
{
/* A receive callback must be set; otherwise, select should not
* have returned this socket. */
configASSERT( pConnection->receiveCallback != NULL );
pConnection->receiveCallback( pConnection,
pConnection->pReceiveContext );
}
}
}
}
/* This task will receive a notification when cleanup is called. Exit when
* cleanup is called. */
if( ulTaskNotifyTake( pdTRUE, 0 ) != 0 )
{
break;
}
}
FreeRTOS_DeleteSocketSet( socketSet );
vTaskDelete( NULL );
}
/*-----------------------------------------------------------*/
IotNetworkError_t IotNetworkFreeRTOS_Init( void )
{
IOT_FUNCTION_ENTRY( IotNetworkError_t, IOT_NETWORK_SUCCESS );
#if ( IOT_NETWORK_ENABLE_TLS == 1 )
int mbedtlsError = 0;
/* Set the mutex functions for mbed TLS thread safety. */
mbedtls_threading_set_alt( mbedtls_platform_mutex_init,
mbedtls_platform_mutex_free,
mbedtls_platform_mutex_lock,
mbedtls_platform_mutex_unlock );
/* Initialize contexts for random number generation. */
mbedtls_entropy_init( &_entropyContext );
mbedtls_ctr_drbg_init( &_ctrDrgbContext );
/* Add a strong entropy source. At least one is required. */
mbedtlsError = mbedtls_entropy_add_source( &_entropyContext,
mbedtls_platform_entropy_poll,
NULL,
32,
MBEDTLS_ENTROPY_SOURCE_STRONG );
if( mbedtlsError != 0 )
{
IotLogError( "Failed to add entropy source, error %d.", mbedtlsError );
IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_FAILURE );
}
/* Seed the random number generator. */
mbedtlsError = mbedtls_ctr_drbg_seed( &_ctrDrgbContext,
mbedtls_entropy_func,
&_entropyContext,
NULL,
0 );
if( mbedtlsError != 0 )
{
IotLogError( "Failed to seed PRNG, error %d.", mbedtlsError );
IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_FAILURE );
}
#endif /* if ( IOT_NETWORK_ENABLE_TLS == 1 ) */
/* Create socket set for network task. */
_socketSet = FreeRTOS_CreateSocketSet();
if( _socketSet == NULL )
{
IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_FAILURE );
}
static StaticTask_t networkTask;
static StackType_t networkTaskStack[ IOT_NETWORK_TASK_STACK_SIZE ];
/* Create the network task. Since valid parameters are provided, this should
* never fail. */
_networkTaskHandle = xTaskCreateStatic( _networkTask,
"Network",
IOT_NETWORK_TASK_STACK_SIZE,
_socketSet,
IOT_NETWORK_TASK_PRIORITY,
( StackType_t * const ) &networkTaskStack,
&networkTask );
configASSERT( _networkTaskHandle != NULL );
IotLogInfo( "Network successfully initialized." );
IOT_FUNCTION_EXIT_NO_CLEANUP();
}
/*-----------------------------------------------------------*/
void IotNetworkFreeRTOS_Cleanup( void )
{
#if ( IOT_NETWORK_ENABLE_TLS == 1 )
/* Free the contexts for random number generation. */
mbedtls_ctr_drbg_free( &_ctrDrgbContext );
mbedtls_entropy_free( &_entropyContext );
/* Clear the mutex functions for mbed TLS thread safety. */
mbedtls_threading_free_alt();
#endif
xTaskNotifyGive( _networkTaskHandle );
IotLogInfo( "Network cleanup done." );
}
/*-----------------------------------------------------------*/
IotNetworkError_t IotNetworkFreeRTOS_Create( IotNetworkServerInfo_t pServerInfo,
IotNetworkCredentials_t pCredentialInfo,
IotNetworkConnection_t * pConnection )
{
IOT_FUNCTION_ENTRY( IotNetworkError_t, IOT_NETWORK_SUCCESS );
Socket_t tcpSocket = FREERTOS_INVALID_SOCKET;
BaseType_t socketStatus = 0;
struct freertos_sockaddr serverAddress = { 0 };
const TickType_t receiveTimeout = pdMS_TO_TICKS( IOT_NETWORK_SOCKET_TIMEOUT_MS );
_networkConnection_t * pNewNetworkConnection = NULL;
/* Credentials are not used if TLS is disabled. */
( void ) pCredentialInfo;
/* Check host name length against the maximum length allowed. */
const size_t hostnameLength = strlen( pServerInfo->pHostName );
if( hostnameLength > ( size_t ) MAX_DNS_NAME_LENGTH )
{
IotLogError( "Host name length exceeds %d, which is the maximum allowed.",
MAX_DNS_NAME_LENGTH );
IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_BAD_PARAMETER );
}
pNewNetworkConnection = pvPortMalloc( sizeof( _networkConnection_t ) );
if( pNewNetworkConnection == NULL )
{
IotLogError( "Failed to allocate memory for new network connection." );
IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_NO_MEMORY );
}
/* Clear the connection information. */
( void ) memset( pNewNetworkConnection, 0x00, sizeof( _networkConnection_t ) );
/* Create a new TCP socket. */
tcpSocket = FreeRTOS_socket( FREERTOS_AF_INET,
FREERTOS_SOCK_STREAM,
FREERTOS_IPPROTO_TCP );
if( tcpSocket == FREERTOS_INVALID_SOCKET )
{
IotLogError( "Failed to create new socket." );
IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_SYSTEM_ERROR );
}
/* Set the timeout for receive. */
socketStatus = FreeRTOS_setsockopt( tcpSocket,
0,
FREERTOS_SO_RCVTIMEO,
&receiveTimeout,
sizeof( TickType_t ) );
if( socketStatus != 0 )
{
IotLogError( "Failed to set socket receive timeout." );
IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_SYSTEM_ERROR );
}
/* Establish connection. */
serverAddress.sin_family = FREERTOS_AF_INET;
serverAddress.sin_port = FreeRTOS_htons( pServerInfo->port );
serverAddress.sin_addr = FreeRTOS_gethostbyname( pServerInfo->pHostName );
serverAddress.sin_len = ( uint8_t ) sizeof( serverAddress );
/* Check for errors from DNS lookup. */
if( serverAddress.sin_addr == 0 )
{
IotLogError( "Failed to resolve %s.", pServerInfo->pHostName );
IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_SYSTEM_ERROR );
}
socketStatus = FreeRTOS_connect( tcpSocket,
&serverAddress,
sizeof( serverAddress ) );
if( socketStatus != 0 )
{
IotLogError( "Failed to establish new connection. Socket status %d.", socketStatus );
IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_SYSTEM_ERROR );
}
/* Set the socket. */
pNewNetworkConnection->socket = tcpSocket;
#if ( IOT_NETWORK_ENABLE_TLS == 1 )
/* Set up TLS if credentials are provided. */
if( pCredentialInfo != NULL )
{
status = _tlsSetup( pNewNetworkConnection,
pServerInfo->pHostName,
pCredentialInfo );
}
#endif
IOT_FUNCTION_CLEANUP_BEGIN();
/* Clean up on failure. */
if( status != IOT_NETWORK_SUCCESS )
{
if( tcpSocket != FREERTOS_INVALID_SOCKET )
{
FreeRTOS_closesocket( tcpSocket );
}
/* Clear the connection information. */
if( pNewNetworkConnection != NULL )
{
vPortFree( pNewNetworkConnection );
}
}
else
{
/* Create the socket mutex. */
pNewNetworkConnection->socketMutex = xSemaphoreCreateMutexStatic( &( pNewNetworkConnection->socketMutexStorage ) );
/* Set the output parameter. */
*pConnection = pNewNetworkConnection;
IotLogInfo( "(Network connection %p) Connection to %s established.",
pNewNetworkConnection,
pServerInfo->pHostName );
}
IOT_FUNCTION_CLEANUP_END();
}
/*-----------------------------------------------------------*/
IotNetworkError_t IotNetworkFreeRTOS_SetReceiveCallback( IotNetworkConnection_t pConnection,
IotNetworkReceiveCallback_t receiveCallback,
void * pContext )
{
IotNetworkError_t status = IOT_NETWORK_SUCCESS;
BaseType_t i = 0;
/* Set the receive callback and context. */
pConnection->receiveCallback = receiveCallback;
pConnection->pReceiveContext = pContext;
/* Add this connection to the list of connections that select should check. */
for( i = 0; i < IOT_NETWORK_MAX_RECEIVE_CALLBACKS; i++ )
{
if( Atomic_CompareAndSwapPointers_p32( &_connections[ i ],
pConnection,
NULL ) == 1 )
{
break;
}
}
if( i == IOT_NETWORK_MAX_RECEIVE_CALLBACKS )
{
status = IOT_NETWORK_NO_MEMORY;
}
else
{
/* Add this socket to the socket set for the network task. */
FreeRTOS_FD_SET( pConnection->socket,
_socketSet,
eSELECT_READ );
}
return status;
}
/*-----------------------------------------------------------*/
size_t IotNetworkFreeRTOS_Send( IotNetworkConnection_t pConnection,
const uint8_t * pMessage,
size_t messageLength )
{
size_t bytesSent = 0;
BaseType_t socketStatus = 0;
/* Only one thread at a time may send on the connection. Lock the send
* mutex to prevent other threads from sending. */
if( xSemaphoreTake( pConnection->socketMutex,
IOT_NETWORK_SOCKET_TIMEOUT_MS ) == pdTRUE )
{
#if ( IOT_NETWORK_ENABLE_TLS == 1 )
if( pConnection->secured == pdTRUE )
{
while( bytesSent < messageLength )
{
socketStatus = ( BaseType_t ) mbedtls_ssl_write( &( pConnection->ssl.context ),
pMessage + bytesSent,
messageLength - bytesSent );
if( ( socketStatus == ( BaseType_t ) MBEDTLS_ERR_SSL_WANT_WRITE ) ||
( socketStatus == ( BaseType_t ) MBEDTLS_ERR_SSL_WANT_READ ) )
{
/* Try again for WANT_WRITE and WANT_READ errors. */
continue;
}
else if( socketStatus < 0 )
{
/* Exit on other errors. */
break;
}
else
{
bytesSent += ( size_t ) socketStatus;
configASSERT( bytesSent <= messageLength );
}
}
}
else
#endif /* if ( IOT_NETWORK_ENABLE_TLS == 1 ) */
{
socketStatus = FreeRTOS_send( pConnection->socket,
pMessage,
messageLength,
0 );
}
if( socketStatus > 0 )
{
bytesSent = ( size_t ) socketStatus;
}
xSemaphoreGive( pConnection->socketMutex );
}
IotLogDebug( "(Network connection %p) Sent %lu bytes.",
pConnection,
( unsigned long ) bytesSent );
return bytesSent;
}
/*-----------------------------------------------------------*/
size_t IotNetworkFreeRTOS_Receive( IotNetworkConnection_t pConnection,
uint8_t * pBuffer,
size_t bytesRequested )
{
BaseType_t socketStatus = 0;
size_t bytesReceived = 0, bytesRemaining = bytesRequested;
/* Block and wait for incoming data. */
while( bytesRemaining > 0 )
{
#if ( IOT_NETWORK_ENABLE_TLS == 1 )
if( pConnection->secured == pdTRUE )
{
if( xSemaphoreTake( pConnection->socketMutex,
IOT_NETWORK_SOCKET_TIMEOUT_MS ) == pdTRUE )
{
socketStatus = ( BaseType_t ) mbedtls_ssl_read( &( pConnection->ssl.context ),
pBuffer + bytesReceived,
bytesRequested - bytesReceived );
xSemaphoreGive( pConnection->socketMutex );
if( ( socketStatus == ( BaseType_t ) MBEDTLS_ERR_SSL_WANT_READ ) ||
( socketStatus == ( BaseType_t ) MBEDTLS_ERR_SSL_WANT_WRITE ) )
{
/* Try again for WANT_WRITE and WANT_READ errors. */
continue;
}
}
else
{
/* Could not obtain socket mutex, exit. */
break;
}
}
else
#endif /* if ( IOT_NETWORK_ENABLE_TLS == 1 ) */
{
socketStatus = FreeRTOS_recv( pConnection->socket,
pBuffer + bytesReceived,
bytesRemaining,
0 );
if( socketStatus == FREERTOS_EWOULDBLOCK )
{
/* The return value EWOULDBLOCK means no data was received within
* the socket timeout. Ignore it and try again. */
continue;
}
}
if( socketStatus < 0 )
{
IotLogError( "Error %ld while receiving data.", ( long int ) socketStatus );
break;
}
else
{
bytesReceived += ( size_t ) socketStatus;
bytesRemaining -= ( size_t ) socketStatus;
configASSERT( bytesReceived + bytesRemaining == bytesRequested );
}
}
if( bytesReceived < bytesRequested )
{
IotLogWarn( "(Network connection %p) Receive requested %lu bytes, but %lu bytes received instead.",
pConnection,
( unsigned long ) bytesRequested,
( unsigned long ) bytesReceived );
}
else
{
IotLogDebug( "(Network connection %p) Successfully received %lu bytes.",
pConnection,
( unsigned long ) bytesRequested );
}
return bytesReceived;
}
/*-----------------------------------------------------------*/
size_t IotNetworkFreeRTOS_ReceiveUpto( IotNetworkConnection_t pConnection,
uint8_t * pBuffer,
size_t bufferSize )
{
int32_t socketStatus = 0;
size_t bytesReceived = 0;
/* Caller should never pass a zero-length buffer. */
configASSERT( bufferSize > 0 );
#if ( IOT_NETWORK_ENABLE_TLS == 1 )
if( pConnection->secured == pdTRUE )
{
if( xSemaphoreTake( pConnection->socketMutex,
IOT_NETWORK_SOCKET_TIMEOUT_MS ) == pdTRUE )
{
do
{
socketStatus = ( BaseType_t ) mbedtls_ssl_read( &( pConnection->ssl.context ),
pBuffer + bytesReceived,
bufferSize - bytesReceived );
} while( ( socketStatus == ( BaseType_t ) MBEDTLS_ERR_SSL_WANT_READ ) ||
( socketStatus == ( BaseType_t ) MBEDTLS_ERR_SSL_WANT_WRITE ) );
xSemaphoreGive( pConnection->socketMutex );
}
else
{
IotLogError( "Could not obtain the socket mutex." );
}
}
else
#endif /* if ( IOT_NETWORK_ENABLE_TLS == 1 ) */
{
socketStatus = FreeRTOS_recv( pConnection->socket,
pBuffer + bytesReceived,
bufferSize - bytesReceived,
0 );
}
if( socketStatus <= 0 )
{
IotLogError( "Error %ld while receiving data.", ( long int ) socketStatus );
}
else
{
bytesReceived += ( size_t ) socketStatus;
}
IotLogDebug( "Received %lu bytes.",
( unsigned long ) bytesReceived );
return bytesReceived;
}
/*-----------------------------------------------------------*/
IotNetworkError_t IotNetworkFreeRTOS_Close( IotNetworkConnection_t pConnection )
{
BaseType_t socketStatus = 0, i = 0;
#if ( IOT_NETWORK_ENABLE_TLS == 1 )
/* Notify the peer that the TLS connection is being closed. */
if( pConnection->secured == pdTRUE )
{
if( xSemaphoreTake( pConnection->socketMutex,
IOT_NETWORK_SOCKET_TIMEOUT_MS ) == pdTRUE )
{
socketStatus = ( BaseType_t ) mbedtls_ssl_close_notify( &( pConnection->ssl.context ) );
/* Ignore the WANT_READ and WANT_WRITE return values. */
if( ( socketStatus != ( BaseType_t ) MBEDTLS_ERR_SSL_WANT_READ ) &&
( socketStatus != ( BaseType_t ) MBEDTLS_ERR_SSL_WANT_WRITE ) )
{
if( socketStatus == 0 )
{
IotLogInfo( "(Network connection %p) TLS close-notify sent.",
pConnection );
}
else
{
IotLogWarn( "(Network connection %p) Failed to send TLS close-notify, error %d.",
pConnection,
socketStatus );
}
}
xSemaphoreGive( pConnection->socketMutex );
}
}
#endif /* if ( IOT_NETWORK_ENABLE_TLS == 1 ) */
/* Call socket shutdown function to close connection. */
socketStatus = FreeRTOS_shutdown( pConnection->socket,
FREERTOS_SHUT_RDWR );
if( socketStatus != 0 )
{
IotLogWarn( "(Network connection %p) Failed to close connection.",
pConnection );
}
else
{
IotLogInfo( "(Network connection %p) Connection closed.",
pConnection );
}
/* Remove this connection from Select's socket set (if present). */
for( i = 0; i < IOT_NETWORK_MAX_RECEIVE_CALLBACKS; i++ )
{
if( Atomic_CompareAndSwapPointers_p32( &_connections[ i ], NULL, pConnection ) == 1 )
{
FreeRTOS_FD_CLR( pConnection->socket, _socketSet, eSELECT_ALL );
}
}
return IOT_NETWORK_SUCCESS;
}
/*-----------------------------------------------------------*/
IotNetworkError_t IotNetworkFreeRTOS_Destroy( IotNetworkConnection_t pConnection )
{
FreeRTOS_closesocket( pConnection->socket );
#if ( IOT_NETWORK_ENABLE_TLS == 1 )
/* Free mbed TLS contexts. */
if( pConnection->secured == pdTRUE )
{
_sslContextFree( pConnection );
}
#endif
/* Free memory used by network connection. */
vPortFree( pConnection );
IotLogInfo( "(Network connection %p) Connection destroyed.",
pConnection );
return IOT_NETWORK_SUCCESS;
}
/*-----------------------------------------------------------*/