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