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