xref: /FreeRTOS-Plus-TCP-v4.0.0/source/FreeRTOS_DNS_Callback.c (revision 62f5d3a1fd65a52b480e9495cbe0ffcef2168754)
1 /*
2  * FreeRTOS+TCP <DEVELOPMENT BRANCH>
3  * Copyright (C) 2022 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
4  *
5  * SPDX-License-Identifier: MIT
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a copy of
8  * this software and associated documentation files (the "Software"), to deal in
9  * the Software without restriction, including without limitation the rights to
10  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
11  * the Software, and to permit persons to whom the Software is furnished to do so,
12  * subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in all
15  * copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
19  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
20  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  *
24  * http://aws.amazon.com/freertos
25  * http://www.FreeRTOS.org
26  */
27 
28 /**
29  * @file FreeRTOS_DNS_Callback.c
30  * @brief File that handles the DNS Callback option
31  */
32 
33 #include "FreeRTOS_DNS_Callback.h"
34 
35 #include "FreeRTOS_IP.h"
36 #include "FreeRTOS_IP_Private.h"
37 #include "FreeRTOS_DNS_Globals.h"
38 #include "FreeRTOS_IP_Timers.h"
39 
40 #if ( ( ipconfigDNS_USE_CALLBACKS == 1 ) && ( ipconfigUSE_DNS != 0 ) )
41 
42 /**
43  * @brief list of callbacks to send
44  */
45     static List_t xCallbackList;
46 
47 /**
48  * @brief A DNS reply was received, see if there is any matching entry and
49  *        call the handler.
50  *
51  * @param[in,out] pxSet a set of variables that are shared among the helper functions.
52  * @param[in] pxAddress Pointer to address info ( IPv4/IPv6 ) obtained from the DNS server.
53  *
54  * @return Returns pdTRUE if uxIdentifier was recognized.
55  */
xDNSDoCallback(ParseSet_t * pxSet,struct freertos_addrinfo * pxAddress)56     BaseType_t xDNSDoCallback( ParseSet_t * pxSet,
57                                struct freertos_addrinfo * pxAddress )
58     {
59         BaseType_t xResult = pdFALSE;
60         const ListItem_t * pxIterator;
61         const ListItem_t * xEnd = listGET_END_MARKER( &xCallbackList );
62         TickType_t uxIdentifier = ( TickType_t ) pxSet->pxDNSMessageHeader->usIdentifier;
63 
64         /* While iterating through the list, the scheduler is suspended.
65          * Remember which function shall be called once the scheduler is
66          * running again. */
67         FOnDNSEvent pCallbackFunction = NULL;
68         void * pvSearchID = NULL;
69 
70         vTaskSuspendAll();
71         {
72             for( pxIterator = ( const ListItem_t * ) listGET_NEXT( xEnd );
73                  pxIterator != ( const ListItem_t * ) xEnd;
74                  pxIterator = ( const ListItem_t * ) listGET_NEXT( pxIterator ) )
75             {
76                 BaseType_t xMatching;
77                 DNSCallback_t * pxCallback = ( ( DNSCallback_t * ) listGET_LIST_ITEM_OWNER( pxIterator ) );
78                 #if ( ipconfigUSE_MDNS == 1 )
79                     /* mDNS port 5353. */
80                     if( pxSet->usPortNumber == FreeRTOS_htons( ipMDNS_PORT ) )
81                     {
82                         /* In mDNS, the query ID field is ignored and the
83                          * hostname will be compared with outstanding requests. */
84 
85                         xMatching = ( strcasecmp( pxCallback->pcName, pxSet->pcName ) == 0 ) ? pdTRUE : pdFALSE;
86                     }
87                     else
88                 #endif /* if ( ipconfigUSE_MDNS == 1 ) */
89                 {
90                     xMatching = ( listGET_LIST_ITEM_VALUE( pxIterator ) == uxIdentifier ) ? pdTRUE : pdFALSE;
91                 }
92 
93                 if( xMatching == pdTRUE )
94                 {
95                     pvSearchID = pxCallback->pvSearchID;
96                     pCallbackFunction = pxCallback->pCallbackFunction;
97                     ( void ) uxListRemove( &pxCallback->xListItem );
98                     vPortFree( pxCallback );
99 
100                     if( listLIST_IS_EMPTY( &xCallbackList ) != pdFALSE )
101                     {
102                         /* The list of outstanding requests is empty. No need for periodic polling. */
103                         vIPSetDNSTimerEnableState( pdFALSE );
104                     }
105 
106                     xResult = pdTRUE;
107                     break;
108                 }
109             }
110         }
111         ( void ) xTaskResumeAll();
112 
113         if( pCallbackFunction != NULL )
114         {
115             pCallbackFunction( pxSet->pcName, pvSearchID, pxAddress );
116         }
117 
118         return xResult;
119     }
120 
121 /**
122  * @brief FreeRTOS_gethostbyname_a() was called along with callback parameters.
123  *        Store them in a list for later reference.
124  *
125  * @param[in] pcHostName The hostname whose IP address is being searched for.
126  * @param[in] pvSearchID The search ID of the DNS callback function to set.
127  * @param[in] pCallbackFunction The callback function pointer.
128  * @param[in] uxTimeout Timeout of the callback function.
129  * @param[in] uxIdentifier Random number used as ID in the DNS message.
130  * @param[in] xIsIPv6 pdTRUE if the address type should be IPv6.
131  */
vDNSSetCallBack(const char * pcHostName,void * pvSearchID,FOnDNSEvent pCallbackFunction,TickType_t uxTimeout,TickType_t uxIdentifier,BaseType_t xIsIPv6)132     void vDNSSetCallBack( const char * pcHostName,
133                           void * pvSearchID,
134                           FOnDNSEvent pCallbackFunction,
135                           TickType_t uxTimeout,
136                           TickType_t uxIdentifier,
137                           BaseType_t xIsIPv6 )
138     {
139         size_t lLength = strlen( pcHostName );
140 
141         /* MISRA Ref 4.12.1 [Use of dynamic memory]. */
142         /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#directive-412. */
143         /* coverity[misra_c_2012_directive_4_12_violation] */
144         DNSCallback_t * pxCallback = ( ( DNSCallback_t * ) pvPortMalloc( sizeof( *pxCallback ) + lLength ) );
145 
146         /* Translate from ms to number of clock ticks. */
147         uxTimeout /= portTICK_PERIOD_MS;
148 
149         if( pxCallback != NULL )
150         {
151             if( listLIST_IS_EMPTY( &xCallbackList ) != pdFALSE )
152             {
153                 /* This is the first one, start the DNS timer to check for timeouts */
154                 vDNSTimerReload( FreeRTOS_min_uint32( 1000U, ( uint32_t ) uxTimeout ) );
155             }
156 
157             ( void ) strcpy( pxCallback->pcName, pcHostName );
158             pxCallback->pCallbackFunction = pCallbackFunction;
159             pxCallback->pvSearchID = pvSearchID;
160             pxCallback->uxRemainingTime = uxTimeout;
161             pxCallback->xIsIPv6 = xIsIPv6;
162 
163             vTaskSetTimeOutState( &( pxCallback->uxTimeoutState ) );
164             listSET_LIST_ITEM_OWNER( &( pxCallback->xListItem ), ( void * ) pxCallback );
165             listSET_LIST_ITEM_VALUE( &( pxCallback->xListItem ), uxIdentifier );
166             vTaskSuspendAll();
167             {
168                 vListInsertEnd( &xCallbackList, &pxCallback->xListItem );
169             }
170             ( void ) xTaskResumeAll();
171         }
172         else
173         {
174             FreeRTOS_debug_printf( ( " vDNSSetCallBack : Could not allocate memory: %u bytes",
175                                      ( unsigned ) ( sizeof( *pxCallback ) + lLength ) ) );
176         }
177     }
178 /*-----------------------------------------------------------*/
179 
180 /**
181  * @brief Iterate through the list of call-back structures and remove
182  * old entries which have reached a timeout.
183  * As soon as the list has become empty, the DNS timer will be stopped.
184  * In case pvSearchID is supplied, the user wants to cancel a DNS request.
185  *
186  * @param[in] pvSearchID The search ID of callback function whose associated
187  *                 DNS request is being cancelled. If non-ID specific checking of
188  *                 all requests is required, then this field should be kept as NULL.
189  */
vDNSCheckCallBack(void * pvSearchID)190     void vDNSCheckCallBack( void * pvSearchID )
191     {
192         const ListItem_t * pxIterator;
193         const ListItem_t * xEnd = listGET_END_MARKER( &xCallbackList );
194 
195         /* When a DNS-search times out, the call-back function shall
196          * be called. Store theses item in a temporary list.
197          * Only when the scheduler is running, user functions
198          * shall be called. */
199         List_t xTempList;
200 
201         vListInitialise( &xTempList );
202 
203         vTaskSuspendAll();
204         {
205             for( pxIterator = ( const ListItem_t * ) listGET_NEXT( xEnd );
206                  pxIterator != xEnd; )
207             {
208                 DNSCallback_t * pxCallback = ( ( DNSCallback_t * ) listGET_LIST_ITEM_OWNER( pxIterator ) );
209                 /* Move to the next item because we might remove this item */
210                 pxIterator = ( const ListItem_t * ) listGET_NEXT( pxIterator );
211 
212                 if( ( pvSearchID != NULL ) && ( pvSearchID == pxCallback->pvSearchID ) )
213                 {
214                     ( void ) uxListRemove( &( pxCallback->xListItem ) );
215                     vPortFree( pxCallback );
216                 }
217                 else if( xTaskCheckForTimeOut( &pxCallback->uxTimeoutState, &( pxCallback->uxRemainingTime ) ) != pdFALSE )
218                 {
219                     /* A time-out occurred in the asynchronous search.
220                      * Remove it from xCallbackList. */
221                     ( void ) uxListRemove( &( pxCallback->xListItem ) );
222 
223                     /* Insert it in a temporary list. The function will be called
224                      * once the scheduler is resumed. */
225                     vListInsertEnd( &( xTempList ), &pxCallback->xListItem );
226                 }
227                 else
228                 {
229                     /* This call-back is still waiting for a reply or a time-out. */
230                 }
231             }
232         }
233         ( void ) xTaskResumeAll();
234 
235         if( listLIST_IS_EMPTY( &xTempList ) == pdFALSE )
236         {
237             /* There is at least one item in xTempList which must be removed and deleted. */
238             xEnd = listGET_END_MARKER( &xTempList );
239 
240             for( pxIterator = ( const ListItem_t * ) listGET_NEXT( xEnd );
241                  pxIterator != xEnd;
242                  )
243             {
244                 DNSCallback_t * pxCallback = ( ( DNSCallback_t * ) listGET_LIST_ITEM_OWNER( pxIterator ) );
245                 /* Move to the next item because we might remove this item */
246                 pxIterator = ( const ListItem_t * ) listGET_NEXT( pxIterator );
247 
248                 /* A time-out occurred in the asynchronous search.
249                  * Call the application hook with the proper information. */
250                 if( pxCallback->xIsIPv6 != 0 )
251                 {
252                     pxCallback->pCallbackFunction( pxCallback->pcName, pxCallback->pvSearchID, NULL );
253                 }
254                 else
255                 {
256                     pxCallback->pCallbackFunction( pxCallback->pcName, pxCallback->pvSearchID, 0U );
257                 }
258 
259                 /* Remove it from 'xTempList' and free the memory. */
260                 ( void ) uxListRemove( &( pxCallback->xListItem ) );
261                 vPortFree( pxCallback );
262             }
263         }
264 
265         if( listLIST_IS_EMPTY( &xCallbackList ) != pdFALSE )
266         {
267             vIPSetDNSTimerEnableState( pdFALSE );
268         }
269     }
270 /*-----------------------------------------------------------*/
271 
272 /**
273  * @brief initialize the cache
274  * @post will modify global list xCallbackList
275  */
vDNSCallbackInitialise()276     void vDNSCallbackInitialise()
277     {
278         vListInitialise( &xCallbackList );
279     }
280 #endif /* if ( ipconfigDNS_USE_CALLBACKS == 1 ) */
281