| /* | |
| * 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; | |
| } | |
| /*-----------------------------------------------------------*/ |