2 * Amazon FreeRTOS Platform V1.0.0
\r
3 * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
\r
5 * Permission is hereby granted, free of charge, to any person obtaining a copy of
\r
6 * this software and associated documentation files (the "Software"), to deal in
\r
7 * the Software without restriction, including without limitation the rights to
\r
8 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
\r
9 * the Software, and to permit persons to whom the Software is furnished to do so,
\r
10 * subject to the following conditions:
\r
12 * The above copyright notice and this permission notice shall be included in all
\r
13 * copies or substantial portions of the Software.
\r
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
\r
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
\r
17 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
\r
18 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
\r
19 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
\r
20 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\r
22 * http://aws.amazon.com/freertos
\r
23 * http://www.FreeRTOS.org
\r
27 * @file iot_network_freertos.c
\r
28 * @brief Implementation of the network-related functions from iot_network_freertos.h
\r
29 * for Amazon FreeRTOS secure sockets.
\r
32 /* The config header is always included first. */
\r
33 #include "iot_config.h"
\r
35 /* Standard includes. */
\r
38 /* FreeRTOS includes. */
\r
40 #include "event_groups.h"
\r
42 /* Error handling include. */
\r
43 #include "private/iot_error.h"
\r
45 /* Amazon FreeRTOS network include. */
\r
46 #include "platform/iot_network_freertos.h"
\r
48 /* Configure logs for the functions in this file. */
\r
49 #ifdef IOT_LOG_LEVEL_NETWORK
\r
50 #define LIBRARY_LOG_LEVEL IOT_LOG_LEVEL_NETWORK
\r
52 #ifdef IOT_LOG_LEVEL_GLOBAL
\r
53 #define LIBRARY_LOG_LEVEL IOT_LOG_LEVEL_GLOBAL
\r
55 #define LIBRARY_LOG_LEVEL IOT_LOG_NONE
\r
59 #define LIBRARY_LOG_NAME ( "NET" )
\r
60 #include "iot_logging_setup.h"
\r
62 /* Provide a default value for the number of milliseconds for a socket poll.
\r
63 * This is a temporary workaround to deal with the lack of poll(). */
\r
64 #ifndef IOT_NETWORK_SOCKET_POLL_MS
\r
65 #define IOT_NETWORK_SOCKET_POLL_MS ( 1000 )
\r
69 * @brief The event group bit to set when a connection's socket is shut down.
\r
71 #define _FLAG_SHUTDOWN ( 1 )
\r
74 * @brief The event group bit to set when a connection's receive task exits.
\r
76 #define _FLAG_RECEIVE_TASK_EXITED ( 2 )
\r
79 * @brief The event group bit to set when the connection is destroyed from the
\r
82 #define _FLAG_CONNECTION_DESTROYED ( 4 )
\r
84 /*-----------------------------------------------------------*/
\r
86 typedef struct _networkConnection
\r
88 Socket_t socket; /**< @brief Amazon FreeRTOS Secure Sockets handle. */
\r
89 StaticSemaphore_t socketMutex; /**< @brief Prevents concurrent threads from sending on a socket. */
\r
90 StaticEventGroup_t connectionFlags; /**< @brief Synchronizes with the receive task. */
\r
91 TaskHandle_t receiveTask; /**< @brief Handle of the receive task, if any. */
\r
92 IotNetworkReceiveCallback_t receiveCallback; /**< @brief Network receive callback, if any. */
\r
93 void * pReceiveContext; /**< @brief The context for the receive callback. */
\r
94 bool bufferedByteValid; /**< @brief Used to determine if the buffered byte is valid. */
\r
95 uint8_t bufferedByte; /**< @brief A single byte buffered from a receive, since AFR Secure Sockets does not have poll(). */
\r
96 } _networkConnection_t;
\r
98 /*-----------------------------------------------------------*/
\r
101 * @brief An #IotNetworkInterface_t that uses the functions in this file.
\r
103 const IotNetworkInterface_t IotNetworkAfr =
\r
105 .create = IotNetworkAfr_Create,
\r
106 .setReceiveCallback = IotNetworkAfr_SetReceiveCallback,
\r
107 .send = IotNetworkAfr_Send,
\r
108 .receive = IotNetworkAfr_Receive,
\r
109 .close = IotNetworkAfr_Close,
\r
110 .destroy = IotNetworkAfr_Destroy
\r
113 /*-----------------------------------------------------------*/
\r
116 * @brief Destroys a network connection.
\r
118 * @param[in] pNetworkConnection The connection to destroy.
\r
120 static void _destroyConnection( _networkConnection_t * pNetworkConnection )
\r
122 /* Call Secure Sockets close function to free resources. */
\r
123 int32_t socketStatus = SOCKETS_Close( pNetworkConnection->socket );
\r
125 if( socketStatus != SOCKETS_ERROR_NONE )
\r
127 IotLogWarn( "Failed to destroy connection." );
\r
130 /* Free the network connection. */
\r
131 vPortFree( pNetworkConnection );
\r
134 /*-----------------------------------------------------------*/
\r
137 * @brief Task routine that waits on incoming network data.
\r
139 * @param[in] pArgument The network connection.
\r
141 static void _networkReceiveTask( void * pArgument )
\r
143 bool destroyConnection = false;
\r
144 int32_t socketStatus = 0;
\r
145 EventBits_t connectionFlags = 0;
\r
147 /* Cast network connection to the correct type. */
\r
148 _networkConnection_t * pNetworkConnection = pArgument;
\r
152 /* No buffered byte should be in the connection. */
\r
153 configASSERT( pNetworkConnection->bufferedByteValid == false );
\r
155 /* Block and wait for 1 byte of data. This simulates the behavior of poll().
\r
156 * THIS IS A TEMPORARY WORKAROUND AND DOES NOT PROVIDE THREAD-SAFETY AGAINST
\r
157 * MULTIPLE CALLS OF RECEIVE. */
\r
160 socketStatus = SOCKETS_Recv( pNetworkConnection->socket,
\r
161 &( pNetworkConnection->bufferedByte ),
\r
165 connectionFlags = xEventGroupGetBits( ( EventGroupHandle_t ) &( pNetworkConnection->connectionFlags ) );
\r
167 if( ( connectionFlags & _FLAG_SHUTDOWN ) == _FLAG_SHUTDOWN )
\r
169 socketStatus = SOCKETS_ECLOSED;
\r
172 /* Check for timeout. Some ports return 0, some return EWOULDBLOCK. */
\r
173 } while( ( socketStatus == 0 ) || ( socketStatus == SOCKETS_EWOULDBLOCK ) );
\r
175 if( socketStatus <= 0 )
\r
180 pNetworkConnection->bufferedByteValid = true;
\r
182 /* Invoke the network callback. */
\r
183 pNetworkConnection->receiveCallback( pNetworkConnection,
\r
184 pNetworkConnection->pReceiveContext );
\r
186 /* Check if the connection was destroyed by the receive callback. This
\r
187 * does not need to be thread-safe because the destroy connection function
\r
188 * may only be called once (per its API doc). */
\r
189 connectionFlags = xEventGroupGetBits( ( EventGroupHandle_t ) &( pNetworkConnection->connectionFlags ) );
\r
191 if( ( connectionFlags & _FLAG_CONNECTION_DESTROYED ) == _FLAG_CONNECTION_DESTROYED )
\r
193 destroyConnection = true;
\r
198 IotLogDebug( "Network receive task terminating." );
\r
200 /* If necessary, destroy the network connection before exiting. */
\r
201 if( destroyConnection == true )
\r
203 _destroyConnection( pNetworkConnection );
\r
207 /* Set the flag to indicate that the receive task has exited. */
\r
208 ( void ) xEventGroupSetBits( ( EventGroupHandle_t ) &( pNetworkConnection->connectionFlags ),
\r
209 _FLAG_RECEIVE_TASK_EXITED );
\r
212 vTaskDelete( NULL );
\r
215 /*-----------------------------------------------------------*/
\r
218 * @brief Set up a secured TLS connection.
\r
220 * @param[in] pAfrCredentials Credentials for the secured connection.
\r
221 * @param[in] tcpSocket An initialized socket to secure.
\r
222 * @param[in] pHostName Remote server name for SNI.
\r
223 * @param[in] hostnameLength The length of `pHostName`.
\r
225 * @return #IOT_NETWORK_SUCCESS or #IOT_NETWORK_SYSTEM_ERROR.
\r
227 static IotNetworkError_t _tlsSetup( const IotNetworkCredentials_t * pAfrCredentials,
\r
228 Socket_t tcpSocket,
\r
229 const char * pHostName,
\r
230 size_t hostnameLength )
\r
232 IOT_FUNCTION_ENTRY( IotNetworkError_t, IOT_NETWORK_SUCCESS );
\r
233 int32_t socketStatus = SOCKETS_ERROR_NONE;
\r
235 /* ALPN options for AWS IoT. */
\r
236 const char * ppcALPNProtos[] = { socketsAWS_IOT_ALPN_MQTT };
\r
238 /* Set secured option. */
\r
239 socketStatus = SOCKETS_SetSockOpt( tcpSocket,
\r
241 SOCKETS_SO_REQUIRE_TLS,
\r
245 if( socketStatus != SOCKETS_ERROR_NONE )
\r
247 IotLogError( "Failed to set secured option for new connection." );
\r
248 IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_SYSTEM_ERROR );
\r
251 /* Set ALPN option. */
\r
252 if( pAfrCredentials->pAlpnProtos != NULL )
\r
254 socketStatus = SOCKETS_SetSockOpt( tcpSocket,
\r
256 SOCKETS_SO_ALPN_PROTOCOLS,
\r
258 sizeof( ppcALPNProtos ) / sizeof( ppcALPNProtos[ 0 ] ) );
\r
260 if( socketStatus != SOCKETS_ERROR_NONE )
\r
262 IotLogError( "Failed to set ALPN option for new connection." );
\r
263 IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_SYSTEM_ERROR );
\r
267 /* Set SNI option. */
\r
268 if( pAfrCredentials->disableSni == false )
\r
270 socketStatus = SOCKETS_SetSockOpt( tcpSocket,
\r
272 SOCKETS_SO_SERVER_NAME_INDICATION,
\r
274 hostnameLength + 1 );
\r
276 if( socketStatus != SOCKETS_ERROR_NONE )
\r
278 IotLogError( "Failed to set SNI option for new connection." );
\r
279 IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_SYSTEM_ERROR );
\r
283 /* Set custom server certificate. */
\r
284 if( pAfrCredentials->pRootCa != NULL )
\r
286 socketStatus = SOCKETS_SetSockOpt( tcpSocket,
\r
288 SOCKETS_SO_TRUSTED_SERVER_CERTIFICATE,
\r
289 pAfrCredentials->pRootCa,
\r
290 pAfrCredentials->rootCaSize );
\r
292 if( socketStatus != SOCKETS_ERROR_NONE )
\r
294 IotLogError( "Failed to set server certificate option for new connection." );
\r
295 IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_SYSTEM_ERROR );
\r
299 IOT_FUNCTION_EXIT_NO_CLEANUP();
\r
302 /*-----------------------------------------------------------*/
\r
304 IotNetworkError_t IotNetworkAfr_Create( void * pConnectionInfo,
\r
305 void * pCredentialInfo,
\r
306 void ** pConnection )
\r
308 IOT_FUNCTION_ENTRY( IotNetworkError_t, IOT_NETWORK_SUCCESS );
\r
309 Socket_t tcpSocket = SOCKETS_INVALID_SOCKET;
\r
310 int32_t socketStatus = SOCKETS_ERROR_NONE;
\r
311 SocketsSockaddr_t serverAddress = { 0 };
\r
312 EventGroupHandle_t pConnectionFlags = NULL;
\r
313 SemaphoreHandle_t pConnectionMutex = NULL;
\r
314 const TickType_t receiveTimeout = pdMS_TO_TICKS( IOT_NETWORK_SOCKET_POLL_MS );
\r
315 _networkConnection_t * pNewNetworkConnection = NULL;
\r
317 /* Cast function parameters to correct types. */
\r
318 const IotNetworkServerInfo_t * pServerInfo = pConnectionInfo;
\r
319 const IotNetworkCredentials_t * pAfrCredentials = pCredentialInfo;
\r
320 _networkConnection_t ** pNetworkConnection = ( _networkConnection_t ** ) pConnection;
\r
322 /* Check host name length against the maximum length allowed by Secure
\r
324 const size_t hostnameLength = strlen( pServerInfo->pHostName );
\r
326 if( hostnameLength > ( size_t ) securesocketsMAX_DNS_NAME_LENGTH )
\r
328 IotLogError( "Host name length exceeds %d, which is the maximum allowed.",
\r
329 securesocketsMAX_DNS_NAME_LENGTH );
\r
330 IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_BAD_PARAMETER );
\r
333 pNewNetworkConnection = pvPortMalloc( sizeof( _networkConnection_t ) );
\r
335 if( pNewNetworkConnection == NULL )
\r
337 IotLogError( "Failed to allocate memory for new network connection." );
\r
338 IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_NO_MEMORY );
\r
341 /* Clear the connection information. */
\r
342 ( void ) memset( pNewNetworkConnection, 0x00, sizeof( _networkConnection_t ) );
\r
344 /* Create a new TCP socket. */
\r
345 tcpSocket = SOCKETS_Socket( SOCKETS_AF_INET,
\r
346 SOCKETS_SOCK_STREAM,
\r
347 SOCKETS_IPPROTO_TCP );
\r
349 if( tcpSocket == SOCKETS_INVALID_SOCKET )
\r
351 IotLogError( "Failed to create new socket." );
\r
352 IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_SYSTEM_ERROR );
\r
355 /* Set up connection encryption if credentials are provided. */
\r
356 if( pAfrCredentials != NULL )
\r
358 status = _tlsSetup( pAfrCredentials, tcpSocket, pServerInfo->pHostName, hostnameLength );
\r
360 if( status != IOT_NETWORK_SUCCESS )
\r
362 IOT_GOTO_CLEANUP();
\r
366 /* Establish connection. */
\r
367 serverAddress.ucSocketDomain = SOCKETS_AF_INET;
\r
368 serverAddress.usPort = SOCKETS_htons( pServerInfo->port );
\r
369 serverAddress.ulAddress = SOCKETS_GetHostByName( pServerInfo->pHostName );
\r
371 /* Check for errors from DNS lookup. */
\r
372 if( serverAddress.ulAddress == 0 )
\r
374 IotLogError( "Failed to resolve %s.", pServerInfo->pHostName );
\r
375 IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_SYSTEM_ERROR );
\r
378 socketStatus = SOCKETS_Connect( tcpSocket,
\r
380 sizeof( SocketsSockaddr_t ) );
\r
382 if( socketStatus != SOCKETS_ERROR_NONE )
\r
384 IotLogError( "Failed to establish new connection." );
\r
385 IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_SYSTEM_ERROR );
\r
388 /* Set a long timeout for receive. */
\r
389 socketStatus = SOCKETS_SetSockOpt( tcpSocket,
\r
391 SOCKETS_SO_RCVTIMEO,
\r
393 sizeof( TickType_t ) );
\r
395 if( socketStatus != SOCKETS_ERROR_NONE )
\r
397 IotLogError( "Failed to set socket receive timeout." );
\r
398 IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_SYSTEM_ERROR );
\r
401 IOT_FUNCTION_CLEANUP_BEGIN();
\r
403 /* Clean up on failure. */
\r
404 if( status != IOT_NETWORK_SUCCESS )
\r
406 if( tcpSocket != SOCKETS_INVALID_SOCKET )
\r
408 SOCKETS_Close( tcpSocket );
\r
411 /* Clear the connection information. */
\r
412 if( pNewNetworkConnection != NULL )
\r
414 vPortFree( pNewNetworkConnection );
\r
419 /* Set the socket. */
\r
420 pNewNetworkConnection->socket = tcpSocket;
\r
422 /* Create the connection event flags and mutex. */
\r
423 pConnectionFlags = xEventGroupCreateStatic( &( pNewNetworkConnection->connectionFlags ) );
\r
424 pConnectionMutex = xSemaphoreCreateMutexStatic( &( pNewNetworkConnection->socketMutex ) );
\r
426 /* Static event flags and mutex creation should never fail. The handles
\r
427 * should point inside the connection object. */
\r
428 configASSERT( pConnectionFlags == ( EventGroupHandle_t ) &( pNewNetworkConnection->connectionFlags ) );
\r
429 configASSERT( pConnectionMutex == ( SemaphoreHandle_t ) &( pNewNetworkConnection->socketMutex ) );
\r
431 /* Set the output parameter. */
\r
432 *pNetworkConnection = pNewNetworkConnection;
\r
435 IOT_FUNCTION_CLEANUP_END();
\r
438 /*-----------------------------------------------------------*/
\r
440 IotNetworkError_t IotNetworkAfr_SetReceiveCallback( void * pConnection,
\r
441 IotNetworkReceiveCallback_t receiveCallback,
\r
444 IotNetworkError_t status = IOT_NETWORK_SUCCESS;
\r
446 /* Cast network connection to the correct type. */
\r
447 _networkConnection_t * pNetworkConnection = ( _networkConnection_t * ) pConnection;
\r
449 /* Set the receive callback and context. */
\r
450 pNetworkConnection->receiveCallback = receiveCallback;
\r
451 pNetworkConnection->pReceiveContext = pContext;
\r
453 /* No flags should be set. */
\r
454 configASSERT( xEventGroupGetBits( ( EventGroupHandle_t ) &( pNetworkConnection->connectionFlags ) ) == 0 );
\r
456 /* Create task that waits for incoming data. */
\r
457 if( xTaskCreate( _networkReceiveTask,
\r
459 IOT_NETWORK_RECEIVE_TASK_STACK_SIZE,
\r
460 pNetworkConnection,
\r
461 IOT_NETWORK_RECEIVE_TASK_PRIORITY,
\r
462 &( pNetworkConnection->receiveTask ) ) != pdPASS )
\r
464 IotLogError( "Failed to create network receive task." );
\r
466 status = IOT_NETWORK_SYSTEM_ERROR;
\r
472 /*-----------------------------------------------------------*/
\r
474 size_t IotNetworkAfr_Send( void * pConnection,
\r
475 const uint8_t * pMessage,
\r
476 size_t messageLength )
\r
478 size_t bytesSent = 0;
\r
479 int32_t socketStatus = SOCKETS_ERROR_NONE;
\r
481 /* Cast network connection to the correct type. */
\r
482 _networkConnection_t * pNetworkConnection = ( _networkConnection_t * ) pConnection;
\r
484 /* Only one thread at a time may send on the connection. Lock the socket
\r
485 * mutex to prevent other threads from sending. */
\r
486 if( xSemaphoreTake( ( QueueHandle_t ) &( pNetworkConnection->socketMutex ),
\r
487 portMAX_DELAY ) == pdTRUE )
\r
489 socketStatus = SOCKETS_Send( pNetworkConnection->socket,
\r
494 if( socketStatus > 0 )
\r
496 bytesSent = ( size_t ) socketStatus;
\r
499 xSemaphoreGive( ( QueueHandle_t ) &( pNetworkConnection->socketMutex ) );
\r
505 /*-----------------------------------------------------------*/
\r
507 size_t IotNetworkAfr_Receive( void * pConnection,
\r
509 size_t bytesRequested )
\r
511 int32_t socketStatus = 0;
\r
512 size_t bytesReceived = 0, bytesRemaining = bytesRequested;
\r
514 /* Cast network connection to the correct type. */
\r
515 _networkConnection_t * pNetworkConnection = ( _networkConnection_t * ) pConnection;
\r
517 /* Write the buffered byte. THIS IS A TEMPORARY WORKAROUND AND ASSUMES THIS
\r
518 * FUNCTION IS ALWAYS CALLED FROM THE RECEIVE CALLBACK. */
\r
519 if( pNetworkConnection->bufferedByteValid == true )
\r
521 *pBuffer = pNetworkConnection->bufferedByte;
\r
524 pNetworkConnection->bufferedByteValid = false;
\r
527 /* Block and wait for incoming data. */
\r
528 while( bytesRemaining > 0 )
\r
530 socketStatus = SOCKETS_Recv( pNetworkConnection->socket,
\r
531 pBuffer + bytesReceived,
\r
535 if( socketStatus == SOCKETS_EWOULDBLOCK )
\r
537 /* The return value EWOULDBLOCK means no data was received within
\r
538 * the socket timeout. Ignore it and try again. */
\r
541 else if( socketStatus <= 0 )
\r
543 IotLogError( "Error %ld while receiving data.", ( long int ) socketStatus );
\r
548 bytesReceived += ( size_t ) socketStatus;
\r
549 bytesRemaining -= ( size_t ) socketStatus;
\r
551 configASSERT( bytesReceived + bytesRemaining == bytesRequested );
\r
555 if( bytesReceived < bytesRequested )
\r
557 IotLogWarn( "Receive requested %lu bytes, but %lu bytes received instead.",
\r
558 ( unsigned long ) bytesRequested,
\r
559 ( unsigned long ) bytesReceived );
\r
563 IotLogDebug( "Successfully received %lu bytes.",
\r
564 ( unsigned long ) bytesRequested );
\r
567 return bytesReceived;
\r
570 /*-----------------------------------------------------------*/
\r
572 IotNetworkError_t IotNetworkAfr_Close( void * pConnection )
\r
574 int32_t socketStatus = SOCKETS_ERROR_NONE;
\r
576 /* Cast network connection to the correct type. */
\r
577 _networkConnection_t * pNetworkConnection = ( _networkConnection_t * ) pConnection;
\r
579 /* Call Secure Sockets shutdown function to close connection. */
\r
580 socketStatus = SOCKETS_Shutdown( pNetworkConnection->socket,
\r
581 SOCKETS_SHUT_RDWR );
\r
583 if( socketStatus != SOCKETS_ERROR_NONE )
\r
585 IotLogWarn( "Failed to close connection." );
\r
588 /* Set the shutdown flag. */
\r
589 ( void ) xEventGroupSetBits( ( EventGroupHandle_t ) &( pNetworkConnection->connectionFlags ),
\r
592 return IOT_NETWORK_SUCCESS;
\r
595 /*-----------------------------------------------------------*/
\r
597 IotNetworkError_t IotNetworkAfr_Destroy( void * pConnection )
\r
599 /* Cast network connection to the correct type. */
\r
600 _networkConnection_t * pNetworkConnection = ( _networkConnection_t * ) pConnection;
\r
602 /* Check if this function is being called from the receive task. */
\r
603 if( xTaskGetCurrentTaskHandle() == pNetworkConnection->receiveTask )
\r
605 /* Set the flag specifying that the connection is destroyed. */
\r
606 ( void ) xEventGroupSetBits( ( EventGroupHandle_t ) &( pNetworkConnection->connectionFlags ),
\r
607 _FLAG_CONNECTION_DESTROYED );
\r
611 /* If a receive task was created, wait for it to exit. */
\r
612 if( pNetworkConnection->receiveTask != NULL )
\r
614 ( void ) xEventGroupWaitBits( ( EventGroupHandle_t ) &( pNetworkConnection->connectionFlags ),
\r
615 _FLAG_RECEIVE_TASK_EXITED,
\r
621 _destroyConnection( pNetworkConnection );
\r
624 return IOT_NETWORK_SUCCESS;
\r
627 /*-----------------------------------------------------------*/
\r