]> git.sur5r.net Git - freertos/blob - FreeRTOS-Labs/Source/FreeRTOS-IoT-Libraries/abstractions/platform/freertos/iot_network_freertos.c
Add the Labs projects provided in the V10.2.1_191129 zip file.
[freertos] / FreeRTOS-Labs / Source / FreeRTOS-IoT-Libraries / abstractions / platform / freertos / iot_network_freertos.c
1 /*\r
2  * Amazon FreeRTOS Platform V1.1.0\r
3  * Copyright (C) 2019 Amazon.com, Inc. or its affiliates.  All Rights Reserved.\r
4  *\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
11  *\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
14  *\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
21  *\r
22  * http://aws.amazon.com/freertos\r
23  * http://www.FreeRTOS.org\r
24  */\r
25 \r
26 /**\r
27  * @file iot_network_freertos.c\r
28  * @brief Implementation of the network-related functions from iot_network_freertos.h\r
29  * for FreeRTOS+TCP sockets.\r
30  */\r
31 \r
32 /* The config header is always included first. */\r
33 #include "iot_config.h"\r
34 \r
35 /* Standard includes. */\r
36 #include <string.h>\r
37 \r
38 /* FreeRTOS includes. */\r
39 #include "FreeRTOS.h"\r
40 #include "atomic.h"\r
41 #include "semphr.h"\r
42 \r
43 /* FreeRTOS+TCP includes. */\r
44 #include "FreeRTOS_IP.h"\r
45 #include "FreeRTOS_Sockets.h"\r
46 \r
47 /* FreeRTOS-IoT-Libraries includes. */\r
48 #include "iot_error.h"\r
49 #include "platform/iot_network_freertos.h"\r
50 \r
51 #if ( IOT_NETWORK_ENABLE_TLS == 1 )\r
52     /* mbed TLS includes. */\r
53     #include "mbedtls/ctr_drbg.h"\r
54     #include "mbedtls/entropy.h"\r
55     #include "mbedtls/ssl.h"\r
56     #include "mbedtls/threading.h"\r
57     #include "mbedtls/x509.h"\r
58 #endif\r
59 \r
60 /* Configure logs for the functions in this file. */\r
61 #ifdef IOT_LOG_LEVEL_NETWORK\r
62     #define LIBRARY_LOG_LEVEL        IOT_LOG_LEVEL_NETWORK\r
63 #else\r
64     #ifdef IOT_LOG_LEVEL_GLOBAL\r
65         #define LIBRARY_LOG_LEVEL    IOT_LOG_LEVEL_GLOBAL\r
66     #else\r
67         #define LIBRARY_LOG_LEVEL    IOT_LOG_NONE\r
68     #endif\r
69 #endif\r
70 \r
71 #define LIBRARY_LOG_NAME    ( "NET" )\r
72 #include "iot_logging_setup.h"\r
73 \r
74 /* Provide a default value for socket timeout and network task parameters. */\r
75 #ifndef IOT_NETWORK_SOCKET_TIMEOUT_MS\r
76     #define IOT_NETWORK_SOCKET_TIMEOUT_MS    ( 5000 )\r
77 #endif\r
78 #ifndef IOT_NETWORK_TASK_STACK_SIZE\r
79     #define IOT_NETWORK_TASK_STACK_SIZE      ( 2048 )\r
80 #endif\r
81 #ifndef IOT_NETWORK_TASK_PRIORITY\r
82     #define IOT_NETWORK_TASK_PRIORITY        ( tskIDLE_PRIORITY )\r
83 #endif\r
84 \r
85 /* Maximum number of simultaneous socket receive callbacks. */\r
86 #ifndef IOT_NETWORK_MAX_RECEIVE_CALLBACKS\r
87     #define IOT_NETWORK_MAX_RECEIVE_CALLBACKS    ( 2 )\r
88 #endif\r
89 \r
90 /**\r
91  * @brief Maximum length of a DNS name.\r
92  *\r
93  * Per https://tools.ietf.org/html/rfc1035, 253 is the maximum string length\r
94  * of a DNS name.\r
95  */\r
96 #define MAX_DNS_NAME_LENGTH    ( 253 )\r
97 /*-----------------------------------------------------------*/\r
98 \r
99 /**\r
100  * @brief Internal network context.\r
101  */\r
102 typedef struct _networkConnection\r
103 {\r
104     Socket_t socket;                             /**< @brief FreeRTOS+TCP sockets handle. */\r
105     SemaphoreHandle_t socketMutex;               /**< @brief Prevents concurrent threads from using a socket. */\r
106     StaticSemaphore_t socketMutexStorage;        /**< @brief Storage space for socketMutex. */\r
107     IotNetworkReceiveCallback_t receiveCallback; /**< @brief Network receive callback, if any. */\r
108     void * pReceiveContext;                      /**< @brief The context for the receive callback. */\r
109 \r
110     #if ( IOT_NETWORK_ENABLE_TLS == 1 )\r
111         BaseType_t secured; /**< @brief Flag that marks a connection as secured. */\r
112 \r
113         /**\r
114          * @brief Secured connection context. Valid if `secured` is `pdTRUE`.\r
115          */\r
116         struct\r
117         {\r
118             mbedtls_ssl_config config;            /**< @brief SSL connection configuration. */\r
119             mbedtls_ssl_context context;          /**< @brief SSL connection context */\r
120             mbedtls_x509_crt_profile certProfile; /**< @brief Certificate security profile for this connection. */\r
121             mbedtls_x509_crt rootCa;              /**< @brief Root CA certificate context. */\r
122             mbedtls_x509_crt clientCert;          /**< @brief Client certificate context. */\r
123             mbedtls_pk_context privKey;           /**< @brief Client private key context. */\r
124         } ssl;\r
125     #endif /* if ( IOT_NETWORK_ENABLE_TLS == 1 ) */\r
126 } _networkConnection_t;\r
127 /*-----------------------------------------------------------*/\r
128 \r
129 #if ( IOT_NETWORK_ENABLE_TLS == 1 )\r
130 \r
131 /**\r
132  * @brief mbed TLS entropy context for generation of random numbers.\r
133  */\r
134     static mbedtls_entropy_context _entropyContext;\r
135 \r
136 /**\r
137  * @brief mbed TLS CTR DRBG context for generation of random numbers.\r
138  */\r
139     static mbedtls_ctr_drbg_context _ctrDrgbContext;\r
140 #endif\r
141 \r
142 /**\r
143  * @brief Handle of the network task.\r
144  */\r
145 static TaskHandle_t _networkTaskHandle;\r
146 \r
147 /**\r
148  * @brief Socket set for the network task.\r
149  */\r
150 static SocketSet_t _socketSet;\r
151 \r
152 /**\r
153  * @brief Connections in _socketSet.\r
154  */\r
155 static _networkConnection_t * _connections[ IOT_NETWORK_MAX_RECEIVE_CALLBACKS ];\r
156 \r
157 /**\r
158  * @brief An #IotNetworkInterface_t that uses the functions in this file.\r
159  */\r
160 const IotNetworkInterface_t IotNetworkFreeRTOS =\r
161 {\r
162     .create             = IotNetworkFreeRTOS_Create,\r
163     .setReceiveCallback = IotNetworkFreeRTOS_SetReceiveCallback,\r
164     .send               = IotNetworkFreeRTOS_Send,\r
165     .receive            = IotNetworkFreeRTOS_Receive,\r
166     .receiveUpto        = IotNetworkFreeRTOS_ReceiveUpto,\r
167     .close              = IotNetworkFreeRTOS_Close,\r
168     .destroy            = IotNetworkFreeRTOS_Destroy\r
169 };\r
170 /*-----------------------------------------------------------*/\r
171 \r
172 #if ( IOT_NETWORK_ENABLE_TLS == 1 )\r
173 \r
174 /**\r
175  * @brief Initialize the mbed TLS structures in a network connection.\r
176  *\r
177  * @param[in] pNetworkConnection The network connection to initialize.\r
178  */\r
179     static void _sslContextInit( _networkConnection_t * pNetworkConnection )\r
180     {\r
181         mbedtls_ssl_config_init( &( pNetworkConnection->ssl.config ) );\r
182         mbedtls_x509_crt_init( &( pNetworkConnection->ssl.rootCa ) );\r
183         mbedtls_pk_init( &( pNetworkConnection->ssl.privKey ) );\r
184         mbedtls_x509_crt_init( &( pNetworkConnection->ssl.clientCert ) );\r
185         mbedtls_ssl_init( &( pNetworkConnection->ssl.context ) );\r
186     }\r
187 /*-----------------------------------------------------------*/\r
188 \r
189 /**\r
190  * @brief Free the mbed TLS structures in a network connection.\r
191  *\r
192  * @param[in] pNetworkConnection The network connection with the contexts to free.\r
193  */\r
194     static void _sslContextFree( _networkConnection_t * pNetworkConnection )\r
195     {\r
196         mbedtls_ssl_free( &( pNetworkConnection->ssl.context ) );\r
197         mbedtls_x509_crt_free( &( pNetworkConnection->ssl.rootCa ) );\r
198         mbedtls_x509_crt_free( &( pNetworkConnection->ssl.clientCert ) );\r
199         mbedtls_pk_free( &( pNetworkConnection->ssl.privKey ) );\r
200         mbedtls_ssl_config_free( &( pNetworkConnection->ssl.config ) );\r
201     }\r
202 /*-----------------------------------------------------------*/\r
203 \r
204 /**\r
205  * @brief Set up TLS on a TCP connection.\r
206  *\r
207  * @param[in] pNetworkConnection An established TCP connection.\r
208  * @param[in] pServerName Remote host name, used for server name indication.\r
209  * @param[in] pCredentials TLS setup parameters.\r
210  *\r
211  * @return #IOT_NETWORK_SUCCESS, #IOT_NETWORK_FAILURE, #IOT_NETWORK_NO_MEMORY,\r
212  * or #IOT_NETWORK_SYSTEM_ERROR.\r
213  */\r
214     static IotNetworkError_t _tlsSetup( _networkConnection_t * pNetworkConnection,\r
215                                         const char * pServerName,\r
216                                         IotNetworkCredentials_t pCredentials )\r
217     {\r
218         IOT_FUNCTION_ENTRY( IotNetworkError_t, IOT_NETWORK_SUCCESS );\r
219         int mbedtlsError = 0;\r
220 \r
221         /* Initialize the mbed TLS context structures. */\r
222         _sslContextInit( pNetworkConnection );\r
223 \r
224         mbedtlsError = mbedtls_ssl_config_defaults( &( pNetworkConnection->ssl.config ),\r
225                                                     MBEDTLS_SSL_IS_CLIENT,\r
226                                                     MBEDTLS_SSL_TRANSPORT_STREAM,\r
227                                                     MBEDTLS_SSL_PRESET_DEFAULT );\r
228 \r
229         if( mbedtlsError != 0 )\r
230         {\r
231             IotLogError( "Failed to set default SSL configuration, error %d.", mbedtlsError );\r
232 \r
233             /* Per mbed TLS docs, mbedtls_ssl_config_defaults only fails on memory allocation. */\r
234             IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_NO_MEMORY );\r
235         }\r
236 \r
237         /* Set up the certificate security profile, starting from the default value. */\r
238         pNetworkConnection->ssl.certProfile = mbedtls_x509_crt_profile_default;\r
239 \r
240         /* test.mosquitto.org only provides a 1024-bit RSA certificate, which is\r
241          * not acceptable by the default mbed TLS certificate security profile.\r
242          * For the purposes of this demo, allow the use of 1024-bit RSA certificates.\r
243          * This block should be removed otherwise. */\r
244         if( strncmp( pServerName, "test.mosquitto.org", strlen( pServerName ) ) == 0 )\r
245         {\r
246             pNetworkConnection->ssl.certProfile.rsa_min_bitlen = 1024;\r
247         }\r
248 \r
249         /* Set SSL authmode and the RNG context. */\r
250         mbedtls_ssl_conf_authmode( &( pNetworkConnection->ssl.config ),\r
251                                    MBEDTLS_SSL_VERIFY_REQUIRED );\r
252         mbedtls_ssl_conf_rng( &( pNetworkConnection->ssl.config ),\r
253                               mbedtls_ctr_drbg_random,\r
254                               &_ctrDrgbContext );\r
255         mbedtls_ssl_conf_cert_profile( &( pNetworkConnection->ssl.config ),\r
256                                        &( pNetworkConnection->ssl.certProfile ) );\r
257 \r
258         /* Parse the server root CA certificate into the SSL context. */\r
259         mbedtlsError = mbedtls_x509_crt_parse( &( pNetworkConnection->ssl.rootCa ),\r
260                                                ( const unsigned char * ) pCredentials->pRootCa,\r
261                                                pCredentials->rootCaSize );\r
262 \r
263         if( mbedtlsError != 0 )\r
264         {\r
265             IotLogError( "Failed to parse server root CA certificate, error %d.",\r
266                          mbedtlsError );\r
267 \r
268             IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_SYSTEM_ERROR );\r
269         }\r
270 \r
271         mbedtls_ssl_conf_ca_chain( &( pNetworkConnection->ssl.config ),\r
272                                    &( pNetworkConnection->ssl.rootCa ),\r
273                                    NULL );\r
274 \r
275         if( ( pCredentials->pPrivateKey != NULL ) && ( pCredentials->pClientCert != NULL ) )\r
276         {\r
277             /* Setup the client private key. */\r
278             mbedtlsError = mbedtls_pk_parse_key( &( pNetworkConnection->ssl.privKey ),\r
279                                                  ( const unsigned char * ) pCredentials->pPrivateKey,\r
280                                                  pCredentials->privateKeySize,\r
281                                                  0,\r
282                                                  0 );\r
283 \r
284             if( mbedtlsError != 0 )\r
285             {\r
286                 IotLogError( "Failed to parse client certificate, error %d.",\r
287                              mbedtlsError );\r
288 \r
289                 IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_SYSTEM_ERROR );\r
290             }\r
291 \r
292             /* Setup the client certificate. */\r
293             mbedtlsError = mbedtls_x509_crt_parse( &( pNetworkConnection->ssl.clientCert ),\r
294                                                    ( const unsigned char * ) pCredentials->pClientCert,\r
295                                                    pCredentials->clientCertSize );\r
296 \r
297             if( mbedtlsError != 0 )\r
298             {\r
299                 IotLogError( "Failed to parse the client private key, error %d.",\r
300                              mbedtlsError );\r
301 \r
302                 IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_SYSTEM_ERROR );\r
303             }\r
304 \r
305             mbedtls_ssl_conf_own_cert( &( pNetworkConnection->ssl.config ),\r
306                                        &( pNetworkConnection->ssl.clientCert ),\r
307                                        &( pNetworkConnection->ssl.privKey ) );\r
308         }\r
309 \r
310         /* Initialize the mbed TLS secured connection context. */\r
311         mbedtlsError = mbedtls_ssl_setup( &( pNetworkConnection->ssl.context ),\r
312                                           &( pNetworkConnection->ssl.config ) );\r
313 \r
314         if( mbedtlsError != 0 )\r
315         {\r
316             IotLogError( "Failed to set up mbed TLS SSL context, error %d.",\r
317                          mbedtlsError );\r
318 \r
319             IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_SYSTEM_ERROR );\r
320         }\r
321 \r
322         /* Set the underlying IO for the TLS connection. */\r
323         mbedtls_ssl_set_bio( &( pNetworkConnection->ssl.context ),\r
324                              pNetworkConnection->socket,\r
325                              mbedtls_platform_send,\r
326                              mbedtls_platform_recv,\r
327                              NULL );\r
328 \r
329         /* Enable SNI if requested. */\r
330         if( pCredentials->disableSni == false )\r
331         {\r
332             mbedtlsError = mbedtls_ssl_set_hostname( &( pNetworkConnection->ssl.context ),\r
333                                                      pServerName );\r
334 \r
335             if( mbedtlsError != 0 )\r
336             {\r
337                 IotLogError( "Failed to set server name, error %d.", mbedtlsError );\r
338 \r
339                 IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_SYSTEM_ERROR );\r
340             }\r
341         }\r
342 \r
343         /* Perform the TLS handshake. */\r
344         do\r
345         {\r
346             mbedtlsError = mbedtls_ssl_handshake( &( pNetworkConnection->ssl.context ) );\r
347         } while( ( mbedtlsError == MBEDTLS_ERR_SSL_WANT_READ ) ||\r
348                  ( mbedtlsError == MBEDTLS_ERR_SSL_WANT_WRITE ) );\r
349 \r
350         if( mbedtlsError != 0 )\r
351         {\r
352             IotLogError( "Failed to perform TLS handshake, error %d.", mbedtlsError );\r
353 \r
354             IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_FAILURE );\r
355         }\r
356 \r
357         /* Clean up on error. */\r
358         IOT_FUNCTION_CLEANUP_BEGIN();\r
359 \r
360         if( status != IOT_NETWORK_SUCCESS )\r
361         {\r
362             _sslContextFree( pNetworkConnection );\r
363         }\r
364         else\r
365         {\r
366             pNetworkConnection->secured = pdTRUE;\r
367 \r
368             IotLogInfo( "(Network connection %p) TLS handshake successful.",\r
369                         pNetworkConnection );\r
370         }\r
371 \r
372         IOT_FUNCTION_CLEANUP_END();\r
373     }\r
374 #endif /* if ( IOT_NETWORK_ENABLE_TLS == 1 ) */\r
375 /*-----------------------------------------------------------*/\r
376 \r
377 static void _networkTask( void * pvParameters )\r
378 {\r
379     _networkConnection_t * pConnection = NULL;\r
380     BaseType_t socketEvents = 0, i = 0, socketStatus = 0;\r
381     SocketSet_t socketSet = pvParameters;\r
382 \r
383     while( true )\r
384     {\r
385         socketEvents = FreeRTOS_select( socketSet, IOT_NETWORK_SOCKET_TIMEOUT_MS );\r
386 \r
387         if( socketEvents > 0 )\r
388         {\r
389             for( i = 0; i < IOT_NETWORK_MAX_RECEIVE_CALLBACKS; i++ )\r
390             {\r
391                 pConnection = _connections[ i ];\r
392 \r
393                 if( pConnection != NULL )\r
394                 {\r
395                     socketStatus = FreeRTOS_FD_ISSET( pConnection->socket, socketSet );\r
396 \r
397                     if( socketStatus & eSELECT_READ )\r
398                     {\r
399                         /* A receive callback must be set; otherwise, select should not\r
400                          * have returned this socket. */\r
401                         configASSERT( pConnection->receiveCallback != NULL );\r
402 \r
403                         pConnection->receiveCallback( pConnection,\r
404                                                       pConnection->pReceiveContext );\r
405                     }\r
406                 }\r
407             }\r
408         }\r
409 \r
410         /* This task will receive a notification when cleanup is called. Exit when\r
411          * cleanup is called. */\r
412         if( ulTaskNotifyTake( pdTRUE, 0 ) != 0 )\r
413         {\r
414             break;\r
415         }\r
416     }\r
417 \r
418     FreeRTOS_DeleteSocketSet( socketSet );\r
419     vTaskDelete( NULL );\r
420 }\r
421 /*-----------------------------------------------------------*/\r
422 \r
423 IotNetworkError_t IotNetworkFreeRTOS_Init( void )\r
424 {\r
425     IOT_FUNCTION_ENTRY( IotNetworkError_t, IOT_NETWORK_SUCCESS );\r
426 \r
427     #if ( IOT_NETWORK_ENABLE_TLS == 1 )\r
428         int mbedtlsError = 0;\r
429 \r
430         /* Set the mutex functions for mbed TLS thread safety. */\r
431         mbedtls_threading_set_alt( mbedtls_platform_mutex_init,\r
432                                    mbedtls_platform_mutex_free,\r
433                                    mbedtls_platform_mutex_lock,\r
434                                    mbedtls_platform_mutex_unlock );\r
435 \r
436         /* Initialize contexts for random number generation. */\r
437         mbedtls_entropy_init( &_entropyContext );\r
438         mbedtls_ctr_drbg_init( &_ctrDrgbContext );\r
439 \r
440         /* Add a strong entropy source. At least one is required. */\r
441         mbedtlsError = mbedtls_entropy_add_source( &_entropyContext,\r
442                                                    mbedtls_platform_entropy_poll,\r
443                                                    NULL,\r
444                                                    32,\r
445                                                    MBEDTLS_ENTROPY_SOURCE_STRONG );\r
446 \r
447         if( mbedtlsError != 0 )\r
448         {\r
449             IotLogError( "Failed to add entropy source, error %d.", mbedtlsError );\r
450 \r
451             IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_FAILURE );\r
452         }\r
453 \r
454         /* Seed the random number generator. */\r
455         mbedtlsError = mbedtls_ctr_drbg_seed( &_ctrDrgbContext,\r
456                                               mbedtls_entropy_func,\r
457                                               &_entropyContext,\r
458                                               NULL,\r
459                                               0 );\r
460 \r
461         if( mbedtlsError != 0 )\r
462         {\r
463             IotLogError( "Failed to seed PRNG, error %d.", mbedtlsError );\r
464 \r
465             IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_FAILURE );\r
466         }\r
467     #endif /* if ( IOT_NETWORK_ENABLE_TLS == 1 ) */\r
468 \r
469     /* Create socket set for network task. */\r
470     _socketSet = FreeRTOS_CreateSocketSet();\r
471 \r
472     if( _socketSet == NULL )\r
473     {\r
474         IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_FAILURE );\r
475     }\r
476 \r
477     static StaticTask_t networkTask;\r
478     static StackType_t networkTaskStack[ IOT_NETWORK_TASK_STACK_SIZE ];\r
479 \r
480     /* Create the network task. Since valid parameters are provided, this should\r
481      * never fail. */\r
482     _networkTaskHandle = xTaskCreateStatic( _networkTask,\r
483                                             "Network",\r
484                                             IOT_NETWORK_TASK_STACK_SIZE,\r
485                                             _socketSet,\r
486                                             IOT_NETWORK_TASK_PRIORITY,\r
487                                             ( StackType_t * const ) &networkTaskStack,\r
488                                             &networkTask );\r
489     configASSERT( _networkTaskHandle != NULL );\r
490 \r
491     IotLogInfo( "Network successfully initialized." );\r
492 \r
493     IOT_FUNCTION_EXIT_NO_CLEANUP();\r
494 }\r
495 /*-----------------------------------------------------------*/\r
496 \r
497 void IotNetworkFreeRTOS_Cleanup( void )\r
498 {\r
499     #if ( IOT_NETWORK_ENABLE_TLS == 1 )\r
500         /* Free the contexts for random number generation. */\r
501         mbedtls_ctr_drbg_free( &_ctrDrgbContext );\r
502         mbedtls_entropy_free( &_entropyContext );\r
503 \r
504         /* Clear the mutex functions for mbed TLS thread safety. */\r
505         mbedtls_threading_free_alt();\r
506     #endif\r
507 \r
508     xTaskNotifyGive( _networkTaskHandle );\r
509 \r
510     IotLogInfo( "Network cleanup done." );\r
511 }\r
512 /*-----------------------------------------------------------*/\r
513 \r
514 IotNetworkError_t IotNetworkFreeRTOS_Create( IotNetworkServerInfo_t pServerInfo,\r
515                                              IotNetworkCredentials_t pCredentialInfo,\r
516                                              IotNetworkConnection_t * pConnection )\r
517 {\r
518     IOT_FUNCTION_ENTRY( IotNetworkError_t, IOT_NETWORK_SUCCESS );\r
519     Socket_t tcpSocket = FREERTOS_INVALID_SOCKET;\r
520     BaseType_t socketStatus = 0;\r
521     struct freertos_sockaddr serverAddress = { 0 };\r
522     const TickType_t receiveTimeout = pdMS_TO_TICKS( IOT_NETWORK_SOCKET_TIMEOUT_MS );\r
523     _networkConnection_t * pNewNetworkConnection = NULL;\r
524 \r
525     /* Credentials are not used if TLS is disabled. */\r
526     ( void ) pCredentialInfo;\r
527 \r
528     /* Check host name length against the maximum length allowed. */\r
529     const size_t hostnameLength = strlen( pServerInfo->pHostName );\r
530 \r
531     if( hostnameLength > ( size_t ) MAX_DNS_NAME_LENGTH )\r
532     {\r
533         IotLogError( "Host name length exceeds %d, which is the maximum allowed.",\r
534                      MAX_DNS_NAME_LENGTH );\r
535         IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_BAD_PARAMETER );\r
536     }\r
537 \r
538     pNewNetworkConnection = pvPortMalloc( sizeof( _networkConnection_t ) );\r
539 \r
540     if( pNewNetworkConnection == NULL )\r
541     {\r
542         IotLogError( "Failed to allocate memory for new network connection." );\r
543         IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_NO_MEMORY );\r
544     }\r
545 \r
546     /* Clear the connection information. */\r
547     ( void ) memset( pNewNetworkConnection, 0x00, sizeof( _networkConnection_t ) );\r
548 \r
549     /* Create a new TCP socket. */\r
550     tcpSocket = FreeRTOS_socket( FREERTOS_AF_INET,\r
551                                  FREERTOS_SOCK_STREAM,\r
552                                  FREERTOS_IPPROTO_TCP );\r
553 \r
554     if( tcpSocket == FREERTOS_INVALID_SOCKET )\r
555     {\r
556         IotLogError( "Failed to create new socket." );\r
557         IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_SYSTEM_ERROR );\r
558     }\r
559 \r
560     /* Set the timeout for receive. */\r
561     socketStatus = FreeRTOS_setsockopt( tcpSocket,\r
562                                         0,\r
563                                         FREERTOS_SO_RCVTIMEO,\r
564                                         &receiveTimeout,\r
565                                         sizeof( TickType_t ) );\r
566 \r
567     if( socketStatus != 0 )\r
568     {\r
569         IotLogError( "Failed to set socket receive timeout." );\r
570         IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_SYSTEM_ERROR );\r
571     }\r
572 \r
573     /* Establish connection. */\r
574     serverAddress.sin_family = FREERTOS_AF_INET;\r
575     serverAddress.sin_port = FreeRTOS_htons( pServerInfo->port );\r
576     serverAddress.sin_addr = FreeRTOS_gethostbyname( pServerInfo->pHostName );\r
577     serverAddress.sin_len = ( uint8_t ) sizeof( serverAddress );\r
578 \r
579     /* Check for errors from DNS lookup. */\r
580     if( serverAddress.sin_addr == 0 )\r
581     {\r
582         IotLogError( "Failed to resolve %s.", pServerInfo->pHostName );\r
583         IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_SYSTEM_ERROR );\r
584     }\r
585 \r
586     socketStatus = FreeRTOS_connect( tcpSocket,\r
587                                      &serverAddress,\r
588                                      sizeof( serverAddress ) );\r
589 \r
590     if( socketStatus != 0 )\r
591     {\r
592         IotLogError( "Failed to establish new connection. Socket status %d.", socketStatus );\r
593         IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_SYSTEM_ERROR );\r
594     }\r
595 \r
596     /* Set the socket. */\r
597     pNewNetworkConnection->socket = tcpSocket;\r
598 \r
599     #if ( IOT_NETWORK_ENABLE_TLS == 1 )\r
600         /* Set up TLS if credentials are provided. */\r
601         if( pCredentialInfo != NULL )\r
602         {\r
603             status = _tlsSetup( pNewNetworkConnection,\r
604                                 pServerInfo->pHostName,\r
605                                 pCredentialInfo );\r
606         }\r
607     #endif\r
608 \r
609     IOT_FUNCTION_CLEANUP_BEGIN();\r
610 \r
611     /* Clean up on failure. */\r
612     if( status != IOT_NETWORK_SUCCESS )\r
613     {\r
614         if( tcpSocket != FREERTOS_INVALID_SOCKET )\r
615         {\r
616             FreeRTOS_closesocket( tcpSocket );\r
617         }\r
618 \r
619         /* Clear the connection information. */\r
620         if( pNewNetworkConnection != NULL )\r
621         {\r
622             vPortFree( pNewNetworkConnection );\r
623         }\r
624     }\r
625     else\r
626     {\r
627         /* Create the socket mutex. */\r
628         pNewNetworkConnection->socketMutex = xSemaphoreCreateMutexStatic( &( pNewNetworkConnection->socketMutexStorage ) );\r
629 \r
630         /* Set the output parameter. */\r
631         *pConnection = pNewNetworkConnection;\r
632 \r
633         IotLogInfo( "(Network connection %p) Connection to %s established.",\r
634                     pNewNetworkConnection,\r
635                     pServerInfo->pHostName );\r
636     }\r
637 \r
638     IOT_FUNCTION_CLEANUP_END();\r
639 }\r
640 /*-----------------------------------------------------------*/\r
641 \r
642 IotNetworkError_t IotNetworkFreeRTOS_SetReceiveCallback( IotNetworkConnection_t pConnection,\r
643                                                          IotNetworkReceiveCallback_t receiveCallback,\r
644                                                          void * pContext )\r
645 {\r
646     IotNetworkError_t status = IOT_NETWORK_SUCCESS;\r
647     BaseType_t i = 0;\r
648 \r
649     /* Set the receive callback and context. */\r
650     pConnection->receiveCallback = receiveCallback;\r
651     pConnection->pReceiveContext = pContext;\r
652 \r
653     /* Add this connection to the list of connections that select should check. */\r
654     for( i = 0; i < IOT_NETWORK_MAX_RECEIVE_CALLBACKS; i++ )\r
655     {\r
656         if( Atomic_CompareAndSwapPointers_p32( &_connections[ i ],\r
657                                                pConnection,\r
658                                                NULL ) == 1 )\r
659         {\r
660             break;\r
661         }\r
662     }\r
663 \r
664     if( i == IOT_NETWORK_MAX_RECEIVE_CALLBACKS )\r
665     {\r
666         status = IOT_NETWORK_NO_MEMORY;\r
667     }\r
668     else\r
669     {\r
670         /* Add this socket to the socket set for the network task. */\r
671         FreeRTOS_FD_SET( pConnection->socket,\r
672                          _socketSet,\r
673                          eSELECT_READ );\r
674     }\r
675 \r
676     return status;\r
677 }\r
678 /*-----------------------------------------------------------*/\r
679 \r
680 size_t IotNetworkFreeRTOS_Send( IotNetworkConnection_t pConnection,\r
681                                 const uint8_t * pMessage,\r
682                                 size_t messageLength )\r
683 {\r
684     size_t bytesSent = 0;\r
685     BaseType_t socketStatus = 0;\r
686 \r
687     /* Only one thread at a time may send on the connection. Lock the send\r
688      * mutex to prevent other threads from sending. */\r
689     if( xSemaphoreTake( pConnection->socketMutex,\r
690                         IOT_NETWORK_SOCKET_TIMEOUT_MS ) == pdTRUE )\r
691     {\r
692         #if ( IOT_NETWORK_ENABLE_TLS == 1 )\r
693             if( pConnection->secured == pdTRUE )\r
694             {\r
695                 while( bytesSent < messageLength )\r
696                 {\r
697                     socketStatus = ( BaseType_t ) mbedtls_ssl_write( &( pConnection->ssl.context ),\r
698                                                                      pMessage + bytesSent,\r
699                                                                      messageLength - bytesSent );\r
700 \r
701                     if( ( socketStatus == ( BaseType_t ) MBEDTLS_ERR_SSL_WANT_WRITE ) ||\r
702                         ( socketStatus == ( BaseType_t ) MBEDTLS_ERR_SSL_WANT_READ ) )\r
703                     {\r
704                         /* Try again for WANT_WRITE and WANT_READ errors. */\r
705                         continue;\r
706                     }\r
707                     else if( socketStatus < 0 )\r
708                     {\r
709                         /* Exit on other errors. */\r
710                         break;\r
711                     }\r
712                     else\r
713                     {\r
714                         bytesSent += ( size_t ) socketStatus;\r
715                         configASSERT( bytesSent <= messageLength );\r
716                     }\r
717                 }\r
718             }\r
719             else\r
720         #endif /* if ( IOT_NETWORK_ENABLE_TLS == 1 ) */\r
721         {\r
722             socketStatus = FreeRTOS_send( pConnection->socket,\r
723                                           pMessage,\r
724                                           messageLength,\r
725                                           0 );\r
726         }\r
727 \r
728         if( socketStatus > 0 )\r
729         {\r
730             bytesSent = ( size_t ) socketStatus;\r
731         }\r
732 \r
733         xSemaphoreGive( pConnection->socketMutex );\r
734     }\r
735 \r
736     IotLogDebug( "(Network connection %p) Sent %lu bytes.",\r
737                  pConnection,\r
738                  ( unsigned long ) bytesSent );\r
739 \r
740     return bytesSent;\r
741 }\r
742 /*-----------------------------------------------------------*/\r
743 \r
744 size_t IotNetworkFreeRTOS_Receive( IotNetworkConnection_t pConnection,\r
745                                    uint8_t * pBuffer,\r
746                                    size_t bytesRequested )\r
747 {\r
748     BaseType_t socketStatus = 0;\r
749     size_t bytesReceived = 0, bytesRemaining = bytesRequested;\r
750 \r
751     /* Block and wait for incoming data. */\r
752     while( bytesRemaining > 0 )\r
753     {\r
754         #if ( IOT_NETWORK_ENABLE_TLS == 1 )\r
755             if( pConnection->secured == pdTRUE )\r
756             {\r
757                 if( xSemaphoreTake( pConnection->socketMutex,\r
758                                     IOT_NETWORK_SOCKET_TIMEOUT_MS ) == pdTRUE )\r
759                 {\r
760                     socketStatus = ( BaseType_t ) mbedtls_ssl_read( &( pConnection->ssl.context ),\r
761                                                                     pBuffer + bytesReceived,\r
762                                                                     bytesRequested - bytesReceived );\r
763 \r
764                     xSemaphoreGive( pConnection->socketMutex );\r
765 \r
766                     if( ( socketStatus == ( BaseType_t ) MBEDTLS_ERR_SSL_WANT_READ ) ||\r
767                         ( socketStatus == ( BaseType_t ) MBEDTLS_ERR_SSL_WANT_WRITE ) )\r
768                     {\r
769                         /* Try again for WANT_WRITE and WANT_READ errors. */\r
770                         continue;\r
771                     }\r
772                 }\r
773                 else\r
774                 {\r
775                     /* Could not obtain socket mutex, exit. */\r
776                     break;\r
777                 }\r
778             }\r
779             else\r
780         #endif /* if ( IOT_NETWORK_ENABLE_TLS == 1 ) */\r
781         {\r
782             socketStatus = FreeRTOS_recv( pConnection->socket,\r
783                                           pBuffer + bytesReceived,\r
784                                           bytesRemaining,\r
785                                           0 );\r
786 \r
787             if( socketStatus == FREERTOS_EWOULDBLOCK )\r
788             {\r
789                 /* The return value EWOULDBLOCK means no data was received within\r
790                  * the socket timeout. Ignore it and try again. */\r
791                 continue;\r
792             }\r
793         }\r
794 \r
795         if( socketStatus < 0 )\r
796         {\r
797             IotLogError( "Error %ld while receiving data.", ( long int ) socketStatus );\r
798             break;\r
799         }\r
800         else\r
801         {\r
802             bytesReceived += ( size_t ) socketStatus;\r
803             bytesRemaining -= ( size_t ) socketStatus;\r
804 \r
805             configASSERT( bytesReceived + bytesRemaining == bytesRequested );\r
806         }\r
807     }\r
808 \r
809     if( bytesReceived < bytesRequested )\r
810     {\r
811         IotLogWarn( "(Network connection %p) Receive requested %lu bytes, but %lu bytes received instead.",\r
812                     pConnection,\r
813                     ( unsigned long ) bytesRequested,\r
814                     ( unsigned long ) bytesReceived );\r
815     }\r
816     else\r
817     {\r
818         IotLogDebug( "(Network connection %p) Successfully received %lu bytes.",\r
819                      pConnection,\r
820                      ( unsigned long ) bytesRequested );\r
821     }\r
822 \r
823     return bytesReceived;\r
824 }\r
825 \r
826 /*-----------------------------------------------------------*/\r
827 \r
828 size_t IotNetworkFreeRTOS_ReceiveUpto( IotNetworkConnection_t pConnection,\r
829                                        uint8_t * pBuffer,\r
830                                        size_t bufferSize )\r
831 {\r
832     int32_t socketStatus = 0;\r
833     size_t bytesReceived = 0;\r
834 \r
835     /* Caller should never pass a zero-length buffer. */\r
836     configASSERT( bufferSize > 0 );\r
837 \r
838     #if ( IOT_NETWORK_ENABLE_TLS == 1 )\r
839         if( pConnection->secured == pdTRUE )\r
840         {\r
841             if( xSemaphoreTake( pConnection->socketMutex,\r
842                                 IOT_NETWORK_SOCKET_TIMEOUT_MS ) == pdTRUE )\r
843             {\r
844                 do\r
845                 {\r
846                     socketStatus = ( BaseType_t ) mbedtls_ssl_read( &( pConnection->ssl.context ),\r
847                                                                     pBuffer + bytesReceived,\r
848                                                                     bufferSize - bytesReceived );\r
849                 } while( ( socketStatus == ( BaseType_t ) MBEDTLS_ERR_SSL_WANT_READ ) ||\r
850                          ( socketStatus == ( BaseType_t ) MBEDTLS_ERR_SSL_WANT_WRITE ) );\r
851 \r
852                 xSemaphoreGive( pConnection->socketMutex );\r
853             }\r
854             else\r
855             {\r
856                 IotLogError( "Could not obtain the socket mutex." );\r
857             }\r
858         }\r
859         else\r
860     #endif /* if ( IOT_NETWORK_ENABLE_TLS == 1 ) */\r
861     {\r
862         socketStatus = FreeRTOS_recv( pConnection->socket,\r
863                                       pBuffer + bytesReceived,\r
864                                       bufferSize - bytesReceived,\r
865                                       0 );\r
866     }\r
867 \r
868     if( socketStatus <= 0 )\r
869     {\r
870         IotLogError( "Error %ld while receiving data.", ( long int ) socketStatus );\r
871     }\r
872     else\r
873     {\r
874         bytesReceived += ( size_t ) socketStatus;\r
875     }\r
876 \r
877     IotLogDebug( "Received %lu bytes.",\r
878                  ( unsigned long ) bytesReceived );\r
879 \r
880     return bytesReceived;\r
881 }\r
882 \r
883 /*-----------------------------------------------------------*/\r
884 \r
885 IotNetworkError_t IotNetworkFreeRTOS_Close( IotNetworkConnection_t pConnection )\r
886 {\r
887     BaseType_t socketStatus = 0, i = 0;\r
888 \r
889     #if ( IOT_NETWORK_ENABLE_TLS == 1 )\r
890         /* Notify the peer that the TLS connection is being closed. */\r
891         if( pConnection->secured == pdTRUE )\r
892         {\r
893             if( xSemaphoreTake( pConnection->socketMutex,\r
894                                 IOT_NETWORK_SOCKET_TIMEOUT_MS ) == pdTRUE )\r
895             {\r
896                 socketStatus = ( BaseType_t ) mbedtls_ssl_close_notify( &( pConnection->ssl.context ) );\r
897 \r
898                 /* Ignore the WANT_READ and WANT_WRITE return values. */\r
899                 if( ( socketStatus != ( BaseType_t ) MBEDTLS_ERR_SSL_WANT_READ ) &&\r
900                     ( socketStatus != ( BaseType_t ) MBEDTLS_ERR_SSL_WANT_WRITE ) )\r
901                 {\r
902                     if( socketStatus == 0 )\r
903                     {\r
904                         IotLogInfo( "(Network connection %p) TLS close-notify sent.",\r
905                                     pConnection );\r
906                     }\r
907                     else\r
908                     {\r
909                         IotLogWarn( "(Network connection %p) Failed to send TLS close-notify, error %d.",\r
910                                     pConnection,\r
911                                     socketStatus );\r
912                     }\r
913                 }\r
914 \r
915                 xSemaphoreGive( pConnection->socketMutex );\r
916             }\r
917         }\r
918     #endif /* if ( IOT_NETWORK_ENABLE_TLS == 1 ) */\r
919 \r
920     /* Call socket shutdown function to close connection. */\r
921     socketStatus = FreeRTOS_shutdown( pConnection->socket,\r
922                                       FREERTOS_SHUT_RDWR );\r
923 \r
924     if( socketStatus != 0 )\r
925     {\r
926         IotLogWarn( "(Network connection %p) Failed to close connection.",\r
927                     pConnection );\r
928     }\r
929     else\r
930     {\r
931         IotLogInfo( "(Network connection %p) Connection closed.",\r
932                     pConnection );\r
933     }\r
934 \r
935     /* Remove this connection from Select's socket set (if present). */\r
936     for( i = 0; i < IOT_NETWORK_MAX_RECEIVE_CALLBACKS; i++ )\r
937     {\r
938         if( Atomic_CompareAndSwapPointers_p32( &_connections[ i ], NULL, pConnection ) == 1 )\r
939         {\r
940             FreeRTOS_FD_CLR( pConnection->socket, _socketSet, eSELECT_ALL );\r
941         }\r
942     }\r
943 \r
944     return IOT_NETWORK_SUCCESS;\r
945 }\r
946 /*-----------------------------------------------------------*/\r
947 \r
948 IotNetworkError_t IotNetworkFreeRTOS_Destroy( IotNetworkConnection_t pConnection )\r
949 {\r
950     FreeRTOS_closesocket( pConnection->socket );\r
951 \r
952     #if ( IOT_NETWORK_ENABLE_TLS == 1 )\r
953         /* Free mbed TLS contexts. */\r
954         if( pConnection->secured == pdTRUE )\r
955         {\r
956             _sslContextFree( pConnection );\r
957         }\r
958     #endif\r
959 \r
960     /* Free memory used by network connection. */\r
961     vPortFree( pConnection );\r
962 \r
963     IotLogInfo( "(Network connection %p) Connection destroyed.",\r
964                 pConnection );\r
965 \r
966     return IOT_NETWORK_SUCCESS;\r
967 }\r
968 /*-----------------------------------------------------------*/\r