xref: /FreeRTOS-Plus-TCP-v3.1.0/source/FreeRTOS_DNS_Cache.c (revision 37bdfe577f3b728058de714e2e747d3c78803f26)
1 /*
2  * FreeRTOS+TCP V3.1.0
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_Cache.c
30  * @brief File that handles the DNS caching option
31  */
32 
33 /* FreeRTOS+TCP includes. */
34 #include "FreeRTOS_IP.h"
35 #include "FreeRTOS_Sockets.h"
36 #include "FreeRTOS_UDP_IP.h"
37 #include "FreeRTOS_DHCP.h"
38 #include "NetworkBufferManagement.h"
39 #include "NetworkInterface.h"
40 
41 #include "FreeRTOS_DNS_Cache.h"
42 #include "FreeRTOS_DNS_Globals.h"
43 
44 /* Standard includes. */
45 #include <stdint.h>
46 #include <stdio.h>
47 #include <string.h>
48 
49 #if ( ( ipconfigUSE_DNS != 0 ) && ( ipconfigUSE_DNS_CACHE == 1 ) )
50 
51 /**
52  * @brief cache entry format structure
53  */
54     typedef struct xDNS_CACHE_TABLE_ROW
55     {
56         uint32_t ulIPAddresses[ ipconfigDNS_CACHE_ADDRESSES_PER_ENTRY ]; /*!< The IP address(es) of an ARP cache entry. */
57         char pcName[ ipconfigDNS_CACHE_NAME_LENGTH ];                    /*!< The name of the host */
58         uint32_t ulTTL;                                                  /*!< Time-to-Live (in seconds) from the DNS server. */
59         uint32_t ulTimeWhenAddedInSeconds;                               /*!< time at which the entry was added */
60         #if ( ipconfigDNS_CACHE_ADDRESSES_PER_ENTRY > 1 )
61             uint8_t ucNumIPAddresses;                                    /*!< number of ip addresses for the same entry */
62             uint8_t ucCurrentIPAddress;                                  /*!< current ip address index */
63         #endif
64     } DNSCacheRow_t;
65 
66 /*!
67  * @brief DNS cache structure instantiation
68  */
69     static DNSCacheRow_t xDNSCache[ ipconfigDNS_CACHE_ENTRIES ] = { 0x0 };
70 
71 /*!
72  * @brief indicates the index of a free entry in the cache structure
73  *        \a  DNSCacheRow_t
74  */
75     static UBaseType_t uxFreeEntry = 0U;
76 
77 
78 
79     static BaseType_t prvFindEntryIndex( const char * pcName,
80                                          UBaseType_t * uxResult );
81 
82     static BaseType_t prvGetCacheIPEntry( UBaseType_t uxIndex,
83                                           uint32_t * pulIP,
84                                           uint32_t ulCurrentTimeSeconds );
85 
86     static void prvUpdateCacheEntry( UBaseType_t uxIndex,
87                                      uint32_t ulTTL,
88                                      const uint32_t * pulIP,
89                                      uint32_t ulCurrentTimeSeconds );
90 
91     static void prvInsertCacheEntry( const char * pcName,
92                                      uint32_t ulTTL,
93                                      const uint32_t * pulIP,
94                                      uint32_t ulCurrentTimeSeconds );
95 
96 /**
97  * @brief perform a dns lookup in the local cache
98  * @param pcHostName the lookup name
99  * @return ulIPAddress with the value from the cache else returns a zero if the
100  *         cache is not enabled or the lookup is not successful
101  * @post the global structure \a xDNSCache might be modified
102  */
FreeRTOS_dnslookup(const char * pcHostName)103     uint32_t FreeRTOS_dnslookup( const char * pcHostName )
104     {
105         uint32_t ulIPAddress = 0U;
106 
107         ( void ) FreeRTOS_ProcessDNSCache( pcHostName,
108                                            &ulIPAddress,
109                                            0,
110                                            pdTRUE );
111 
112         return ulIPAddress;
113     }
114 
115 /**
116  * @brief perform a dns update in the local cache
117  * @param pcName the lookup name
118  * @param pulIP the ip value to insert/replace
119  * @param ulTTL Time To live (in seconds)
120  * @return this is a dummy return, we are actually ignoring the return value
121  *         from this function
122  * @post the global structure \a xDNSCache might be modified
123  */
FreeRTOS_dns_update(const char * pcName,uint32_t * pulIP,uint32_t ulTTL)124     BaseType_t FreeRTOS_dns_update( const char * pcName,
125                                     uint32_t * pulIP,
126                                     uint32_t ulTTL )
127     {
128         ( void ) FreeRTOS_ProcessDNSCache( pcName,
129                                            pulIP,
130                                            ulTTL,
131                                            pdFALSE );
132         return pdTRUE;
133     }
134 
135 /**
136  * @brief perform a dns clear in the local cache
137  * @post the global structure \a xDNSCache is modified
138  */
FreeRTOS_dnsclear(void)139     void FreeRTOS_dnsclear( void )
140     {
141         ( void ) memset( xDNSCache, 0x0, sizeof( xDNSCache ) );
142         uxFreeEntry = 0U;
143     }
144 
145 /**
146  * @brief process a DNS Cache request (get, update, or insert)
147  *
148  * @param[in] pcName: the name of the host
149  * @param[in,out] pulIP: when doing a lookup, will be set, when doing an update,
150  *                       will be read.
151  * @param[in] ulTTL: Time To Live (in seconds)
152  * @param[in] xLookUp: pdTRUE if a look-up is expected, pdFALSE, when the DNS cache must
153  *                     be updated.
154  * @return whether the operation was successful
155  * @post the global structure \a xDNSCache might be modified
156  */
FreeRTOS_ProcessDNSCache(const char * pcName,uint32_t * pulIP,uint32_t ulTTL,BaseType_t xLookUp)157     BaseType_t FreeRTOS_ProcessDNSCache( const char * pcName,
158                                          uint32_t * pulIP,
159                                          uint32_t ulTTL,
160                                          BaseType_t xLookUp )
161     {
162         UBaseType_t uxIndex;
163         BaseType_t xResult;
164         TickType_t xCurrentTickCount = xTaskGetTickCount();
165         uint32_t ulCurrentTimeSeconds;
166 
167         configASSERT( ( pcName != NULL ) );
168 
169         ulCurrentTimeSeconds = ( xCurrentTickCount / portTICK_PERIOD_MS ) / 1000U;
170         xResult = prvFindEntryIndex( pcName, &uxIndex );
171 
172         if( xResult == pdTRUE )
173         { /* Element found */
174             if( xLookUp == pdTRUE )
175             {
176                 /* This statement can only be reached when xResult is true; which
177                  * implies that the entry is present and a 'get' operation will result
178                  * in success. Therefore, it is safe to ignore the return value of the
179                  * below function. */
180                 ( void ) prvGetCacheIPEntry( uxIndex,
181                                              pulIP,
182                                              ulCurrentTimeSeconds );
183             }
184             else
185             {
186                 prvUpdateCacheEntry( uxIndex,
187                                      ulTTL,
188                                      pulIP,
189                                      ulCurrentTimeSeconds );
190             }
191         }
192         else /* Element not Found xResult = pdFALSE */
193         {
194             if( xLookUp == pdTRUE )
195             {
196                 *pulIP = 0U;
197             }
198             else
199             {
200                 prvInsertCacheEntry( pcName,
201                                      ulTTL,
202                                      pulIP,
203                                      ulCurrentTimeSeconds );
204             }
205         }
206 
207         if( ( xLookUp == pdFALSE ) || ( *pulIP != 0U ) )
208         {
209             FreeRTOS_debug_printf( ( "FreeRTOS_ProcessDNSCache: %s: '%s' @ %xip (TTL %u)\n",
210                                      ( xLookUp != 0 ) ? "look-up" : "add",
211                                      pcName,
212                                      ( unsigned ) FreeRTOS_ntohl( *pulIP ),
213                                      ( unsigned ) FreeRTOS_ntohl( ulTTL ) ) );
214         }
215 
216         return xResult;
217     }
218 
219 /**
220  * @brief returns the index of the hostname entry in the dns cache.
221  * @param[in] pcName find it in the cache
222  * @param [out] xResult index number
223  * @returns res pdTRUE if index in found else pdFALSE
224  */
prvFindEntryIndex(const char * pcName,UBaseType_t * uxResult)225     static BaseType_t prvFindEntryIndex( const char * pcName,
226                                          UBaseType_t * uxResult )
227     {
228         BaseType_t xReturn = pdFALSE;
229         UBaseType_t uxIndex;
230 
231         /* For each entry in the DNS cache table. */
232         for( uxIndex = 0; uxIndex < ipconfigDNS_CACHE_ENTRIES; uxIndex++ )
233         {
234             if( xDNSCache[ uxIndex ].pcName[ 0 ] == ( char ) 0 )
235             { /* empty slot */
236                 continue;
237             }
238 
239             if( strcmp( xDNSCache[ uxIndex ].pcName, pcName ) == 0 )
240             { /* hostname found */
241                 xReturn = pdTRUE;
242                 *uxResult = uxIndex;
243                 break;
244             }
245         }
246 
247         return xReturn;
248     }
249 
250 /**
251  * @brief get entry at \p index from the cache
252  * @param[in]  uxIndex : index in the cache
253  * @param[out] pulIP fill it with the result
254  * @param[in]  ulCurrentTimeSeconds current time
255  * @returns    \c pdTRUE if the value is valid \c pdFALSE otherwise
256  * @post the global structure \a xDNSCache might be modified
257  *
258  */
prvGetCacheIPEntry(UBaseType_t uxIndex,uint32_t * pulIP,uint32_t ulCurrentTimeSeconds)259     static BaseType_t prvGetCacheIPEntry( UBaseType_t uxIndex,
260                                           uint32_t * pulIP,
261                                           uint32_t ulCurrentTimeSeconds )
262     {
263         BaseType_t isRead;
264         uint32_t ulIPAddressIndex = 0;
265         uint32_t ulAge = ulCurrentTimeSeconds - xDNSCache[ uxIndex ].ulTimeWhenAddedInSeconds;
266 
267         /* Confirm that the record is still fresh.
268          * The field ulTTL was stored as network-endian. */
269         if( ulAge < FreeRTOS_ntohl( xDNSCache[ uxIndex ].ulTTL ) )
270         {
271             #if ( ipconfigDNS_CACHE_ADDRESSES_PER_ENTRY > 1 )
272                 uint8_t ucIndex;
273 
274                 /* The ucCurrentIPAddress value increments without bound and will rollover, */
275                 /*  modulo it by the number of IP addresses to keep it in range.     */
276                 /*  Also perform a final modulo by the max number of IP addresses    */
277                 /*  per DNS cache entry to prevent out-of-bounds access in the event */
278                 /*  that ucNumIPAddresses has been corrupted.                        */
279 
280                 ucIndex = xDNSCache[ uxIndex ].ucCurrentIPAddress % xDNSCache[ uxIndex ].ucNumIPAddresses;
281                 ucIndex = ucIndex % ( uint8_t ) ipconfigDNS_CACHE_ADDRESSES_PER_ENTRY;
282                 ulIPAddressIndex = ucIndex;
283 
284                 xDNSCache[ uxIndex ].ucCurrentIPAddress++;
285             #endif /* if ( ipconfigDNS_CACHE_ADDRESSES_PER_ENTRY > 1 ) */
286 
287             *pulIP = xDNSCache[ uxIndex ].ulIPAddresses[ ulIPAddressIndex ];
288             isRead = pdTRUE;
289         }
290         else
291         {
292             /* Age out the old cached record. */
293             xDNSCache[ uxIndex ].pcName[ 0 ] = ( char ) 0;
294             isRead = pdFALSE;
295         }
296 
297         return isRead;
298     }
299 
300 /**
301  * @brief update entry at \p index in the cache
302  * @param[in] uxIndex : index in the cache
303  * @param[in] ulTTL time to live (in seconds)
304  * @param[in] pulIP ip to update the cache with
305  * @param[in] ulCurrentTimeSeconds current time
306  * @post the global structure \a xDNSCache is modified
307  *
308  */
prvUpdateCacheEntry(UBaseType_t uxIndex,uint32_t ulTTL,const uint32_t * pulIP,uint32_t ulCurrentTimeSeconds)309     static void prvUpdateCacheEntry( UBaseType_t uxIndex,
310                                      uint32_t ulTTL,
311                                      const uint32_t * pulIP,
312                                      uint32_t ulCurrentTimeSeconds )
313     {
314         uint32_t ulIPAddressIndex = 0;
315 
316         #if ( ipconfigDNS_CACHE_ADDRESSES_PER_ENTRY > 1 )
317             if( xDNSCache[ uxIndex ].ucNumIPAddresses <
318                 ( uint8_t ) ipconfigDNS_CACHE_ADDRESSES_PER_ENTRY )
319             {
320                 /* If more answers exist than there are IP address storage
321                  * slots they will overwrite entry 0 */
322                 ulIPAddressIndex = xDNSCache[ uxIndex ].ucNumIPAddresses;
323                 xDNSCache[ uxIndex ].ucNumIPAddresses++;
324             }
325         #endif
326         xDNSCache[ uxIndex ].ulIPAddresses[ ulIPAddressIndex ] = *pulIP;
327         xDNSCache[ uxIndex ].ulTTL = ulTTL;
328         xDNSCache[ uxIndex ].ulTimeWhenAddedInSeconds = ulCurrentTimeSeconds;
329     }
330 
331 /**
332  * @brief insert entry in the cache
333  * @param[in] pcName cache entry key
334  * @param[in] ulTTL time to live (in seconds)
335  * @param[in] pulIP ip address
336  * @param[in] ulCurrentTimeSeconds current time
337  * @post the global structure \a xDNSCache is modified
338  */
prvInsertCacheEntry(const char * pcName,uint32_t ulTTL,const uint32_t * pulIP,uint32_t ulCurrentTimeSeconds)339     static void prvInsertCacheEntry( const char * pcName,
340                                      uint32_t ulTTL,
341                                      const uint32_t * pulIP,
342                                      uint32_t ulCurrentTimeSeconds )
343     {
344         /* Add or update the item. */
345         if( strlen( pcName ) < ( size_t ) ipconfigDNS_CACHE_NAME_LENGTH )
346         {
347             ( void ) strcpy( xDNSCache[ uxFreeEntry ].pcName, pcName );
348 
349             xDNSCache[ uxFreeEntry ].ulIPAddresses[ 0 ] = *pulIP;
350             xDNSCache[ uxFreeEntry ].ulTTL = ulTTL;
351             xDNSCache[ uxFreeEntry ].ulTimeWhenAddedInSeconds = ulCurrentTimeSeconds;
352             #if ( ipconfigDNS_CACHE_ADDRESSES_PER_ENTRY > 1 )
353                 xDNSCache[ uxFreeEntry ].ucNumIPAddresses = 1;
354                 xDNSCache[ uxFreeEntry ].ucCurrentIPAddress = 0;
355 
356                 /* Initialize all remaining IP addresses in this entry to 0 */
357                 ( void ) memset( &xDNSCache[ uxFreeEntry ].ulIPAddresses[ 1 ],
358                                  0,
359                                  sizeof( xDNSCache[ uxFreeEntry ].ulIPAddresses[ 1 ] ) *
360                                  ( ( uint32_t ) ipconfigDNS_CACHE_ADDRESSES_PER_ENTRY - 1U ) );
361             #endif
362             uxFreeEntry++;
363 
364             if( uxFreeEntry == ipconfigDNS_CACHE_ENTRIES )
365             {
366                 uxFreeEntry = 0;
367             }
368         }
369     }
370 
371 #endif /* if ( ( ipconfigUSE_DNS != 0 ) && ( ipconfigUSE_DNS_CACHE == 1 ) ) */
372