xref: /FreeRTOS-Plus-TCP-v3.1.0/source/FreeRTOS_DNS.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.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