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.c 30 * @brief Implements the Domain Name System for the FreeRTOS+TCP network stack. 31 */ 32 33 /* Standard includes. */ 34 #include <stdint.h> 35 #include <stdio.h> 36 37 /* FreeRTOS includes. */ 38 #include "FreeRTOS.h" 39 #include "task.h" 40 #include "semphr.h" 41 42 /* FreeRTOS+TCP includes. */ 43 #include "FreeRTOS_DNS_Globals.h" 44 #include "FreeRTOS_IP.h" 45 #include "FreeRTOS_IP_Private.h" 46 #include "FreeRTOS_UDP_IP.h" 47 #include "FreeRTOS_DNS.h" 48 #include "FreeRTOS_DHCP.h" 49 #include "NetworkBufferManagement.h" 50 #include "NetworkInterface.h" 51 52 #include "FreeRTOS_DNS_Cache.h" 53 #include "FreeRTOS_DNS_Parser.h" 54 #include "FreeRTOS_DNS_Networking.h" 55 #include "FreeRTOS_DNS_Callback.h" 56 57 58 /* Exclude the entire file if DNS is not enabled. */ 59 #if ( ipconfigUSE_DNS != 0 ) 60 61 /* 62 * Create the DNS message in the zero copy buffer passed in the first parameter. 63 */ 64 _static size_t prvCreateDNSMessage( uint8_t * pucUDPPayloadBuffer, 65 const char * pcHostName, 66 TickType_t uxIdentifier ); 67 68 69 /* 70 * Check if hostname is already known. If not, call prvGetHostByName() to send a DNS request. 71 */ 72 #if ( ipconfigDNS_USE_CALLBACKS == 1 ) 73 static uint32_t prvPrepareLookup( const char * pcHostName, 74 FOnDNSEvent pCallback, 75 void * pvSearchID, 76 TickType_t uxTimeout ); 77 #else 78 static uint32_t prvPrepareLookup( const char * pcHostName ); 79 #endif 80 81 /* 82 * Prepare and send a message to a DNS server. 'uxReadTimeOut_ticks' will be passed as 83 * zero, in case the user has supplied a call-back function. 84 */ 85 static uint32_t prvGetHostByName( const char * pcHostName, 86 TickType_t uxIdentifier, 87 TickType_t uxReadTimeOut_ticks ); 88 89 #if ( ipconfigUSE_LLMNR == 1 ) 90 /** @brief The MAC address used for LLMNR. */ 91 const MACAddress_t xLLMNR_MacAdress = { { 0x01, 0x00, 0x5e, 0x00, 0x00, 0xfc } }; 92 #endif /* ipconfigUSE_LLMNR == 1 */ 93 94 /*-----------------------------------------------------------*/ 95 96 /** 97 * @brief A DNS query consists of a header, as described in 'struct xDNSMessage' 98 * It is followed by 1 or more queries, each one consisting of a name and a tail, 99 * with two fields: type and class 100 */ 101 #include "pack_struct_start.h" 102 struct xDNSTail 103 { 104 uint16_t usType; /**< Type of DNS message. */ 105 uint16_t usClass; /**< Class of DNS message. */ 106 } 107 #include "pack_struct_end.h" 108 typedef struct xDNSTail DNSTail_t; 109 /*-----------------------------------------------------------*/ 110 111 112 #if ( ipconfigDNS_USE_CALLBACKS == 1 ) 113 114 /** 115 * @brief Define FreeRTOS_gethostbyname() as a normal blocking call. 116 * @param[in] pcHostName: The hostname whose IP address is being searched for. 117 * @return The IP-address of the hostname. 118 */ FreeRTOS_gethostbyname(const char * pcHostName)119 uint32_t FreeRTOS_gethostbyname( const char * pcHostName ) 120 { 121 return FreeRTOS_gethostbyname_a( pcHostName, NULL, ( void * ) NULL, 0U ); 122 } 123 /*-----------------------------------------------------------*/ 124 125 /** @brief Initialise the list of call-back structures. 126 */ vDNSInitialise(void)127 void vDNSInitialise( void ) 128 { 129 vDNSCallbackInitialise(); 130 } 131 /*-----------------------------------------------------------*/ 132 133 134 /** 135 * @brief Remove the entry defined by the search ID to cancel a DNS request. 136 * @param[in] pvSearchID: The search ID of the callback function associated with 137 * the DNS request being cancelled. Note that the value of 138 * the pointer matters, not the pointee. 139 */ FreeRTOS_gethostbyname_cancel(void * pvSearchID)140 void FreeRTOS_gethostbyname_cancel( void * pvSearchID ) 141 { 142 /* _HT_ Should better become a new API call to have the IP-task remove the callback */ 143 vDNSCheckCallBack( pvSearchID ); 144 } 145 /*-----------------------------------------------------------*/ 146 147 /*-----------------------------------------------------------*/ 148 149 150 #endif /* ipconfigDNS_USE_CALLBACKS == 1 */ 151 /*-----------------------------------------------------------*/ 152 153 #if ( ipconfigDNS_USE_CALLBACKS == 0 ) 154 155 /** 156 * @brief Get the IP-address corresponding to the given hostname. 157 * @param[in] pcHostName: The hostname whose IP address is being queried. 158 * @return The IP-address corresponding to the hostname. 0 is returned in 159 * case of failure. 160 */ FreeRTOS_gethostbyname(const char * pcHostName)161 uint32_t FreeRTOS_gethostbyname( const char * pcHostName ) 162 { 163 return prvPrepareLookup( pcHostName ); 164 } 165 #else 166 167 /** 168 * @brief Get the IP-address corresponding to the given hostname. 169 * @param[in] pcHostName: The hostname whose IP address is being queried. 170 * @param[in] pCallback: The callback function which will be called upon DNS response. 171 * @param[in] pvSearchID: Search ID for the callback function. 172 * @param[in] uxTimeout: Timeout for the callback function. 173 * @return The IP-address corresponding to the hostname. 0 is returned in case of 174 * failure. 175 */ FreeRTOS_gethostbyname_a(const char * pcHostName,FOnDNSEvent pCallback,void * pvSearchID,TickType_t uxTimeout)176 uint32_t FreeRTOS_gethostbyname_a( const char * pcHostName, 177 FOnDNSEvent pCallback, 178 void * pvSearchID, 179 TickType_t uxTimeout ) 180 { 181 return prvPrepareLookup( pcHostName, pCallback, pvSearchID, uxTimeout ); 182 } 183 #endif /* if ( ipconfigDNS_USE_CALLBACKS == 0 ) */ 184 185 #if ( ipconfigDNS_USE_CALLBACKS == 1 ) 186 187 /** 188 * @brief Check if hostname is already known. If not, call prvGetHostByName() to send a DNS request. 189 * 190 * @param[in] pcHostName: The hostname whose IP address is being queried. 191 * @param[in] pCallback: The callback function which will be called upon DNS response. 192 * @param[in] pvSearchID: Search ID for the callback function. 193 * @param[in] uxTimeout: Timeout for the callback function. 194 * @return The IP-address corresponding to the hostname. 195 */ prvPrepareLookup(const char * pcHostName,FOnDNSEvent pCallback,void * pvSearchID,TickType_t uxTimeout)196 static uint32_t prvPrepareLookup( const char * pcHostName, 197 FOnDNSEvent pCallback, 198 void * pvSearchID, 199 TickType_t uxTimeout ) 200 #else 201 202 /** 203 * @brief Check if hostname is already known. If not, call prvGetHostByName() to send a DNS request. 204 * @param[in] pcHostName: The hostname whose IP address is being queried. 205 * @return The IP-address corresponding to the hostname. 206 */ 207 static uint32_t prvPrepareLookup( const char * pcHostName ) 208 #endif 209 { 210 uint32_t ulIPAddress = 0U; 211 TickType_t uxReadTimeOut_ticks = ipconfigDNS_RECEIVE_BLOCK_TIME_TICKS; 212 213 /* Generate a unique identifier for this query. Keep it in a local variable 214 * as gethostbyname() may be called from different threads */ 215 BaseType_t xHasRandom = pdFALSE; 216 TickType_t uxIdentifier = 0U; 217 218 BaseType_t xLengthOk = pdFALSE; 219 220 if( pcHostName != NULL ) 221 { 222 size_t xLength = strlen( pcHostName ) + 1U; 223 224 #if ( ipconfigUSE_DNS_CACHE != 0 ) 225 if( xLength <= ipconfigDNS_CACHE_NAME_LENGTH ) 226 #else 227 if( xLength <= dnsMAX_HOSTNAME_LENGTH ) 228 #endif 229 { 230 /* The name is not too long. */ 231 xLengthOk = pdTRUE; 232 } 233 else 234 { 235 FreeRTOS_printf( ( "prvPrepareLookup: name is too long ( %lu > %lu )\n", 236 ( uint32_t ) xLength, 237 ( uint32_t ) ipconfigDNS_CACHE_NAME_LENGTH ) ); 238 } 239 } 240 241 if( ( pcHostName != NULL ) && ( xLengthOk != pdFALSE ) ) 242 { 243 /* If the supplied hostname is IP address, convert it to uint32_t 244 * and return. */ 245 #if ( ipconfigINCLUDE_FULL_INET_ADDR == 1 ) 246 { 247 ulIPAddress = FreeRTOS_inet_addr( pcHostName ); 248 } 249 #endif /* ipconfigINCLUDE_FULL_INET_ADDR == 1 */ 250 251 #if ( ipconfigUSE_DNS_CACHE == 1 ) 252 /* Check the cache before issuing another DNS request. */ 253 if( ulIPAddress == 0U ) 254 { 255 ulIPAddress = FreeRTOS_dnslookup( pcHostName ); 256 257 if( ulIPAddress != 0U ) 258 { 259 FreeRTOS_debug_printf( ( "FreeRTOS_gethostbyname: found '%s' in cache: %lxip\n", pcHostName, ulIPAddress ) ); 260 } 261 else 262 { 263 /* prvGetHostByName will be called to start a DNS lookup. */ 264 } 265 } 266 #endif /* if ( ipconfigUSE_DNS_CACHE == 1 ) */ 267 268 /* Generate a unique identifier. */ 269 if( ulIPAddress == 0U ) 270 { 271 uint32_t ulNumber; 272 273 xHasRandom = xApplicationGetRandomNumber( &( ulNumber ) ); 274 /* DNS identifiers are 16-bit. */ 275 uxIdentifier = ( TickType_t ) ( ulNumber & 0xffffU ); 276 } 277 278 #if ( ipconfigDNS_USE_CALLBACKS == 1 ) 279 { 280 if( pCallback != NULL ) 281 { 282 if( ulIPAddress == 0U ) 283 { 284 /* The user has provided a callback function, so do not block on recvfrom() */ 285 if( xHasRandom != pdFALSE ) 286 { 287 uxReadTimeOut_ticks = 0U; 288 vDNSSetCallBack( pcHostName, 289 pvSearchID, 290 pCallback, 291 uxTimeout, 292 uxIdentifier ); 293 } 294 } 295 else 296 { 297 /* The IP address is known, do the call-back now. */ 298 pCallback( pcHostName, pvSearchID, ulIPAddress ); 299 } 300 } 301 } 302 #endif /* if ( ipconfigDNS_USE_CALLBACKS == 1 ) */ 303 304 if( ( ulIPAddress == 0U ) && ( xHasRandom != pdFALSE ) ) 305 { 306 ulIPAddress = prvGetHostByName( pcHostName, 307 uxIdentifier, 308 uxReadTimeOut_ticks ); 309 } 310 } 311 312 return ulIPAddress; 313 } 314 /*-----------------------------------------------------------*/ 315 316 #if ( ipconfigUSE_LLMNR == 1 ) 317 318 /*! 319 * @brief If LLMNR is being used then determine if the host name includes a '.' 320 * if not then LLMNR can be used as the lookup method. 321 * @param [in] pcHostName hostname to check 322 * @returns true if the hostname is a dotted format, else false 323 * 324 */ llmnr_has_dot(const char * pcHostName)325 static BaseType_t llmnr_has_dot( const char * pcHostName ) 326 { 327 BaseType_t bHasDot = pdFALSE; 328 const char * pucPtr; 329 330 for( pucPtr = pcHostName; *pucPtr != ( char ) 0; pucPtr++ ) 331 { 332 if( *pucPtr == '.' ) 333 { 334 bHasDot = pdTRUE; 335 break; 336 } 337 } 338 339 return bHasDot; 340 } 341 #endif /* if ( ipconfigUSE_LLMNR == 1 ) */ 342 343 /*! 344 * @brief create a payload buffer and return it through the parameter 345 * @param [out] ppxNetworkBuffer network buffer to create 346 * @param [in] pcHostName hostname to get its length 347 * @returns pointer address to the payload buffer 348 * 349 */ prvGetPayloadBuffer(NetworkBufferDescriptor_t ** ppxNetworkBuffer,const char * pcHostName)350 static uint8_t * prvGetPayloadBuffer( NetworkBufferDescriptor_t ** ppxNetworkBuffer, 351 const char * pcHostName ) 352 { 353 size_t uxExpectedPayloadLength; 354 uint8_t * pucUDPPayloadBuffer = NULL; 355 size_t uxHeaderBytes; 356 357 uxHeaderBytes = ipSIZE_OF_ETH_HEADER + 358 ipSIZE_OF_IPv4_HEADER + 359 ipSIZE_OF_UDP_HEADER; 360 361 uxExpectedPayloadLength = sizeof( DNSMessage_t ) + 362 strlen( pcHostName ) + 363 sizeof( uint16_t ) + 364 sizeof( uint16_t ) + 2U; 365 366 /* Get a buffer. This uses a maximum delay, but the delay will be 367 * capped to ipconfigUDP_MAX_SEND_BLOCK_TIME_TICKS so the return value 368 * still needs to be tested. */ 369 *ppxNetworkBuffer = pxGetNetworkBufferWithDescriptor( uxExpectedPayloadLength + 370 uxHeaderBytes, 371 0U ); 372 373 if( *ppxNetworkBuffer != NULL ) 374 { 375 pucUDPPayloadBuffer = &( ( *ppxNetworkBuffer )->pucEthernetBuffer[ uxHeaderBytes ] ); 376 } 377 378 return pucUDPPayloadBuffer; 379 } 380 381 /*! 382 * @brief fill pxAddress from pucUDPPayloadBuffer 383 * @param [out] pxAddress ip address and port ... structure 384 * @param [in] pcHostName hostname to get its length 385 */ prvFillSockAddress(struct freertos_sockaddr * pxAddress,const char * pcHostName)386 static void prvFillSockAddress( struct freertos_sockaddr * pxAddress, 387 const char * pcHostName ) 388 { 389 uint32_t ulIPAddress = 0U; 390 391 #if ( ipconfigUSE_LLMNR != 1 ) 392 ( void ) pcHostName; 393 #endif 394 395 /* Obtain the DNS server address. */ 396 FreeRTOS_GetAddressConfiguration( NULL, NULL, NULL, &ulIPAddress ); 397 #if ( ipconfigUSE_LLMNR == 1 ) 398 BaseType_t bHasDot = llmnr_has_dot( pcHostName ); 399 400 if( bHasDot == pdFALSE ) 401 { 402 /* Use LLMNR addressing. */ 403 pxAddress->sin_addr = ipLLMNR_IP_ADDR; /* Is in network byte order. */ 404 pxAddress->sin_port = ipLLMNR_PORT; 405 pxAddress->sin_port = FreeRTOS_ntohs( pxAddress->sin_port ); 406 } 407 else 408 #endif /* if ( ipconfigUSE_LLMNR == 1 ) */ 409 { 410 /* Use DNS server. */ 411 pxAddress->sin_addr = ulIPAddress; 412 pxAddress->sin_port = dnsDNS_PORT; 413 } 414 } 415 416 /*! 417 * @brief return ip address from the dns reply message 418 * @param [in] pxReceiveBuffer received buffer from the DNS server 419 * @param [in] uxIdentifier matches sent and received packets 420 * @returns ip address or zero on error 421 * 422 */ prvDNSReply(const struct xDNSBuffer * pxReceiveBuffer,TickType_t uxIdentifier)423 static uint32_t prvDNSReply( const struct xDNSBuffer * pxReceiveBuffer, 424 TickType_t uxIdentifier ) 425 { 426 uint32_t ulIPAddress = 0U; 427 BaseType_t xExpected; 428 429 /* MISRA Ref 11.3.1 [Misaligned access] */ 430 /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ 431 /* coverity[misra_c_2012_rule_11_3_violation] */ 432 const DNSMessage_t * pxDNSMessageHeader = 433 ( ( const DNSMessage_t * ) 434 pxReceiveBuffer->pucPayloadBuffer ); 435 436 /* See if the identifiers match. */ 437 if( uxIdentifier == ( TickType_t ) pxDNSMessageHeader->usIdentifier ) 438 { 439 xExpected = pdTRUE; 440 } 441 else 442 { 443 xExpected = pdFALSE; 444 } 445 446 /* The reply was received. Process it. */ 447 #if ( ipconfigDNS_USE_CALLBACKS == 0 ) 448 449 /* It is useless to analyse the unexpected reply 450 * unless asynchronous look-ups are enabled. */ 451 if( xExpected != pdFALSE ) 452 #endif /* ipconfigDNS_USE_CALLBACKS == 0 */ 453 { 454 ulIPAddress = DNS_ParseDNSReply( pxReceiveBuffer->pucPayloadBuffer, 455 pxReceiveBuffer->uxPayloadLength, 456 xExpected ); 457 } 458 459 return ulIPAddress; 460 } 461 462 /*! 463 * @brief prepare the buffer before sending 464 * @param [in] pcHostName 465 * @param [in] uxIdentifier matches sent and received packets 466 * @param [in] xDNSSocket a valid socket 467 * @param [in] pxAddress address structure 468 * @returns pdTRUE if sending the data was successful, pdFALSE otherwise. 469 */ prvSendBuffer(const char * pcHostName,TickType_t uxIdentifier,Socket_t xDNSSocket,const struct freertos_sockaddr * pxAddress)470 static BaseType_t prvSendBuffer( const char * pcHostName, 471 TickType_t uxIdentifier, 472 Socket_t xDNSSocket, 473 const struct freertos_sockaddr * pxAddress ) 474 { 475 BaseType_t uxReturn = pdFAIL; 476 struct xDNSBuffer xDNSBuf = { 0 }; 477 NetworkBufferDescriptor_t * pxNetworkBuffer = NULL; 478 479 /*get dns message buffer */ 480 xDNSBuf.pucPayloadBuffer = prvGetPayloadBuffer( &pxNetworkBuffer, 481 pcHostName ); 482 483 if( xDNSBuf.pucPayloadBuffer != NULL ) 484 { 485 xDNSBuf.uxPayloadSize = pxNetworkBuffer->xDataLength; 486 487 #if ( ipconfigUSE_LLMNR == 1 ) 488 { 489 if( FreeRTOS_ntohs( pxAddress->sin_port ) == ipLLMNR_PORT ) 490 { 491 ( ( ( DNSMessage_t * ) xDNSBuf.pucPayloadBuffer ) )->usFlags = 0; 492 } 493 } 494 #endif 495 496 xDNSBuf.uxPayloadLength = prvCreateDNSMessage( xDNSBuf.pucPayloadBuffer, 497 pcHostName, 498 uxIdentifier ); 499 500 /* send the dns message */ 501 uxReturn = DNS_SendRequest( xDNSSocket, 502 pxAddress, 503 &xDNSBuf ); 504 505 if( uxReturn == pdFAIL ) 506 { 507 vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ); 508 } 509 } 510 511 return uxReturn; 512 } 513 514 /*! 515 * @brief main dns operation description function 516 * @param [in] pcHostName hostname to get its ip address 517 * @param [in] uxIdentifier Identifier to match sent and received packets 518 * @param [in] xDNSSocket socket 519 * @returns ip address or zero on error 520 */ prvGetHostByNameOp(const char * pcHostName,TickType_t uxIdentifier,Socket_t xDNSSocket)521 static uint32_t prvGetHostByNameOp( const char * pcHostName, 522 TickType_t uxIdentifier, 523 Socket_t xDNSSocket ) 524 { 525 uint32_t ulIPAddress = 0; 526 527 struct freertos_sockaddr xAddress; 528 DNSBuffer_t xReceiveBuffer = { 0 }; 529 BaseType_t uxReturn = pdFAIL; 530 531 prvFillSockAddress( &xAddress, pcHostName ); 532 533 do 534 { 535 uxReturn = prvSendBuffer( pcHostName, 536 uxIdentifier, 537 xDNSSocket, 538 &xAddress ); 539 540 if( uxReturn == pdFAIL ) 541 { 542 break; 543 } 544 545 /* Create the message in the obtained buffer. */ 546 547 /* receive a dns reply message */ 548 DNS_ReadReply( xDNSSocket, 549 &xAddress, 550 &xReceiveBuffer ); 551 552 if( xReceiveBuffer.pucPayloadBuffer == NULL ) 553 { 554 break; 555 } 556 557 ulIPAddress = prvDNSReply( &xReceiveBuffer, uxIdentifier ); 558 559 /* Finished with the buffer. The zero copy interface 560 * is being used, so the buffer must be freed by the 561 * task. */ 562 FreeRTOS_ReleaseUDPPayloadBuffer( xReceiveBuffer.pucPayloadBuffer ); 563 } while( ipFALSE_BOOL ); 564 565 return ulIPAddress; 566 } 567 568 /*! 569 * @brief helper function to prvGetHostByNameOP with multiple retries equal to 570 * ipconfigDNS_REQUEST_ATTEMPTS 571 * 572 * @param [in] pcHostName hostname to get its ip address 573 * @param [in] uxIdentifier Identifier to match sent and received packets 574 * @param [in] xDNSSocket socket 575 * @returns ip address or zero on error 576 * 577 */ prvGetHostByNameOp_WithRetry(const char * pcHostName,TickType_t uxIdentifier,Socket_t xDNSSocket)578 static uint32_t prvGetHostByNameOp_WithRetry( const char * pcHostName, 579 TickType_t uxIdentifier, 580 Socket_t xDNSSocket ) 581 { 582 uint32_t ulIPAddress; 583 BaseType_t xAttempt; 584 585 for( xAttempt = 0; xAttempt < ipconfigDNS_REQUEST_ATTEMPTS; xAttempt++ ) 586 { 587 ulIPAddress = prvGetHostByNameOp( pcHostName, 588 uxIdentifier, 589 xDNSSocket ); 590 591 if( ulIPAddress != 0U ) 592 { /* ip found, no need to retry */ 593 break; 594 } 595 } 596 597 return ulIPAddress; 598 } 599 600 601 /** 602 * @brief Prepare and send a message to a DNS server. 'uxReadTimeOut_ticks' will be passed as 603 * zero, in case the user has supplied a call-back function. 604 * 605 * @param[in] pcHostName The hostname for which an IP address is required. 606 * @param[in] uxIdentifier Identifier to match sent and received packets 607 * @param[in] uxReadTimeOut_ticks The timeout in ticks for waiting. In case the user has supplied 608 * a call-back function, this value should be zero. 609 * @return The IPv4 IP address for the hostname being queried. It will be zero if there is no reply. 610 */ prvGetHostByName(const char * pcHostName,TickType_t uxIdentifier,TickType_t uxReadTimeOut_ticks)611 static uint32_t prvGetHostByName( const char * pcHostName, 612 TickType_t uxIdentifier, 613 TickType_t uxReadTimeOut_ticks ) 614 { 615 Socket_t xDNSSocket; 616 uint32_t ulIPAddress = 0U; 617 618 xDNSSocket = DNS_CreateSocket( uxReadTimeOut_ticks ); 619 620 if( xDNSSocket != NULL ) 621 { 622 if( uxReadTimeOut_ticks == 0U ) 623 { 624 ulIPAddress = prvGetHostByNameOp( pcHostName, 625 uxIdentifier, 626 xDNSSocket ); 627 } 628 else 629 { 630 ulIPAddress = prvGetHostByNameOp_WithRetry( pcHostName, 631 uxIdentifier, 632 xDNSSocket ); 633 } 634 635 /* Finished with the socket. */ 636 DNS_CloseSocket( xDNSSocket ); 637 } 638 639 return ulIPAddress; 640 } 641 /*-----------------------------------------------------------*/ 642 643 /** 644 * @brief Create the DNS message in the zero copy buffer passed in the first parameter. 645 * @param[in,out] pucUDPPayloadBuffer The zero copy buffer where the DNS message will be created. 646 * @param[in] pcHostName Hostname to be looked up. 647 * @param[in] uxIdentifier Identifier to match sent and received packets 648 * @return Total size of the generated message, which is the space from the last written byte 649 * to the beginning of the buffer. 650 */ prvCreateDNSMessage(uint8_t * pucUDPPayloadBuffer,const char * pcHostName,TickType_t uxIdentifier)651 _static size_t prvCreateDNSMessage( uint8_t * pucUDPPayloadBuffer, 652 const char * pcHostName, 653 TickType_t uxIdentifier ) 654 { 655 DNSMessage_t * pxDNSMessageHeader; 656 size_t uxStart, uxIndex; 657 DNSTail_t const * pxTail; 658 static const DNSMessage_t xDefaultPartDNSHeader = 659 { 660 0, /* The identifier will be overwritten. */ 661 dnsOUTGOING_FLAGS, /* Flags set for standard query. */ 662 dnsONE_QUESTION, /* One question is being asked. */ 663 0, /* No replies are included. */ 664 0, /* No authorities. */ 665 0 /* No additional authorities. */ 666 }; 667 668 /* memcpy() helper variables for MISRA Rule 21.15 compliance*/ 669 const void * pvCopySource; 670 void * pvCopyDest; 671 672 /* Copy in the const part of the header. Intentionally using different 673 * pointers with memcpy() to put the information in to correct place. */ 674 675 /* 676 * Use helper variables for memcpy() to remain 677 * compliant with MISRA Rule 21.15. These should be 678 * optimized away. 679 */ 680 pvCopySource = &xDefaultPartDNSHeader; 681 pvCopyDest = pucUDPPayloadBuffer; 682 ( void ) memcpy( pvCopyDest, pvCopySource, sizeof( xDefaultPartDNSHeader ) ); 683 684 /* Write in a unique identifier. Cast the Payload Buffer to DNSMessage_t 685 * to easily access fields of the DNS Message. */ 686 687 /* MISRA Ref 11.3.1 [Misaligned access] */ 688 /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ 689 /* coverity[misra_c_2012_rule_11_3_violation] */ 690 pxDNSMessageHeader = ( ( DNSMessage_t * ) pucUDPPayloadBuffer ); 691 pxDNSMessageHeader->usIdentifier = ( uint16_t ) uxIdentifier; 692 693 /* Create the resource record at the end of the header. First 694 * find the end of the header. */ 695 uxStart = sizeof( xDefaultPartDNSHeader ); 696 697 /* Leave a gap for the first length byte. */ 698 uxIndex = uxStart + 1U; 699 700 /* Copy in the host name. */ 701 ( void ) strcpy( ( char * ) &( pucUDPPayloadBuffer[ uxIndex ] ), pcHostName ); 702 703 /* Walk through the string to replace the '.' characters with byte 704 * counts. pucStart holds the address of the byte count. Walking the 705 * string starts after the byte count position. */ 706 uxIndex = uxStart; 707 708 do 709 { 710 size_t uxLength; 711 712 /* Skip the length byte. */ 713 uxIndex++; 714 715 while( ( pucUDPPayloadBuffer[ uxIndex ] != ( uint8_t ) 0U ) && 716 ( pucUDPPayloadBuffer[ uxIndex ] != ( uint8_t ) ASCII_BASELINE_DOT ) ) 717 { 718 uxIndex++; 719 } 720 721 /* Fill in the byte count, then move the pucStart pointer up to 722 * the found byte position. */ 723 uxLength = uxIndex - ( uxStart + 1U ); 724 pucUDPPayloadBuffer[ uxStart ] = ( uint8_t ) uxLength; 725 726 uxStart = uxIndex; 727 } while( pucUDPPayloadBuffer[ uxIndex ] != ( uint8_t ) 0U ); 728 729 /* Finish off the record. Cast the record onto DNSTail_t structure to easily 730 * access the fields of the DNS Message. */ 731 732 /* MISRA Ref 11.3.1 [Misaligned access] */ 733 /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ 734 /* coverity[misra_c_2012_rule_11_3_violation] */ 735 pxTail = ( ( DNSTail_t * ) &( pucUDPPayloadBuffer[ uxStart + 1U ] ) ); 736 737 #if defined( _lint ) || defined( __COVERITY__ ) 738 ( void ) pxTail; 739 #else 740 vSetField16( pxTail, DNSTail_t, usType, dnsTYPE_A_HOST ); 741 vSetField16( pxTail, DNSTail_t, usClass, dnsCLASS_IN ); 742 #endif 743 744 /* Return the total size of the generated message, which is the space from 745 * the last written byte to the beginning of the buffer. */ 746 return uxIndex + sizeof( DNSTail_t ) + 1U; 747 } 748 749 /*-----------------------------------------------------------*/ 750 751 /* The function below will only be called : 752 * when ipconfigDNS_USE_CALLBACKS == 1 753 * when ipconfigUSE_LLMNR == 1 754 * for testing purposes, by the module test_freertos_tcp.c 755 */ 756 757 /** 758 * @brief Perform some preliminary checks and then parse the DNS packet. 759 * 760 * @param[in] pxNetworkBuffer: The network buffer to be parsed. 761 * 762 * @return pdFAIL Always to indicate that the packet was not consumed and must 763 * be released by the caller. 764 */ ulDNSHandlePacket(const NetworkBufferDescriptor_t * pxNetworkBuffer)765 uint32_t ulDNSHandlePacket( const NetworkBufferDescriptor_t * pxNetworkBuffer ) 766 { 767 uint8_t * pucPayLoadBuffer; 768 size_t uxPayloadSize; 769 770 /* Only proceed if the payload length indicated in the header 771 * appears to be valid. */ 772 if( pxNetworkBuffer->xDataLength >= sizeof( UDPPacket_t ) ) 773 { 774 uxPayloadSize = pxNetworkBuffer->xDataLength - sizeof( UDPPacket_t ); 775 776 if( uxPayloadSize >= sizeof( DNSMessage_t ) ) 777 { 778 pucPayLoadBuffer = &( pxNetworkBuffer->pucEthernetBuffer[ sizeof( UDPPacket_t ) ] ); 779 780 /* The parameter pdFALSE indicates that the reply was not expected. */ 781 ( void ) DNS_ParseDNSReply( pucPayLoadBuffer, 782 uxPayloadSize, 783 pdFALSE ); 784 } 785 } 786 787 /* The packet was not consumed. */ 788 return pdFAIL; 789 } 790 /*-----------------------------------------------------------*/ 791 792 793 #if ( ipconfigUSE_NBNS == 1 ) 794 795 /** 796 * @brief Handle an NBNS packet. 797 * 798 * @param[in] pxNetworkBuffer: The network buffer holding the NBNS packet. 799 * 800 * @return pdFAIL to show that the packet was not consumed. 801 */ ulNBNSHandlePacket(NetworkBufferDescriptor_t * pxNetworkBuffer)802 uint32_t ulNBNSHandlePacket( NetworkBufferDescriptor_t * pxNetworkBuffer ) 803 { 804 UDPPacket_t * pxUDPPacket = ( ( UDPPacket_t * ) 805 pxNetworkBuffer->pucEthernetBuffer ); 806 uint8_t * pucUDPPayloadBuffer = &( pxNetworkBuffer->pucEthernetBuffer[ sizeof( *pxUDPPacket ) ] ); 807 808 size_t uxBytesNeeded = sizeof( UDPPacket_t ) + sizeof( NBNSRequest_t ); 809 810 /* Check for minimum buffer size. */ 811 if( pxNetworkBuffer->xDataLength >= uxBytesNeeded ) 812 { 813 DNS_TreatNBNS( pucUDPPayloadBuffer, 814 pxNetworkBuffer->xDataLength, 815 pxUDPPacket->xIPHeader.ulSourceIPAddress ); 816 } 817 818 /* The packet was not consumed. */ 819 return pdFAIL; 820 } 821 822 #endif /* ipconfigUSE_NBNS */ 823 824 /*-----------------------------------------------------------*/ 825 826 #endif /* ipconfigUSE_DNS != 0 */ 827 828 /*-----------------------------------------------------------*/ 829 830 /* Provide access to private members for testing. */ 831 #ifdef FREERTOS_ENABLE_UNIT_TESTS 832 #include "freertos_tcp_test_access_dns_define.h" 833 #endif 834