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_Parser.c 30 * @brief Implements the DNS message parser 31 */ 32 33 /* FreeRTOS includes. */ 34 #include "FreeRTOS.h" 35 /* FreeRTOS+TCP includes. */ 36 #include "FreeRTOS_IP.h" 37 #include "FreeRTOS_IP_Private.h" 38 39 #include "FreeRTOS_DNS_Globals.h" 40 #include "FreeRTOS_DNS_Parser.h" 41 #include "FreeRTOS_DNS_Cache.h" 42 #include "FreeRTOS_DNS_Callback.h" 43 44 #include "NetworkBufferManagement.h" 45 46 #include <string.h> 47 48 #if ( ipconfigUSE_DNS != 0 ) 49 50 /** @brief The list of all callback structures. */ 51 52 /** 53 * @brief Read the Name field out of a DNS response packet. 54 * 55 * @param[in] pucByte: Pointer to the DNS response. 56 * @param[in] uxRemainingBytes: Length of the DNS response. 57 * @param[out] pcName: The pointer in which the name in the DNS response will be returned. 58 * @param[in] uxDestLen: Size of the pcName array. 59 * 60 * @return If a fully formed name was found, then return the number of bytes processed in pucByte. 61 */ DNS_ReadNameField(const uint8_t * pucByte,size_t uxRemainingBytes,char * pcName,size_t uxDestLen)62 size_t DNS_ReadNameField( const uint8_t * pucByte, 63 size_t uxRemainingBytes, 64 char * pcName, 65 size_t uxDestLen ) 66 { 67 size_t uxNameLen = 0U; 68 size_t uxIndex = 0U; 69 size_t uxSourceLen = uxRemainingBytes; 70 71 /* uxCount gets the values from pucByte and counts down to 0. 72 * No need to have a different type than that of pucByte */ 73 size_t uxCount; 74 75 if( uxSourceLen == ( size_t ) 0U ) 76 { 77 /* Return 0 value in case of error. */ 78 uxIndex = 0U; 79 } 80 81 /* Determine if the name is the fully coded name, or an offset to the name 82 * elsewhere in the message. */ 83 else if( ( pucByte[ uxIndex ] & dnsNAME_IS_OFFSET ) == dnsNAME_IS_OFFSET ) 84 { 85 /* Jump over the two byte offset. */ 86 if( uxSourceLen > sizeof( uint16_t ) ) 87 { 88 uxIndex += sizeof( uint16_t ); 89 } 90 else 91 { 92 uxIndex = 0U; 93 } 94 } 95 else 96 { 97 /* 'uxIndex' points to the full name. Walk over the string. */ 98 while( ( uxIndex < uxSourceLen ) && ( pucByte[ uxIndex ] != ( uint8_t ) 0x00U ) ) 99 { 100 /* If this is not the first time through the loop, then add a 101 * separator in the output. */ 102 if( ( uxNameLen > 0U ) ) 103 { 104 if( uxNameLen >= uxDestLen ) 105 { 106 uxIndex = 0U; 107 /* coverity[break_stmt] : Break statement terminating the loop */ 108 break; 109 } 110 111 pcName[ uxNameLen ] = '.'; 112 uxNameLen++; 113 } 114 115 /* Process the first/next sub-string. */ 116 uxCount = ( size_t ) pucByte[ uxIndex ]; 117 uxIndex++; 118 119 if( ( uxIndex + uxCount ) > uxSourceLen ) 120 { 121 uxIndex = 0U; 122 break; 123 } 124 125 while( uxCount-- != 0U ) 126 { 127 if( uxNameLen >= uxDestLen ) 128 { 129 uxIndex = 0U; 130 break; 131 132 /* break out of inner loop here 133 * break out of outer loop at the test uxNameLen >= uxDestLen. */ 134 } 135 136 pcName[ uxNameLen ] = ( char ) pucByte[ uxIndex ]; 137 uxNameLen++; 138 uxIndex++; 139 } 140 } 141 142 /* Confirm that a fully formed name was found. */ 143 if( uxIndex > 0U ) 144 { 145 /* Here, there is no need to check for pucByte[ uxindex ] == 0 because: 146 * When we break out of the above while loop, uxIndex is made 0 thereby 147 * failing above check. Whenever we exit the loop otherwise, either 148 * pucByte[ uxIndex ] == 0 (which makes the check here unnecessary) or 149 * uxIndex >= uxSourceLen (which makes sure that we do not go in the 'if' 150 * case). 151 */ 152 if( ( uxNameLen < uxDestLen ) && ( uxIndex < uxSourceLen ) ) 153 { 154 pcName[ uxNameLen ] = '\0'; 155 uxIndex++; 156 } 157 else 158 { 159 uxIndex = 0U; 160 } 161 } 162 } 163 164 return uxIndex; 165 } 166 167 /** 168 * @brief Simple routine that jumps over the NAME field of a resource record. 169 * 170 * @param[in] pucByte: The pointer to the resource record. 171 * @param[in] uxLength: Length of the resource record. 172 * 173 * @return It returns the number of bytes read, or zero when an error has occurred. 174 */ DNS_SkipNameField(const uint8_t * pucByte,size_t uxLength)175 size_t DNS_SkipNameField( const uint8_t * pucByte, 176 size_t uxLength ) 177 { 178 size_t uxChunkLength; 179 size_t uxSourceLenCpy = uxLength; 180 size_t uxIndex = 0U; 181 182 if( uxSourceLenCpy == 0U ) 183 { 184 uxIndex = 0U; 185 } 186 187 /* Determine if the name is the fully coded name, or an offset to the name 188 * elsewhere in the message. */ 189 else if( ( pucByte[ uxIndex ] & dnsNAME_IS_OFFSET ) == dnsNAME_IS_OFFSET ) 190 { 191 /* Jump over the two byte offset. */ 192 if( uxSourceLenCpy > sizeof( uint16_t ) ) 193 { 194 uxIndex += sizeof( uint16_t ); 195 } 196 else 197 { 198 uxIndex = 0U; 199 } 200 } 201 else 202 { 203 /* pucByte points to the full name. Walk over the string. */ 204 while( ( pucByte[ uxIndex ] != 0U ) && ( uxSourceLenCpy > 1U ) ) 205 { 206 /* Conversion to size_t causes addition to be done 207 * in size_t */ 208 uxChunkLength = ( ( size_t ) pucByte[ uxIndex ] ) + 1U; 209 210 if( uxSourceLenCpy > uxChunkLength ) 211 { 212 uxSourceLenCpy -= uxChunkLength; 213 uxIndex += uxChunkLength; 214 } 215 else 216 { 217 uxIndex = 0U; 218 break; 219 } 220 } 221 222 /* Confirm that a fully formed name was found. */ 223 if( uxIndex > 0U ) 224 { 225 if( pucByte[ uxIndex ] == 0U ) 226 { 227 uxIndex++; 228 } 229 else 230 { 231 uxIndex = 0U; 232 } 233 } 234 } 235 236 return uxIndex; 237 } 238 239 /** 240 * @brief Process a response packet from a DNS server, or an LLMNR reply. 241 * 242 * @param[in] pucUDPPayloadBuffer: The DNS response received as a UDP 243 * payload. 244 * @param[in] uxBufferLength: Length of the UDP payload buffer. 245 * @param[in] xExpected: indicates whether the identifier in the reply 246 * was expected, and thus if the DNS cache may be 247 * updated with the reply. 248 * 249 * @return The IP address in the DNS response if present and if xExpected is set to pdTRUE. 250 * An error code (dnsPARSE_ERROR) if there was an error in the DNS response. 251 * 0 if xExpected set to pdFALSE. 252 */ DNS_ParseDNSReply(uint8_t * pucUDPPayloadBuffer,size_t uxBufferLength,BaseType_t xExpected)253 uint32_t DNS_ParseDNSReply( uint8_t * pucUDPPayloadBuffer, 254 size_t uxBufferLength, 255 BaseType_t xExpected ) 256 { 257 DNSMessage_t * pxDNSMessageHeader; 258 uint32_t ulIPAddress = 0U; 259 260 #if ( ipconfigUSE_LLMNR == 1 ) 261 char * pcRequestedName = NULL; 262 #endif 263 uint8_t * pucByte; 264 size_t uxSourceBytesRemaining; 265 uint16_t x; 266 uint16_t usQuestions; 267 BaseType_t xReturn = pdTRUE; 268 269 #if ( ipconfigUSE_LLMNR == 1 ) 270 uint16_t usType = 0U; 271 uint16_t usClass = 0U; 272 #endif 273 #if ( ipconfigUSE_DNS_CACHE == 1 ) || ( ipconfigDNS_USE_CALLBACKS == 1 ) 274 BaseType_t xDoStore = xExpected; 275 char pcName[ ipconfigDNS_CACHE_NAME_LENGTH ] = ""; 276 #endif 277 278 /* Ensure that the buffer is of at least minimal DNS message length. */ 279 if( uxBufferLength < sizeof( DNSMessage_t ) ) 280 { 281 xReturn = pdFALSE; 282 } 283 else 284 { 285 uxSourceBytesRemaining = uxBufferLength; 286 287 /* Parse the DNS message header. Map the byte stream onto a structure 288 * for easier access. */ 289 290 /* MISRA Ref 11.3.1 [Misaligned access] */ 291 /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ 292 /* coverity[misra_c_2012_rule_11_3_violation] */ 293 pxDNSMessageHeader = ( ( DNSMessage_t * ) 294 pucUDPPayloadBuffer ); 295 296 /* Introduce a do {} while (0) to allow the use of breaks. */ 297 do 298 { 299 size_t uxBytesRead = 0U; 300 size_t uxResult; 301 302 /* Start at the first byte after the header. */ 303 pucByte = &( pucUDPPayloadBuffer[ sizeof( DNSMessage_t ) ] ); 304 uxSourceBytesRemaining -= sizeof( DNSMessage_t ); 305 306 /* Skip any question records. */ 307 usQuestions = FreeRTOS_ntohs( pxDNSMessageHeader->usQuestions ); 308 309 for( x = 0U; x < usQuestions; x++ ) 310 { 311 #if ( ipconfigUSE_LLMNR == 1 ) 312 { 313 if( x == 0U ) 314 { 315 pcRequestedName = ( char * ) pucByte; 316 } 317 } 318 #endif 319 320 #if ( ipconfigUSE_DNS_CACHE == 1 ) || ( ipconfigDNS_USE_CALLBACKS == 1 ) 321 if( x == 0U ) 322 { 323 uxResult = DNS_ReadNameField( pucByte, 324 uxSourceBytesRemaining, 325 pcName, 326 sizeof( pcName ) ); 327 } 328 else 329 #endif /* ipconfigUSE_DNS_CACHE || ipconfigDNS_USE_CALLBACKS */ 330 { 331 /* Skip the variable length pcName field. */ 332 uxResult = DNS_SkipNameField( pucByte, 333 uxSourceBytesRemaining ); 334 } 335 336 /* Check for a malformed response. */ 337 if( uxResult == 0U ) 338 { 339 xReturn = pdFALSE; 340 break; 341 } 342 343 uxBytesRead += uxResult; 344 pucByte = &( pucByte[ uxResult ] ); 345 uxSourceBytesRemaining -= uxResult; 346 347 /* Check the remaining buffer size. */ 348 if( uxSourceBytesRemaining >= sizeof( uint32_t ) ) 349 { 350 #if ( ipconfigUSE_LLMNR == 1 ) 351 { 352 /* usChar2u16 returns value in host endianness. */ 353 usType = usChar2u16( pucByte ); 354 usClass = usChar2u16( &( pucByte[ 2 ] ) ); 355 } 356 #endif /* ipconfigUSE_LLMNR */ 357 358 /* Skip the type and class fields. */ 359 pucByte = &( pucByte[ sizeof( uint32_t ) ] ); 360 uxSourceBytesRemaining -= sizeof( uint32_t ); 361 } 362 else 363 { 364 xReturn = pdFALSE; 365 break; 366 } 367 } 368 369 if( xReturn == pdFALSE ) 370 { 371 /* No need to proceed. Break out of the do-while loop. */ 372 break; 373 } 374 375 /* Search through the answer records. */ 376 pxDNSMessageHeader->usAnswers = 377 FreeRTOS_ntohs( pxDNSMessageHeader->usAnswers ); 378 379 if( ( pxDNSMessageHeader->usFlags & dnsRX_FLAGS_MASK ) 380 == dnsEXPECTED_RX_FLAGS ) 381 { 382 ulIPAddress = parseDNSAnswer( pxDNSMessageHeader, 383 pucByte, 384 uxSourceBytesRemaining, 385 &uxBytesRead 386 #if ( ipconfigUSE_DNS_CACHE == 1 ) || ( ipconfigDNS_USE_CALLBACKS == 1 ) 387 , 388 pcName, 389 xDoStore 390 #endif 391 ); 392 } 393 394 #if ( ipconfigUSE_LLMNR == 1 ) 395 396 /* No need to check that pcRequestedName != NULL since sQuestions != 0, then 397 * pcRequestedName is assigned with this statement 398 * "pcRequestedName = ( char * ) pucByte;" */ 399 else if( ( usQuestions != ( uint16_t ) 0U ) && 400 ( usType == dnsTYPE_A_HOST ) && 401 ( usClass == dnsCLASS_IN ) ) 402 { 403 /* If this is not a reply to our DNS request, it might an LLMNR 404 * request. */ 405 if( xApplicationDNSQueryHook( &( pcRequestedName[ 1 ] ) ) != pdFALSE ) 406 { 407 int16_t usLength; 408 NetworkBufferDescriptor_t * pxNewBuffer = NULL; 409 NetworkBufferDescriptor_t * pxNetworkBuffer = 410 pxUDPPayloadBuffer_to_NetworkBuffer( pucUDPPayloadBuffer ); 411 LLMNRAnswer_t * pxAnswer; 412 uint8_t * pucNewBuffer = NULL; 413 414 if( pxNetworkBuffer != NULL ) 415 { 416 if( xBufferAllocFixedSize == pdFALSE ) 417 { 418 size_t uxDataLength = uxBufferLength + 419 sizeof( UDPHeader_t ) + 420 sizeof( EthernetHeader_t ) + 421 sizeof( IPHeader_t ); 422 423 /* Set the size of the outgoing packet. */ 424 pxNetworkBuffer->xDataLength = uxDataLength; 425 pxNewBuffer = pxDuplicateNetworkBufferWithDescriptor( pxNetworkBuffer, 426 uxDataLength + 427 sizeof( LLMNRAnswer_t ) ); 428 429 if( pxNewBuffer != NULL ) 430 { 431 BaseType_t xOffset1, xOffset2; 432 433 xOffset1 = ( BaseType_t ) ( pucByte - pucUDPPayloadBuffer ); 434 xOffset2 = ( BaseType_t ) ( ( ( uint8_t * ) pcRequestedName ) - pucUDPPayloadBuffer ); 435 436 pxNetworkBuffer = pxNewBuffer; 437 pucNewBuffer = &( pxNetworkBuffer->pucEthernetBuffer[ ipUDP_PAYLOAD_OFFSET_IPv4 ] ); 438 439 pucByte = &( pucNewBuffer[ xOffset1 ] ); 440 pcRequestedName = ( char * ) &( pucNewBuffer[ xOffset2 ] ); 441 pxDNSMessageHeader = ( ( DNSMessage_t * ) pucNewBuffer ); 442 } 443 else 444 { 445 /* Just to indicate that the message may not be answered. */ 446 pxNetworkBuffer = NULL; 447 } 448 } 449 else 450 { 451 pucNewBuffer = &( pxNetworkBuffer->pucEthernetBuffer[ ipUDP_PAYLOAD_OFFSET_IPv4 ] ); 452 } 453 } 454 455 if( ( pxNetworkBuffer != NULL ) ) 456 { 457 pxAnswer = ( ( LLMNRAnswer_t * ) pucByte ); 458 /* We leave 'usIdentifier' and 'usQuestions' untouched */ 459 #ifndef _lint 460 vSetField16( pxDNSMessageHeader, DNSMessage_t, usFlags, dnsLLMNR_FLAGS_IS_REPONSE ); /* Set the response flag */ 461 vSetField16( pxDNSMessageHeader, DNSMessage_t, usAnswers, 1 ); /* Provide a single answer */ 462 vSetField16( pxDNSMessageHeader, DNSMessage_t, usAuthorityRRs, 0 ); /* No authority */ 463 vSetField16( pxDNSMessageHeader, DNSMessage_t, usAdditionalRRs, 0 ); /* No additional info */ 464 #endif /* lint */ 465 466 pxAnswer->ucNameCode = dnsNAME_IS_OFFSET; 467 pxAnswer->ucNameOffset = ( uint8_t ) ( pcRequestedName - ( char * ) pucNewBuffer ); 468 469 #ifndef _lint 470 vSetField16( pxAnswer, LLMNRAnswer_t, usType, dnsTYPE_A_HOST ); /* Type A: host */ 471 vSetField16( pxAnswer, LLMNRAnswer_t, usClass, dnsCLASS_IN ); /* 1: Class IN */ 472 vSetField32( pxAnswer, LLMNRAnswer_t, ulTTL, dnsLLMNR_TTL_VALUE ); 473 vSetField16( pxAnswer, LLMNRAnswer_t, usDataLength, 4 ); 474 vSetField32( pxAnswer, LLMNRAnswer_t, 475 ulIPAddress, 476 FreeRTOS_ntohl( *ipLOCAL_IP_ADDRESS_POINTER ) ); 477 #endif /* lint */ 478 usLength = ( int16_t ) ( sizeof( *pxAnswer ) + ( size_t ) ( pucByte - pucNewBuffer ) ); 479 480 prepareReplyDNSMessage( pxNetworkBuffer, usLength ); 481 /* This function will fill in the eth addresses and send the packet */ 482 vReturnEthernetFrame( pxNetworkBuffer, pdFALSE ); 483 484 if( pxNewBuffer != NULL ) 485 { 486 vReleaseNetworkBufferAndDescriptor( pxNewBuffer ); 487 } 488 } 489 } 490 } 491 else 492 { 493 /* Not an expected reply. */ 494 } 495 #endif /* ipconfigUSE_LLMNR == 1 */ 496 ( void ) uxBytesRead; 497 } while( ipFALSE_BOOL ); 498 } 499 500 if( xReturn == pdFALSE ) 501 { 502 /* There was an error while parsing the DNS response. Return error code. */ 503 ulIPAddress = ( uint32_t ) dnsPARSE_ERROR; 504 } 505 else if( xExpected == pdFALSE ) 506 { 507 /* Do not return a valid IP-address in case the reply was not expected. */ 508 ulIPAddress = 0U; 509 } 510 else 511 { 512 /* The IP-address found will be returned. */ 513 } 514 515 #if ( ipconfigUSE_DNS_CACHE == 1 ) || ( ipconfigDNS_USE_CALLBACKS == 1 ) 516 ( void ) xDoStore; 517 #endif 518 519 return ulIPAddress; 520 } 521 522 /** 523 * @brief perform a dns lookup in the local cache 524 * @param[in] pxDNSMessageHeader DNS header 525 * @param pucByte buffer 526 * @param uxSourceBytesRemaining remaining bytes in pucByte 527 * @param[out] uxBytesRead total bytes consumed by the function 528 * @param pcName update the cache and /or send to callback 529 * @param xDoStore whether to update the cache 530 * @return ip address extracted from the frame or zero if not found 531 */ parseDNSAnswer(const DNSMessage_t * pxDNSMessageHeader,uint8_t * pucByte,size_t uxSourceBytesRemaining,size_t * uxBytesRead,const char * pcName,BaseType_t xDoStore)532 uint32_t parseDNSAnswer( const DNSMessage_t * pxDNSMessageHeader, 533 uint8_t * pucByte, 534 size_t uxSourceBytesRemaining, 535 size_t * uxBytesRead 536 #if ( ipconfigUSE_DNS_CACHE == 1 ) || ( ipconfigDNS_USE_CALLBACKS == 1 ) 537 , 538 const char * pcName, 539 BaseType_t xDoStore 540 #endif 541 ) 542 { 543 uint16_t x; 544 size_t uxResult; 545 const uint16_t usCount = ( uint16_t ) ipconfigDNS_CACHE_ADDRESSES_PER_ENTRY; 546 uint16_t usNumARecordsStored = 0; 547 BaseType_t xReturn = pdTRUE; 548 uint16_t usType = 0U; 549 const DNSAnswerRecord_t * pxDNSAnswerRecord; 550 const void * pvCopySource; 551 void * pvCopyDest; 552 const size_t uxAddressLength = ipSIZE_OF_IPv4_ADDRESS; 553 uint32_t ulIPAddress = 0U; 554 uint32_t ulReturnIPAddress = 0U; 555 uint16_t usDataLength; 556 uint8_t * pucBuffer = pucByte; 557 size_t uxRxSourceByteRemaining = uxSourceBytesRemaining; 558 559 for( x = 0U; x < pxDNSMessageHeader->usAnswers; x++ ) 560 { 561 BaseType_t xDoAccept; 562 563 if( usNumARecordsStored >= usCount ) 564 { 565 /* Only count ipconfigDNS_CACHE_ADDRESSES_PER_ENTRY number of records. */ 566 break; 567 } 568 569 uxResult = DNS_SkipNameField( pucBuffer, 570 uxRxSourceByteRemaining ); 571 572 /* Check for a malformed response. */ 573 if( uxResult == 0U ) 574 { 575 xReturn = pdFALSE; 576 break; 577 } 578 579 *uxBytesRead += uxResult; 580 pucBuffer = &( pucBuffer[ uxResult ] ); 581 uxRxSourceByteRemaining -= uxResult; 582 583 /* Is there enough data for an IPv4 A record answer and, if so, 584 * is this an A record? */ 585 if( uxRxSourceByteRemaining < sizeof( uint16_t ) ) 586 { 587 xReturn = pdFALSE; 588 break; 589 } 590 591 usType = usChar2u16( pucBuffer ); 592 593 if( usType == ( uint16_t ) dnsTYPE_A_HOST ) 594 { 595 if( uxRxSourceByteRemaining >= ( sizeof( DNSAnswerRecord_t ) + uxAddressLength ) ) 596 { 597 xDoAccept = pdTRUE; 598 } 599 else 600 { 601 xDoAccept = pdFALSE; 602 } 603 } 604 else 605 { 606 /* Unknown host type. */ 607 xDoAccept = pdFALSE; 608 } 609 610 if( xDoAccept != pdFALSE ) 611 { 612 /* This is the required record type and is of sufficient size. */ 613 614 /* Mapping pucBuffer to a DNSAnswerRecord allows easy access of the 615 * fields of the structure. */ 616 617 /* MISRA Ref 11.3.1 [Misaligned access] */ 618 /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ 619 /* coverity[misra_c_2012_rule_11_3_violation] */ 620 pxDNSAnswerRecord = ( ( DNSAnswerRecord_t * ) pucBuffer ); 621 622 /* Sanity check the data length of an IPv4 answer. */ 623 if( FreeRTOS_ntohs( pxDNSAnswerRecord->usDataLength ) == 624 ( uint16_t ) uxAddressLength ) 625 { 626 /* Copy the IP address out of the record. Using different pointers 627 * to copy only the portion we want is intentional here. */ 628 629 /* 630 * Use helper variables for memcpy() to remain 631 * compliant with MISRA Rule 21.15. These should be 632 * optimized away. 633 */ 634 pvCopySource = &pucBuffer[ sizeof( DNSAnswerRecord_t ) ]; 635 pvCopyDest = &ulIPAddress; 636 ( void ) memcpy( pvCopyDest, pvCopySource, uxAddressLength ); 637 638 #if ( ipconfigDNS_USE_CALLBACKS == 1 ) 639 { 640 /* See if any asynchronous call was made to FreeRTOS_gethostbyname_a() */ 641 if( xDNSDoCallback( ( TickType_t ) pxDNSMessageHeader->usIdentifier, 642 pcName, 643 ulIPAddress ) != pdFALSE ) 644 { 645 /* This device has requested this DNS look-up. 646 * The result may be stored in the DNS cache. */ 647 xDoStore = pdTRUE; 648 } 649 } 650 #endif /* ipconfigDNS_USE_CALLBACKS == 1 */ 651 #if ( ipconfigUSE_DNS_CACHE == 1 ) 652 { 653 char cBuffer[ 16 ]; 654 655 /* The reply will only be stored in the DNS cache when the 656 * request was issued by this device. */ 657 if( xDoStore != pdFALSE ) 658 { 659 ( void ) FreeRTOS_dns_update( 660 pcName, 661 &ulIPAddress, 662 pxDNSAnswerRecord->ulTTL ); 663 usNumARecordsStored++; /* Track # of A records stored */ 664 } 665 666 ( void ) FreeRTOS_inet_ntop( FREERTOS_AF_INET, 667 ( const void * ) &( ulIPAddress ), 668 cBuffer, 669 ( socklen_t ) sizeof( cBuffer ) ); 670 /* Show what has happened. */ 671 FreeRTOS_printf( ( "DNS[0x%04lX]: The answer to '%s' (%s) will%s be stored\n", 672 ( UBaseType_t ) pxDNSMessageHeader->usIdentifier, 673 pcName, 674 cBuffer, 675 ( xDoStore != 0 ) ? "" : " NOT" ) ); 676 } 677 #endif /* ipconfigUSE_DNS_CACHE */ 678 679 if( ( ulReturnIPAddress == 0U ) && ( ulIPAddress != 0U ) ) 680 { 681 /* Remember the first IP-address that is found. */ 682 ulReturnIPAddress = ulIPAddress; 683 } 684 } 685 686 pucBuffer = &( pucBuffer[ sizeof( DNSAnswerRecord_t ) + uxAddressLength ] ); 687 uxRxSourceByteRemaining -= ( sizeof( DNSAnswerRecord_t ) + uxAddressLength ); 688 } 689 else if( uxRxSourceByteRemaining >= sizeof( DNSAnswerRecord_t ) ) 690 { 691 /* It's not an A record, so skip it. Get the header location 692 * and then jump over the header. */ 693 /* Cast the response to DNSAnswerRecord for easy access to fields of the DNS response. */ 694 695 /* MISRA Ref 11.3.1 [Misaligned access] */ 696 /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ 697 /* coverity[misra_c_2012_rule_11_3_violation] */ 698 pxDNSAnswerRecord = ( ( DNSAnswerRecord_t * ) pucBuffer ); 699 700 pucBuffer = &( pucBuffer[ sizeof( DNSAnswerRecord_t ) ] ); 701 uxRxSourceByteRemaining -= sizeof( DNSAnswerRecord_t ); 702 703 /* Determine the length of the answer data from the header. */ 704 usDataLength = FreeRTOS_ntohs( pxDNSAnswerRecord->usDataLength ); 705 706 /* Jump over the answer. */ 707 if( uxRxSourceByteRemaining >= usDataLength ) 708 { 709 pucBuffer = &( pucBuffer[ usDataLength ] ); 710 uxRxSourceByteRemaining -= usDataLength; 711 } 712 else 713 { 714 /* Malformed response. */ 715 xReturn = pdFALSE; 716 break; 717 } 718 } 719 else 720 { 721 /* Do nothing */ 722 } 723 } 724 725 return ( xReturn != 0 ) ? ulReturnIPAddress : 0U; 726 } 727 728 #if ( ( ipconfigUSE_NBNS == 1 ) || ( ipconfigUSE_LLMNR == 1 ) ) 729 730 /** 731 * @brief Send a DNS message to be used in NBNS or LLMNR 732 * 733 * @param[in] pxNetworkBuffer: The network buffer descriptor with the DNS message. 734 * @param[in] lNetLength: The length of the DNS message. 735 */ prepareReplyDNSMessage(NetworkBufferDescriptor_t * pxNetworkBuffer,BaseType_t lNetLength)736 void prepareReplyDNSMessage( NetworkBufferDescriptor_t * pxNetworkBuffer, 737 BaseType_t lNetLength ) 738 { 739 UDPPacket_t * pxUDPPacket; 740 IPHeader_t * pxIPHeader; 741 UDPHeader_t * pxUDPHeader; 742 size_t uxDataLength; 743 744 pxUDPPacket = ( ( UDPPacket_t * ) 745 pxNetworkBuffer->pucEthernetBuffer ); 746 pxIPHeader = &pxUDPPacket->xIPHeader; 747 pxUDPHeader = &pxUDPPacket->xUDPHeader; 748 /* HT: started using defines like 'ipSIZE_OF_xxx' */ 749 pxIPHeader->usLength = FreeRTOS_htons( ( uint16_t ) lNetLength + 750 ipSIZE_OF_IPv4_HEADER + 751 ipSIZE_OF_UDP_HEADER ); 752 /* HT:endian: should not be translated, copying from packet to packet */ 753 pxIPHeader->ulDestinationIPAddress = pxIPHeader->ulSourceIPAddress; 754 pxIPHeader->ulSourceIPAddress = *ipLOCAL_IP_ADDRESS_POINTER; 755 pxIPHeader->ucTimeToLive = ipconfigUDP_TIME_TO_LIVE; 756 pxIPHeader->usIdentification = FreeRTOS_htons( usPacketIdentifier ); 757 758 /* The stack doesn't support fragments, so the fragment offset field must always be zero. 759 * The header was never memset to zero, so set both the fragment offset and fragmentation flags in one go. 760 */ 761 #if ( ipconfigFORCE_IP_DONT_FRAGMENT != 0 ) 762 pxIPHeader->usFragmentOffset = ipFRAGMENT_FLAGS_DONT_FRAGMENT; 763 #else 764 pxIPHeader->usFragmentOffset = 0U; 765 #endif 766 usPacketIdentifier++; 767 pxUDPHeader->usLength = FreeRTOS_htons( ( uint32_t ) lNetLength + 768 ipSIZE_OF_UDP_HEADER ); 769 vFlip_16( pxUDPHeader->usSourcePort, pxUDPHeader->usDestinationPort ); 770 771 /* Important: tell NIC driver how many bytes must be sent */ 772 uxDataLength = ( ( size_t ) lNetLength ) + ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_UDP_HEADER + ipSIZE_OF_ETH_HEADER; 773 774 #if ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 0 ) 775 { 776 /* Calculate the IP header checksum. */ 777 pxIPHeader->usHeaderChecksum = 0U; 778 pxIPHeader->usHeaderChecksum = usGenerateChecksum( 0U, ( uint8_t * ) &( pxIPHeader->ucVersionHeaderLength ), ipSIZE_OF_IPv4_HEADER ); 779 pxIPHeader->usHeaderChecksum = ~FreeRTOS_htons( pxIPHeader->usHeaderChecksum ); 780 781 /* calculate the UDP checksum for outgoing package */ 782 ( void ) usGenerateProtocolChecksum( ( uint8_t * ) pxUDPPacket, uxDataLength, pdTRUE ); 783 } 784 #endif 785 786 /* Important: tell NIC driver how many bytes must be sent */ 787 pxNetworkBuffer->xDataLength = uxDataLength; 788 } 789 790 #endif /* ipconfigUSE_NBNS == 1 || ipconfigUSE_LLMNR == 1 */ 791 792 #if ( ipconfigUSE_NBNS == 1 ) 793 794 /** 795 * @brief Respond to an NBNS query or an NBNS reply. 796 * 797 * @param[in] pucPayload: the UDP payload of the NBNS message. 798 * @param[in] uxBufferLength: Length of the Buffer. 799 * @param[in] ulIPAddress: IP address of the sender. 800 */ DNS_TreatNBNS(uint8_t * pucPayload,size_t uxBufferLength,uint32_t ulIPAddress)801 void DNS_TreatNBNS( uint8_t * pucPayload, 802 size_t uxBufferLength, 803 uint32_t ulIPAddress ) 804 { 805 uint16_t usFlags; 806 uint16_t usType; 807 uint16_t usClass; 808 uint8_t * pucSource; 809 uint8_t * pucTarget; 810 uint8_t ucByte; 811 uint8_t ucNBNSName[ 17 ]; 812 uint8_t * pucUDPPayloadBuffer = pucPayload; 813 NetworkBufferDescriptor_t * pxNetworkBuffer; 814 815 /* Read the request flags in host endianness. */ 816 usFlags = usChar2u16( &( pucUDPPayloadBuffer[ offsetof( NBNSRequest_t, usFlags ) ] ) ); 817 818 if( ( usFlags & dnsNBNS_FLAGS_OPCODE_MASK ) == dnsNBNS_FLAGS_OPCODE_QUERY ) 819 { 820 usType = usChar2u16( &( pucUDPPayloadBuffer[ offsetof( NBNSRequest_t, usType ) ] ) ); 821 usClass = usChar2u16( &( pucUDPPayloadBuffer[ offsetof( NBNSRequest_t, usClass ) ] ) ); 822 823 /* Not used for now */ 824 ( void ) usClass; 825 826 /* For NBNS a name is 16 bytes long, written with capitals only. 827 * Make sure that the copy is terminated with a zero. */ 828 pucTarget = &( ucNBNSName[ sizeof( ucNBNSName ) - 2U ] ); 829 pucTarget[ 1 ] = ( uint8_t ) 0U; 830 831 /* Start with decoding the last 2 bytes. */ 832 pucSource = &( pucUDPPayloadBuffer[ ( dnsNBNS_ENCODED_NAME_LENGTH - 2 ) + 833 offsetof( NBNSRequest_t, ucName ) ] ); 834 835 for( ; ; ) 836 { 837 const uint8_t ucCharA = ( uint8_t ) 0x41U; 838 839 ucByte = ( ( uint8_t ) ( ( pucSource[ 0 ] - ucCharA ) << 4 ) ) | 840 ( pucSource[ 1 ] - ucCharA ); 841 842 /* Make sure there are no trailing spaces in the name. */ 843 if( ( ucByte == ( uint8_t ) ' ' ) && ( pucTarget[ 1 ] == 0U ) ) 844 { 845 ucByte = 0U; 846 } 847 848 *pucTarget = ucByte; 849 850 if( pucTarget == ucNBNSName ) 851 { 852 break; 853 } 854 855 pucTarget -= 1; 856 pucSource -= 2; 857 } 858 859 #if ( ipconfigUSE_DNS_CACHE == 1 ) 860 { 861 if( ( usFlags & dnsNBNS_FLAGS_RESPONSE ) != 0U ) 862 { 863 /* If this is a response from another device, 864 * add the name to the DNS cache */ 865 ( void ) FreeRTOS_dns_update( ( char * ) ucNBNSName, &( ulIPAddress ), 0 ); 866 } 867 } 868 #else 869 { 870 /* Avoid compiler warnings. */ 871 ( void ) ulIPAddress; 872 } 873 #endif /* ipconfigUSE_DNS_CACHE */ 874 875 if( ( ( usFlags & dnsNBNS_FLAGS_RESPONSE ) == 0U ) && 876 ( usType == dnsNBNS_TYPE_NET_BIOS ) && 877 ( xApplicationDNSQueryHook( ( const char * ) ucNBNSName ) != pdFALSE ) ) 878 { 879 uint16_t usLength; 880 DNSMessage_t * pxMessage; 881 NBNSAnswer_t * pxAnswer; 882 NetworkBufferDescriptor_t * pxNewBuffer = NULL; 883 884 /* Someone is looking for a device with ucNBNSName, 885 * prepare a positive reply. */ 886 pxNetworkBuffer = pxUDPPayloadBuffer_to_NetworkBuffer( pucUDPPayloadBuffer ); 887 888 if( ( xBufferAllocFixedSize == pdFALSE ) && 889 ( pxNetworkBuffer != NULL ) ) 890 { 891 /* The field xDataLength was set to the total length of the UDP packet, 892 * i.e. the payload size plus sizeof( UDPPacket_t ). */ 893 pxNewBuffer = pxDuplicateNetworkBufferWithDescriptor( pxNetworkBuffer, pxNetworkBuffer->xDataLength + sizeof( NBNSAnswer_t ) ); 894 895 if( pxNewBuffer != NULL ) 896 { 897 pucUDPPayloadBuffer = &( pxNewBuffer->pucEthernetBuffer[ sizeof( UDPPacket_t ) ] ); 898 pxNetworkBuffer = pxNewBuffer; 899 } 900 else 901 { 902 /* Just prevent that a reply will be sent */ 903 pxNetworkBuffer = NULL; 904 } 905 } 906 907 /* Should not occur: pucUDPPayloadBuffer is part of a xNetworkBufferDescriptor */ 908 if( pxNetworkBuffer != NULL ) 909 { 910 pxMessage = ( ( DNSMessage_t * ) pucUDPPayloadBuffer ); 911 912 /* As the fields in the structures are not word-aligned, we have to 913 * copy the values byte-by-byte using macro's vSetField16() and vSetField32() */ 914 #ifndef _lint 915 vSetField16( pxMessage, DNSMessage_t, usFlags, dnsNBNS_QUERY_RESPONSE_FLAGS ); /* 0x8500 */ 916 vSetField16( pxMessage, DNSMessage_t, usQuestions, 0 ); 917 vSetField16( pxMessage, DNSMessage_t, usAnswers, 1 ); 918 vSetField16( pxMessage, DNSMessage_t, usAuthorityRRs, 0 ); 919 vSetField16( pxMessage, DNSMessage_t, usAdditionalRRs, 0 ); 920 #else 921 ( void ) pxMessage; 922 #endif 923 924 pxAnswer = ( ( NBNSAnswer_t * ) &( pucUDPPayloadBuffer[ offsetof( NBNSRequest_t, usType ) ] ) ); 925 926 #ifndef _lint 927 vSetField16( pxAnswer, NBNSAnswer_t, usType, usType ); /* Type */ 928 vSetField16( pxAnswer, NBNSAnswer_t, usClass, dnsNBNS_CLASS_IN ); /* Class */ 929 vSetField32( pxAnswer, NBNSAnswer_t, ulTTL, dnsNBNS_TTL_VALUE ); 930 vSetField16( pxAnswer, NBNSAnswer_t, usDataLength, 6 ); /* 6 bytes including the length field */ 931 vSetField16( pxAnswer, NBNSAnswer_t, usNbFlags, dnsNBNS_NAME_FLAGS ); 932 vSetField32( pxAnswer, NBNSAnswer_t, ulIPAddress, FreeRTOS_ntohl( *ipLOCAL_IP_ADDRESS_POINTER ) ); 933 #else 934 ( void ) pxAnswer; 935 #endif 936 937 usLength = ( uint16_t ) ( sizeof( NBNSAnswer_t ) + ( size_t ) offsetof( NBNSRequest_t, usType ) ); 938 939 prepareReplyDNSMessage( pxNetworkBuffer, ( BaseType_t ) usLength ); 940 /* This function will fill in the eth addresses and send the packet */ 941 vReturnEthernetFrame( pxNetworkBuffer, pdFALSE ); 942 943 if( pxNewBuffer != NULL ) 944 { 945 vReleaseNetworkBufferAndDescriptor( pxNewBuffer ); 946 } 947 } 948 } 949 } 950 } 951 952 #endif /* ipconfigUSE_NBNS */ 953 #endif /* ipconfigUSE_DNS != 0 */ 954