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