1 /*
2  * FreeRTOS V202212.00
3  * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy of
6  * this software and associated documentation files (the "Software"), to deal in
7  * the Software without restriction, including without limitation the rights to
8  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9  * the Software, and to permit persons to whom the Software is furnished to do so,
10  * subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in all
13  * copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21  *
22  * https://www.FreeRTOS.org
23  * https://github.com/FreeRTOS
24  *
25  */
26 
27 /**
28  * @file sockets_wrapper.c
29  * @brief FreeRTOS Sockets connect and disconnect wrapper implementation.
30  */
31 
32 /* Include header that defines log levels. */
33 #include "logging_levels.h"
34 
35 /* Logging configuration for the Sockets. */
36 #ifndef LIBRARY_LOG_NAME
37     #define LIBRARY_LOG_NAME     "SocketsWrapper"
38 #endif
39 #ifndef LIBRARY_LOG_LEVEL
40     #define LIBRARY_LOG_LEVEL    LOG_INFO
41 #endif
42 
43 extern void vLoggingPrintf( const char * pcFormatString,
44                             ... );
45 
46 #include "logging_stack.h"
47 
48 /* Standard includes. */
49 #include <string.h>
50 
51 /* FreeRTOS includes. */
52 #include "FreeRTOS.h"
53 
54 #include "sockets_wrapper.h"
55 
56 /*-----------------------------------------------------------*/
57 
58 /* Maximum number of times to call FreeRTOS_recv when initiating a graceful shutdown. */
59 #ifndef FREERTOS_SOCKETS_WRAPPER_SHUTDOWN_LOOPS
60     #define FREERTOS_SOCKETS_WRAPPER_SHUTDOWN_LOOPS    ( 3 )
61 #endif
62 
63 /* A negative error code indicating a network failure. */
64 #define FREERTOS_SOCKETS_WRAPPER_NETWORK_ERROR    ( -1 )
65 
66 /*-----------------------------------------------------------*/
67 
Sockets_Connect(Socket_t * pTcpSocket,const char * pHostName,uint16_t port,uint32_t receiveTimeoutMs,uint32_t sendTimeoutMs)68 BaseType_t Sockets_Connect( Socket_t * pTcpSocket,
69                             const char * pHostName,
70                             uint16_t port,
71                             uint32_t receiveTimeoutMs,
72                             uint32_t sendTimeoutMs )
73 {
74     Socket_t tcpSocket = FREERTOS_INVALID_SOCKET;
75     BaseType_t socketStatus = 0;
76     struct freertos_sockaddr serverAddress = { 0 };
77     TickType_t transportTimeout = 0;
78 
79     /* Create a new TCP socket. */
80     tcpSocket = FreeRTOS_socket( FREERTOS_AF_INET, FREERTOS_SOCK_STREAM, FREERTOS_IPPROTO_TCP );
81 
82     if( tcpSocket == FREERTOS_INVALID_SOCKET )
83     {
84         LogError( ( "Failed to create new socket." ) );
85         socketStatus = FREERTOS_SOCKETS_WRAPPER_NETWORK_ERROR;
86     }
87     else
88     {
89         LogDebug( ( "Created new TCP socket." ) );
90 
91         /* Connection parameters. */
92         serverAddress.sin_family = FREERTOS_AF_INET;
93         serverAddress.sin_port = FreeRTOS_htons( port );
94         serverAddress.sin_len = ( uint8_t ) sizeof( serverAddress );
95 
96         #if defined( ipconfigIPv4_BACKWARD_COMPATIBLE ) && ( ipconfigIPv4_BACKWARD_COMPATIBLE == 0 )
97             serverAddress.sin_address.ulIP_IPv4 = ( uint32_t ) FreeRTOS_gethostbyname( pHostName );
98 
99             /* Check for errors from DNS lookup. */
100             if( serverAddress.sin_address.ulIP_IPv4 == 0U )
101         #else
102             serverAddress.sin_addr = ( uint32_t ) FreeRTOS_gethostbyname( pHostName );
103 
104             /* Check for errors from DNS lookup. */
105             if( serverAddress.sin_addr == 0U )
106         #endif /* defined( ipconfigIPv4_BACKWARD_COMPATIBLE ) && ( ipconfigIPv4_BACKWARD_COMPATIBLE == 0 ) */
107 
108         {
109             LogError( ( "Failed to connect to server: DNS resolution failed: Hostname=%s.",
110                         pHostName ) );
111             socketStatus = FREERTOS_SOCKETS_WRAPPER_NETWORK_ERROR;
112         }
113     }
114 
115     if( socketStatus == 0 )
116     {
117         /* Establish connection. */
118         LogDebug( ( "Creating TCP Connection to %s.", pHostName ) );
119         socketStatus = FreeRTOS_connect( tcpSocket, &serverAddress, sizeof( serverAddress ) );
120 
121         if( socketStatus != 0 )
122         {
123             LogError( ( "Failed to connect to server: FreeRTOS_Connect failed: ReturnCode=%d,"
124                         " Hostname=%s, Port=%u.",
125                         socketStatus,
126                         pHostName,
127                         port ) );
128         }
129     }
130 
131     if( socketStatus == 0 )
132     {
133         /* Set socket receive timeout. */
134         transportTimeout = pdMS_TO_TICKS( receiveTimeoutMs );
135         /* Setting the receive block time cannot fail. */
136         ( void ) FreeRTOS_setsockopt( tcpSocket,
137                                       0,
138                                       FREERTOS_SO_RCVTIMEO,
139                                       &transportTimeout,
140                                       sizeof( TickType_t ) );
141 
142         /* Set socket send timeout. */
143         transportTimeout = pdMS_TO_TICKS( sendTimeoutMs );
144         /* Setting the send block time cannot fail. */
145         ( void ) FreeRTOS_setsockopt( tcpSocket,
146                                       0,
147                                       FREERTOS_SO_SNDTIMEO,
148                                       &transportTimeout,
149                                       sizeof( TickType_t ) );
150     }
151 
152     /* Clean up on failure. */
153     if( socketStatus != 0 )
154     {
155         if( tcpSocket != FREERTOS_INVALID_SOCKET )
156         {
157             ( void ) FreeRTOS_closesocket( tcpSocket );
158         }
159     }
160     else
161     {
162         /* Set the socket. */
163         *pTcpSocket = tcpSocket;
164         LogInfo( ( "Established TCP connection with %s.", pHostName ) );
165     }
166 
167     return socketStatus;
168 }
169 
170 /*-----------------------------------------------------------*/
171 
Sockets_Disconnect(Socket_t tcpSocket)172 void Sockets_Disconnect( Socket_t tcpSocket )
173 {
174     BaseType_t waitForShutdownLoopCount = 0;
175     uint8_t pDummyBuffer[ 2 ];
176 
177     if( tcpSocket != FREERTOS_INVALID_SOCKET )
178     {
179         /* Initiate graceful shutdown. */
180         ( void ) FreeRTOS_shutdown( tcpSocket, FREERTOS_SHUT_RDWR );
181 
182         /* Wait for the socket to disconnect gracefully (indicated by FreeRTOS_recv()
183          * returning a FREERTOS_EINVAL error) before closing the socket. */
184         while( FreeRTOS_recv( tcpSocket, pDummyBuffer, sizeof( pDummyBuffer ), 0 ) >= 0 )
185         {
186             /* We don't need to delay since FreeRTOS_recv should already have a timeout. */
187 
188             if( ++waitForShutdownLoopCount >= FREERTOS_SOCKETS_WRAPPER_SHUTDOWN_LOOPS )
189             {
190                 break;
191             }
192         }
193 
194         ( void ) FreeRTOS_closesocket( tcpSocket );
195     }
196 }
197 
198 /*-----------------------------------------------------------*/
199