]> git.sur5r.net Git - freertos/blob - FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/FreeRTOS_DNS.c
commit 9f316c246baafa15c542a5aea81a94f26e3d6507
[freertos] / FreeRTOS-Plus / Source / FreeRTOS-Plus-TCP / FreeRTOS_DNS.c
1 /*
2  * FreeRTOS+TCP V2.2.1
3  * Copyright (C) 2017 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy of
6  * this software and associated documentation files (the "Software"), to deal in
7  * the Software without restriction, including without limitation the rights to
8  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9  * the Software, and to permit persons to whom the Software is furnished to do so,
10  * subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in all
13  * copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21  *
22  * http://aws.amazon.com/freertos
23  * http://www.FreeRTOS.org
24  */
25
26 /* Standard includes. */
27 #include <stdint.h>
28
29 /* FreeRTOS includes. */
30 #include "FreeRTOS.h"
31 #include "task.h"
32 #include "queue.h"
33 #include "list.h"
34 #include "semphr.h"
35
36 /* FreeRTOS+TCP includes. */
37 #include "FreeRTOS_IP.h"
38 #include "FreeRTOS_Sockets.h"
39 #include "FreeRTOS_IP_Private.h"
40 #include "FreeRTOS_UDP_IP.h"
41 #include "FreeRTOS_DNS.h"
42 #include "NetworkBufferManagement.h"
43 #include "NetworkInterface.h"
44 #include "IPTraceMacroDefaults.h"
45
46 /* Exclude the entire file if DNS is not enabled. */
47 #if( ipconfigUSE_DNS != 0 )
48
49 #if( ipconfigBYTE_ORDER == pdFREERTOS_LITTLE_ENDIAN )
50         #define dnsDNS_PORT                             0x3500u
51         #define dnsONE_QUESTION                 0x0100u
52         #define dnsOUTGOING_FLAGS               0x0001u     /* Standard query. */
53         #define dnsRX_FLAGS_MASK                0x0f80u     /* The bits of interest in the flags field of incoming DNS messages. */
54         #define dnsEXPECTED_RX_FLAGS    0x0080u     /* Should be a response, without any errors. */
55 #else
56         #define dnsDNS_PORT                             0x0035u
57         #define dnsONE_QUESTION                 0x0001u
58         #define dnsOUTGOING_FLAGS               0x0100u     /* Standard query. */
59         #define dnsRX_FLAGS_MASK                0x800fu     /* The bits of interest in the flags field of incoming DNS messages. */
60         #define dnsEXPECTED_RX_FLAGS    0x8000u     /* Should be a response, without any errors. */
61
62 #endif /* ipconfigBYTE_ORDER */
63
64 /* The maximum number of times a DNS request should be sent out if a response
65 is not received, before giving up. */
66 #ifndef ipconfigDNS_REQUEST_ATTEMPTS
67         #define ipconfigDNS_REQUEST_ATTEMPTS    5
68 #endif
69
70 /* If the top two bits in the first character of a name field are set then the
71 name field is an offset to the string, rather than the string itself. */
72 #define dnsNAME_IS_OFFSET                                        ( ( uint8_t ) 0xc0 )
73
74 /* NBNS flags. */
75 #define dnsNBNS_FLAGS_RESPONSE                           0x8000u
76 #define dnsNBNS_FLAGS_OPCODE_MASK                        0x7800u
77 #define dnsNBNS_FLAGS_OPCODE_QUERY                       0x0000u
78 #define dnsNBNS_FLAGS_OPCODE_REGISTRATION        0x2800u
79
80 /* Host types. */
81 #define dnsTYPE_A_HOST                                           0x01u
82 #define dnsCLASS_IN                                                      0x01u
83
84 /* LLMNR constants. */
85 #define dnsLLMNR_TTL_VALUE                                       300000uL
86 #define dnsLLMNR_FLAGS_IS_REPONSE                        0x8000u
87
88 /* NBNS constants. */
89 #define dnsNBNS_TTL_VALUE                                        3600uL /* 1 hour valid */
90 #define dnsNBNS_TYPE_NET_BIOS                            0x0020u
91 #define dnsNBNS_CLASS_IN                                         0x01u
92 #define dnsNBNS_NAME_FLAGS                                       0x6000u
93 #define dnsNBNS_ENCODED_NAME_LENGTH                      32
94
95 /* If the queried NBNS name matches with the device's name,
96 the query will be responded to with these flags: */
97 #define dnsNBNS_QUERY_RESPONSE_FLAGS             ( 0x8500u )
98
99 /* Flag DNS parsing errors in situations where an IPv4 address is the return
100 type. */
101 #define dnsPARSE_ERROR                                           0uL
102
103 /*
104  * Create a socket and bind it to the standard DNS port number.  Return the
105  * the created socket - or NULL if the socket could not be created or bound.
106  */
107 static Socket_t prvCreateDNSSocket( void );
108
109 /*
110  * Create the DNS message in the zero copy buffer passed in the first parameter.
111  */
112 static size_t prvCreateDNSMessage( uint8_t *pucUDPPayloadBuffer,
113                                                                    const char *pcHostName,
114                                                                    TickType_t uxIdentifier );
115
116 /*
117  * Simple routine that jumps over the NAME field of a resource record.
118  */
119 static uint8_t * prvSkipNameField( uint8_t *pucByte,
120                                                                    size_t uxSourceLen );
121
122 /*
123  * Process a response packet from a DNS server.
124  * The parameter 'xExpected' indicates whether the identifier in the reply
125  * was expected, and thus if the DNS cache may be updated with the reply.
126  */
127 static uint32_t prvParseDNSReply( uint8_t *pucUDPPayloadBuffer,
128                                                                   size_t uxBufferLength,
129                                                                   BaseType_t xExpected );
130
131 /*
132  * Prepare and send a message to a DNS server.  'uxReadTimeOut_ticks' will be passed as
133  * zero, in case the user has supplied a call-back function.
134  */
135 static uint32_t prvGetHostByName( const char *pcHostName,
136                                                                   TickType_t uxIdentifier,
137                                                                   TickType_t uxReadTimeOut_ticks );
138
139 /*
140  * The NBNS and the LLMNR protocol share this reply function.
141  */
142 #if( ( ipconfigUSE_NBNS == 1 ) || ( ipconfigUSE_LLMNR == 1 ) )
143         static void prvReplyDNSMessage( NetworkBufferDescriptor_t *pxNetworkBuffer,
144                                                                         BaseType_t lNetLength );
145 #endif
146
147 #if( ipconfigUSE_NBNS == 1 )
148         static portINLINE void prvTreatNBNS( uint8_t *pucUDPPayloadBuffer,
149                                                                                  size_t uxBufferLength,
150                                                                                  uint32_t ulIPAddress );
151 #endif /* ipconfigUSE_NBNS */
152
153
154 #if( ipconfigUSE_DNS_CACHE == 1 ) || ( ipconfigDNS_USE_CALLBACKS == 1 )
155         static uint8_t * prvReadNameField( uint8_t *pucByte,
156                                                                            size_t uxSourceLen,
157                                                                            char *pcName,
158                                                                            size_t uxLen );
159 #endif /* ipconfigUSE_DNS_CACHE || ipconfigDNS_USE_CALLBACKS */
160
161 #if( ipconfigUSE_DNS_CACHE == 1 )
162         static void prvProcessDNSCache( const char *pcName,
163                                                                         uint32_t *pulIP,
164                                                                         uint32_t ulTTL,
165                                                                         BaseType_t xLookUp );
166
167         typedef struct xDNS_CACHE_TABLE_ROW
168         {
169                 uint32_t ulIPAddress;                         /* The IP address of an ARP cache entry. */
170                 char pcName[ ipconfigDNS_CACHE_NAME_LENGTH ]; /* The name of the host */
171                 uint32_t ulTTL;                               /* Time-to-Live (in seconds) from the DNS server. */
172                 uint32_t ulTimeWhenAddedInSeconds;
173         } DNSCacheRow_t;
174
175         static DNSCacheRow_t xDNSCache[ ipconfigDNS_CACHE_ENTRIES ];
176
177         void FreeRTOS_dnsclear()
178         {
179                 memset( xDNSCache, 0x0, sizeof( xDNSCache ) );
180         }
181 #endif /* ipconfigUSE_DNS_CACHE == 1 */
182
183 #if( ipconfigUSE_LLMNR == 1 )
184         const MACAddress_t xLLMNR_MacAdress = { { 0x01, 0x00, 0x5e, 0x00, 0x00, 0xfc } };
185 #endif /* ipconfigUSE_LLMNR == 1 */
186
187 /*-----------------------------------------------------------*/
188
189 #include "pack_struct_start.h"
190 struct xDNSMessage
191 {
192         uint16_t usIdentifier;
193         uint16_t usFlags;
194         uint16_t usQuestions;
195         uint16_t usAnswers;
196         uint16_t usAuthorityRRs;
197         uint16_t usAdditionalRRs;
198 }
199 #include "pack_struct_end.h"
200 typedef struct xDNSMessage DNSMessage_t;
201
202 /* A DNS query consists of a header, as described in 'struct xDNSMessage'
203 It is followed by 1 or more queries, each one consisting of a name and a tail,
204 with two fields: type and class
205 */
206 #include "pack_struct_start.h"
207 struct xDNSTail
208 {
209         uint16_t usType;
210         uint16_t usClass;
211 }
212 #include "pack_struct_end.h"
213 typedef struct xDNSTail DNSTail_t;
214
215 /* DNS answer record header. */
216 #include "pack_struct_start.h"
217 struct xDNSAnswerRecord
218 {
219         uint16_t usType;
220         uint16_t usClass;
221         uint32_t ulTTL;
222         uint16_t usDataLength;
223 }
224 #include "pack_struct_end.h"
225 typedef struct xDNSAnswerRecord DNSAnswerRecord_t;
226
227 #if( ipconfigUSE_LLMNR == 1 )
228
229         #include "pack_struct_start.h"
230         struct xLLMNRAnswer
231         {
232                 uint8_t ucNameCode;
233                 uint8_t ucNameOffset;   /* The name is not repeated in the answer, only the offset is given with "0xc0 <offs>" */
234                 uint16_t usType;
235                 uint16_t usClass;
236                 uint32_t ulTTL;
237                 uint16_t usDataLength;
238                 uint32_t ulIPAddress;
239         }
240         #include "pack_struct_end.h"
241         typedef struct xLLMNRAnswer LLMNRAnswer_t;
242
243 #endif /* ipconfigUSE_LLMNR == 1 */
244
245 #if( ipconfigUSE_NBNS == 1 )
246
247         #include "pack_struct_start.h"
248         struct xNBNSRequest
249         {
250                 uint16_t usRequestId;
251                 uint16_t usFlags;
252                 uint16_t ulRequestCount;
253                 uint16_t usAnswerRSS;
254                 uint16_t usAuthRSS;
255                 uint16_t usAdditionalRSS;
256                 uint8_t ucNameSpace;
257                 uint8_t ucName[ dnsNBNS_ENCODED_NAME_LENGTH ];
258                 uint8_t ucNameZero;
259                 uint16_t usType;
260                 uint16_t usClass;
261         }
262         #include "pack_struct_end.h"
263         typedef struct xNBNSRequest NBNSRequest_t;
264
265         #include "pack_struct_start.h"
266         struct xNBNSAnswer
267         {
268                 uint16_t usType;
269                 uint16_t usClass;
270                 uint32_t ulTTL;
271                 uint16_t usDataLength;
272                 uint16_t usNbFlags;     /* NetBIOS flags 0x6000 : IP-address, big-endian */
273                 uint32_t ulIPAddress;
274         }
275         #include "pack_struct_end.h"
276         typedef struct xNBNSAnswer NBNSAnswer_t;
277
278         #endif /* ipconfigUSE_NBNS == 1 */
279
280 /*-----------------------------------------------------------*/
281
282 #if( ipconfigUSE_DNS_CACHE == 1 )
283         uint32_t FreeRTOS_dnslookup( const char *pcHostName )
284         {
285         uint32_t ulIPAddress = 0uL;
286
287                 prvProcessDNSCache( pcHostName, &ulIPAddress, 0, pdTRUE );
288                 return ulIPAddress;
289         }
290 #endif /* ipconfigUSE_DNS_CACHE == 1 */
291 /*-----------------------------------------------------------*/
292
293 #if( ipconfigDNS_USE_CALLBACKS == 1 )
294
295         typedef struct xDNS_Callback
296         {
297                 TickType_t uxRemaningTime;              /* Timeout in ms */
298                 FOnDNSEvent pCallbackFunction;  /* Function to be called when the address has been found or when a timeout has beeen reached */
299                 TimeOut_t uxTimeoutState;
300                 void *pvSearchID;
301                 struct xLIST_ITEM xListItem;
302                 char pcName[ 1 ];
303         } DNSCallback_t;
304
305         static List_t xCallbackList;
306
307         /* Define FreeRTOS_gethostbyname() as a normal blocking call. */
308         uint32_t FreeRTOS_gethostbyname( const char *pcHostName )
309         {
310                 return FreeRTOS_gethostbyname_a( pcHostName, ( FOnDNSEvent ) NULL, ( void * ) NULL, 0 );
311         }
312         /*-----------------------------------------------------------*/
313
314         /* Initialise the list of call-back structures. */
315         void vDNSInitialise( void );
316         void vDNSInitialise( void )
317         {
318                 vListInitialise( &xCallbackList );
319         }
320         /*-----------------------------------------------------------*/
321
322         /* Iterate through the list of call-back structures and remove
323         old entries which have reached a timeout.
324         As soon as the list hase become empty, the DNS timer will be stopped
325         In case pvSearchID is supplied, the user wants to cancel a DNS request
326         */
327         void vDNSCheckCallBack( void *pvSearchID );
328         void vDNSCheckCallBack( void *pvSearchID )
329         {
330         const ListItem_t *pxIterator;
331         const MiniListItem_t * xEnd = ( const MiniListItem_t * ) listGET_END_MARKER( &xCallbackList );
332
333                 vTaskSuspendAll();
334                 {
335                         for( pxIterator  = ( const ListItem_t * ) listGET_NEXT( xEnd );
336                                  pxIterator != ( const ListItem_t * ) xEnd;
337                                  )
338                         {
339                         DNSCallback_t *pxCallback = ( DNSCallback_t * ) listGET_LIST_ITEM_OWNER( pxIterator );
340
341                                 /* Move to the next item because we might remove this item */
342                                 pxIterator = ( const ListItem_t * ) listGET_NEXT( pxIterator );
343
344                                 if( ( pvSearchID != NULL ) && ( pvSearchID == pxCallback->pvSearchID ) )
345                                 {
346                                         uxListRemove( &pxCallback->xListItem );
347                                         vPortFree( pxCallback );
348                                 }
349                                 else if( xTaskCheckForTimeOut( &pxCallback->uxTimeoutState, &pxCallback->uxRemaningTime ) != pdFALSE )
350                                 {
351                                         pxCallback->pCallbackFunction( pxCallback->pcName, pxCallback->pvSearchID, 0 );
352                                         uxListRemove( &pxCallback->xListItem );
353                                         vPortFree( ( void * ) pxCallback );
354                                 }
355                         }
356                 }
357                 xTaskResumeAll();
358
359                 if( listLIST_IS_EMPTY( &xCallbackList ) )
360                 {
361                         vIPSetDnsTimerEnableState( pdFALSE );
362                 }
363         }
364         /*-----------------------------------------------------------*/
365
366         void FreeRTOS_gethostbyname_cancel( void *pvSearchID )
367         {
368                 /* _HT_ Should better become a new API call to have the IP-task remove the callback */
369                 vDNSCheckCallBack( pvSearchID );
370         }
371         /*-----------------------------------------------------------*/
372
373         /* FreeRTOS_gethostbyname_a() was called along with callback parameters.
374         Store them in a list for later reference. */
375         static void vDNSSetCallBack( const char *pcHostName,
376                                                                  void *pvSearchID,
377                                                                  FOnDNSEvent pCallbackFunction,
378                                                                  TickType_t uxTimeout,
379                                                                  TickType_t uxIdentifier );
380         static void vDNSSetCallBack( const char *pcHostName,
381                                                                  void *pvSearchID,
382                                                                  FOnDNSEvent pCallbackFunction,
383                                                                  TickType_t uxTimeout,
384                                                                  TickType_t uxIdentifier )
385         {
386         size_t lLength = strlen( pcHostName );
387         DNSCallback_t *pxCallback = ( DNSCallback_t * ) pvPortMalloc( sizeof( *pxCallback ) + lLength );
388
389                 /* Translate from ms to number of clock ticks. */
390                 uxTimeout /= portTICK_PERIOD_MS;
391
392                 if( pxCallback != NULL )
393                 {
394                         if( listLIST_IS_EMPTY( &xCallbackList ) )
395                         {
396                                 /* This is the first one, start the DNS timer to check for timeouts */
397                                 vIPReloadDNSTimer( FreeRTOS_min_uint32( 1000U, uxTimeout ) );
398                         }
399
400                         strcpy( pxCallback->pcName, pcHostName );
401                         pxCallback->pCallbackFunction = pCallbackFunction;
402                         pxCallback->pvSearchID = pvSearchID;
403                         pxCallback->uxRemaningTime = uxTimeout;
404                         vTaskSetTimeOutState( &pxCallback->uxTimeoutState );
405                         listSET_LIST_ITEM_OWNER( &( pxCallback->xListItem ), ( void * ) pxCallback );
406                         listSET_LIST_ITEM_VALUE( &( pxCallback->xListItem ), uxIdentifier );
407                         vTaskSuspendAll();
408                         {
409                                 vListInsertEnd( &xCallbackList, &pxCallback->xListItem );
410                         }
411                         xTaskResumeAll();
412                 }
413         }
414         /*-----------------------------------------------------------*/
415
416         /* A DNS reply was received, see if there is any matching entry and
417         call the handler.  Returns pdTRUE if uxIdentifier was recognised. */
418         static BaseType_t xDNSDoCallback( TickType_t uxIdentifier,
419                                                                           const char *pcName,
420                                                                           uint32_t ulIPAddress );
421         static BaseType_t xDNSDoCallback( TickType_t uxIdentifier,
422                                                                           const char *pcName,
423                                                                           uint32_t ulIPAddress )
424         {
425         BaseType_t xResult = pdFALSE;
426         const ListItem_t *pxIterator;
427         const MiniListItem_t * xEnd = ( const MiniListItem_t * ) listGET_END_MARKER( &xCallbackList );
428
429                 vTaskSuspendAll();
430                 {
431                         for( pxIterator  = ( const ListItem_t * ) listGET_NEXT( xEnd );
432                                  pxIterator != ( const ListItem_t * ) xEnd;
433                                  pxIterator  = ( const ListItem_t * ) listGET_NEXT( pxIterator ) )
434                         {
435                                 /* The cast will take away the 'configLIST_VOLATILE' */
436                                 if( uxIdentifier == ( TickType_t ) listGET_LIST_ITEM_VALUE( pxIterator ) )
437                                 {
438                                 DNSCallback_t *pxCallback = ( DNSCallback_t * ) listGET_LIST_ITEM_OWNER( pxIterator );
439
440                                         pxCallback->pCallbackFunction( pcName, pxCallback->pvSearchID, ulIPAddress );
441                                         uxListRemove( &pxCallback->xListItem );
442                                         vPortFree( pxCallback );
443
444                                         if( listLIST_IS_EMPTY( &xCallbackList ) )
445                                         {
446                                                 /* The list of outstanding requests is empty. No need for periodic polling. */
447                                                 vIPSetDnsTimerEnableState( pdFALSE );
448                                         }
449
450                                         xResult = pdTRUE;
451                                         break;
452                                 }
453                         }
454                 }
455                 xTaskResumeAll();
456                 return xResult;
457         }
458
459 #endif /* ipconfigDNS_USE_CALLBACKS == 1 */
460 /*-----------------------------------------------------------*/
461
462 #if( ipconfigDNS_USE_CALLBACKS == 0 )
463         uint32_t FreeRTOS_gethostbyname( const char *pcHostName )
464 #else
465         uint32_t FreeRTOS_gethostbyname_a( const char *pcHostName,
466                                                                            FOnDNSEvent pCallback,
467                                                                            void *pvSearchID,
468                                                                            TickType_t uxTimeout )
469 #endif
470 {
471 uint32_t ulIPAddress = 0uL;
472 TickType_t uxReadTimeOut_ticks = ipconfigDNS_RECEIVE_BLOCK_TIME_TICKS;
473 TickType_t uxIdentifier = 0u;
474 BaseType_t xHasRandom = pdFALSE;
475
476         if( pcHostName != NULL )
477         {
478                 /* If the supplied hostname is IP address, convert it to uint32_t
479                 and return. */
480                 #if( ipconfigINCLUDE_FULL_INET_ADDR == 1 )
481                 {
482                         ulIPAddress = FreeRTOS_inet_addr( pcHostName );
483                 }
484                 #endif /* ipconfigINCLUDE_FULL_INET_ADDR == 1 */
485
486                 /* If a DNS cache is used then check the cache before issuing another DNS
487                 request. */
488                 #if( ipconfigUSE_DNS_CACHE == 1 )
489                 {
490                         if( ulIPAddress == 0uL )
491                         {
492                                 ulIPAddress = FreeRTOS_dnslookup( pcHostName );
493
494                                 if( ulIPAddress != 0 )
495                                 {
496                                         FreeRTOS_debug_printf( ( "FreeRTOS_gethostbyname: found '%s' in cache: %lxip\n", pcHostName, ulIPAddress ) );
497                                 }
498                                 else
499                                 {
500                                         /* prvGetHostByName will be called to start a DNS lookup. */
501                                 }
502                         }
503                 }
504                 #endif /* ipconfigUSE_DNS_CACHE == 1 */
505
506                 /* Generate a unique identifier. */
507                 if( ulIPAddress == 0uL )
508                 {
509                 uint32_t ulNumber;
510
511                         xHasRandom = xApplicationGetRandomNumber( &( ulNumber ) );
512                         /* DNS identifiers are 16-bit. */
513                         uxIdentifier = ( TickType_t ) ( ulNumber & 0xffffu );
514                         /* ipconfigRAND32() may not return a non-zero value. */
515                 }
516
517                 #if( ipconfigDNS_USE_CALLBACKS == 1 )
518                 {
519                         if( pCallback != NULL )
520                         {
521                                 if( ulIPAddress == 0uL )
522                                 {
523                                         /* The user has provided a callback function, so do not block on recvfrom() */
524                                         if( xHasRandom != pdFALSE )
525                                         {
526                                                 uxReadTimeOut_ticks = 0u;
527                                                 vDNSSetCallBack( pcHostName, pvSearchID, pCallback, uxTimeout, uxIdentifier );
528                                         }
529                                 }
530                                 else
531                                 {
532                                         /* The IP address is known, do the call-back now. */
533                                         pCallback( pcHostName, pvSearchID, ulIPAddress );
534                                 }
535                         }
536                 }
537                 #endif /* if ( ipconfigDNS_USE_CALLBACKS == 1 ) */
538
539                 if( ( ulIPAddress == 0uL ) && ( xHasRandom != pdFALSE ) )
540                 {
541                         ulIPAddress = prvGetHostByName( pcHostName, uxIdentifier, uxReadTimeOut_ticks );
542                 }
543         }
544         return ulIPAddress;
545 }
546 /*-----------------------------------------------------------*/
547
548 static uint32_t prvGetHostByName( const char *pcHostName,
549                                                                   TickType_t uxIdentifier,
550                                                                   TickType_t uxReadTimeOut_ticks )
551 {
552 struct freertos_sockaddr xAddress;
553 Socket_t xDNSSocket;
554 uint32_t ulIPAddress = 0uL;
555 uint8_t *pucUDPPayloadBuffer;
556 uint32_t ulAddressLength = sizeof( struct freertos_sockaddr );
557 BaseType_t xAttempt;
558 int32_t lBytes;
559 size_t uxPayloadLength, uxExpectedPayloadLength;
560 TickType_t uxWriteTimeOut_ticks = ipconfigDNS_SEND_BLOCK_TIME_TICKS;
561
562 #if( ipconfigUSE_LLMNR == 1 )
563         BaseType_t bHasDot = pdFALSE;
564 #endif /* ipconfigUSE_LLMNR == 1 */
565
566         /* If LLMNR is being used then determine if the host name includes a '.' -
567         if not then LLMNR can be used as the lookup method. */
568         #if( ipconfigUSE_LLMNR == 1 )
569         {
570         const char *pucPtr;
571
572                 for( pucPtr = pcHostName; *pucPtr; pucPtr++ )
573                 {
574                         if( *pucPtr == '.' )
575                         {
576                                 bHasDot = pdTRUE;
577                                 break;
578                         }
579                 }
580         }
581         #endif /* ipconfigUSE_LLMNR == 1 */
582
583         /* Two is added at the end for the count of characters in the first
584         subdomain part and the string end byte. */
585         uxExpectedPayloadLength = sizeof( DNSMessage_t ) + strlen( pcHostName ) + sizeof( uint16_t ) + sizeof( uint16_t ) + 2u;
586
587         xDNSSocket = prvCreateDNSSocket();
588
589         if( xDNSSocket != NULL )
590         {
591                 FreeRTOS_setsockopt( xDNSSocket, 0, FREERTOS_SO_SNDTIMEO, ( void * ) &uxWriteTimeOut_ticks, sizeof( TickType_t ) );
592                 FreeRTOS_setsockopt( xDNSSocket, 0, FREERTOS_SO_RCVTIMEO, ( void * ) &uxReadTimeOut_ticks,  sizeof( TickType_t ) );
593
594                 for( xAttempt = 0; xAttempt < ipconfigDNS_REQUEST_ATTEMPTS; xAttempt++ )
595                 {
596                         /* Get a buffer.  This uses a maximum delay, but the delay will be
597                         capped to ipconfigUDP_MAX_SEND_BLOCK_TIME_TICKS so the return value
598                         still needs to be tested. */
599                         pucUDPPayloadBuffer = ( uint8_t * ) FreeRTOS_GetUDPPayloadBuffer( uxExpectedPayloadLength, portMAX_DELAY );
600
601                         if( pucUDPPayloadBuffer != NULL )
602                         {
603                                 /* Create the message in the obtained buffer. */
604                                 uxPayloadLength = prvCreateDNSMessage( pucUDPPayloadBuffer, pcHostName, uxIdentifier );
605
606                                 iptraceSENDING_DNS_REQUEST();
607
608                                 /* Obtain the DNS server address. */
609                                 FreeRTOS_GetAddressConfiguration( NULL, NULL, NULL, &ulIPAddress );
610
611                                 /* Send the DNS message. */
612 #if( ipconfigUSE_LLMNR == 1 )
613                                 if( bHasDot == pdFALSE )
614                                 {
615                                         /* Use LLMNR addressing. */
616                                         ( ( DNSMessage_t * ) pucUDPPayloadBuffer )->usFlags = 0;
617                                         xAddress.sin_addr = ipLLMNR_IP_ADDR; /* Is in network byte order. */
618                                         xAddress.sin_port = FreeRTOS_ntohs( ipLLMNR_PORT );
619                                 }
620                                 else
621 #endif
622                                 {
623                                         /* Use DNS server. */
624                                         xAddress.sin_addr = ulIPAddress;
625                                         xAddress.sin_port = dnsDNS_PORT;
626                                 }
627
628                                 ulIPAddress = 0uL;
629
630                                 if( FreeRTOS_sendto( xDNSSocket, pucUDPPayloadBuffer, uxPayloadLength, FREERTOS_ZERO_COPY, &xAddress, sizeof( xAddress ) ) != 0 )
631                                 {
632                                         /* Wait for the reply. */
633                                         lBytes = FreeRTOS_recvfrom( xDNSSocket, &pucUDPPayloadBuffer, 0, FREERTOS_ZERO_COPY, &xAddress, &ulAddressLength );
634
635                                         if( lBytes > 0 )
636                                         {
637                                         BaseType_t xExpected;
638                                         DNSMessage_t *pxDNSMessageHeader = ( DNSMessage_t * ) pucUDPPayloadBuffer;
639
640                                                 /* See if the identifiers match. */
641                                                 if( uxIdentifier == ( TickType_t ) pxDNSMessageHeader->usIdentifier )
642                                                 {
643                                                         xExpected = pdTRUE;
644                                                 }
645                                                 else
646                                                 {
647                                                         /* The reply was not expected. */
648                                                         xExpected = pdFALSE;
649                                                 }
650
651                                                 /* The reply was received.  Process it. */
652                                         #if( ipconfigDNS_USE_CALLBACKS == 0 )
653                                                 /* It is useless to analyse the unexpected reply
654                                                 unless asynchronous look-ups are enabled. */
655                                                 if( xExpected != pdFALSE )
656                                         #endif /* ipconfigDNS_USE_CALLBACKS == 0 */
657                                                 {
658                                                         ulIPAddress = prvParseDNSReply( pucUDPPayloadBuffer, ( size_t ) lBytes, xExpected );
659                                                 }
660
661                                                 /* Finished with the buffer.  The zero copy interface
662                                                 is being used, so the buffer must be freed by the
663                                                 task. */
664                                                 FreeRTOS_ReleaseUDPPayloadBuffer( ( void * ) pucUDPPayloadBuffer );
665
666                                                 if( ulIPAddress != 0uL )
667                                                 {
668                                                         /* All done. */
669                                                         break;
670                                                 }
671                                         }
672                                 }
673                                 else
674                                 {
675                                         /* The message was not sent so the stack will not be
676                                         releasing the zero copy - it must be released here. */
677                                         FreeRTOS_ReleaseUDPPayloadBuffer( ( void * ) pucUDPPayloadBuffer );
678                                 }
679                         }
680
681                         if( uxReadTimeOut_ticks == 0u )
682                         {
683                                 /* This DNS lookup is asynchronous, using a call-back:
684                                 send the request only once. */
685                                 break;
686                         }
687                 }
688
689                 /* Finished with the socket. */
690                 FreeRTOS_closesocket( xDNSSocket );
691         }
692
693         return ulIPAddress;
694 }
695 /*-----------------------------------------------------------*/
696
697 static size_t prvCreateDNSMessage( uint8_t *pucUDPPayloadBuffer,
698                                                                    const char *pcHostName,
699                                                                    TickType_t uxIdentifier )
700 {
701 DNSMessage_t *pxDNSMessageHeader;
702 uint8_t *pucStart, *pucByte;
703 DNSTail_t *pxTail;
704 static const DNSMessage_t xDefaultPartDNSHeader =
705 {
706         0,                 /* The identifier will be overwritten. */
707         dnsOUTGOING_FLAGS, /* Flags set for standard query. */
708         dnsONE_QUESTION,   /* One question is being asked. */
709         0,                 /* No replies are included. */
710         0,                 /* No authorities. */
711         0                  /* No additional authorities. */
712 };
713
714         /* Copy in the const part of the header. */
715         memcpy( ( void * ) pucUDPPayloadBuffer, ( void * ) &xDefaultPartDNSHeader, sizeof( xDefaultPartDNSHeader ) );
716
717         /* Write in a unique identifier. */
718         pxDNSMessageHeader = ( DNSMessage_t * ) pucUDPPayloadBuffer;
719         pxDNSMessageHeader->usIdentifier = ( uint16_t ) uxIdentifier;
720
721         /* Create the resource record at the end of the header.  First
722         find the end of the header. */
723         pucStart = pucUDPPayloadBuffer + sizeof( xDefaultPartDNSHeader );
724
725         /* Leave a gap for the first length bytes. */
726         pucByte = pucStart + 1;
727
728         /* Copy in the host name. */
729         strcpy( ( char * ) pucByte, pcHostName );
730
731         /* Mark the end of the string. */
732         pucByte += strlen( pcHostName );
733         *pucByte = 0x00u;
734
735         /* Walk the string to replace the '.' characters with byte counts.
736         pucStart holds the address of the byte count.  Walking the string
737         starts after the byte count position. */
738         pucByte = pucStart;
739
740         do
741         {
742                 pucByte++;
743
744                 while( ( *pucByte != 0x00 ) && ( *pucByte != '.' ) )
745                 {
746                         pucByte++;
747                 }
748
749                 /* Fill in the byte count, then move the pucStart pointer up to
750                 the found byte position. */
751                 *pucStart = ( uint8_t ) ( ( uint32_t ) pucByte - ( uint32_t ) pucStart );
752                 ( *pucStart )--;
753
754                 pucStart = pucByte;
755         } while( *pucByte != 0x00 );
756
757         /* Finish off the record. */
758
759         pxTail = ( DNSTail_t * ) ( pucByte + 1 );
760
761         vSetField16( pxTail, DNSTail_t, usType, dnsTYPE_A_HOST ); /* Type A: host */
762         vSetField16( pxTail, DNSTail_t, usClass, dnsCLASS_IN );   /* 1: Class IN */
763
764         /* Return the total size of the generated message, which is the space from
765         the last written byte to the beginning of the buffer. */
766         return ( ( uint32_t ) pucByte - ( uint32_t ) pucUDPPayloadBuffer + 1 ) + sizeof( *pxTail );
767 }
768 /*-----------------------------------------------------------*/
769
770 #if( ipconfigUSE_DNS_CACHE == 1 ) || ( ipconfigDNS_USE_CALLBACKS == 1 )
771
772         static uint8_t * prvReadNameField( uint8_t *pucByte,
773                                                                            size_t uxSourceLen,
774                                                                            char *pcName,
775                                                                            size_t uxDestLen )
776         {
777         size_t uxNameLen = 0;
778         BaseType_t xCount;
779
780                 if( 0 == uxSourceLen )
781                 {
782                         return NULL;
783                 }
784
785                 /* Determine if the name is the fully coded name, or an offset to the name
786                 elsewhere in the message. */
787                 if( ( *pucByte & dnsNAME_IS_OFFSET ) == dnsNAME_IS_OFFSET )
788                 {
789                         /* Jump over the two byte offset. */
790                         if( uxSourceLen > sizeof( uint16_t ) )
791                         {
792                                 pucByte += sizeof( uint16_t );
793                         }
794                         else
795                         {
796                                 pucByte = NULL;
797                         }
798                 }
799                 else
800                 {
801                         /* pucByte points to the full name. Walk over the string. */
802                         while( ( NULL != pucByte ) && ( *pucByte != 0x00u ) && ( uxSourceLen > 1u ) )
803                         {
804                                 /* If this is not the first time through the loop, then add a
805                                 separator in the output. */
806                                 if( ( uxNameLen > 0 ) && ( uxNameLen < ( uxDestLen - 1u ) ) )
807                                 {
808                                         pcName[ uxNameLen++ ] = '.';
809                                 }
810
811                                 /* Process the first/next sub-string. */
812                                 for( xCount = *( pucByte++ ), uxSourceLen--;
813                                          xCount-- && uxSourceLen > 1u;
814                                          pucByte++, uxSourceLen-- )
815                                 {
816                                         if( uxNameLen < uxDestLen - 1u )
817                                         {
818                                                 pcName[ uxNameLen++ ] = *( ( char * ) pucByte );
819                                         }
820                                         else
821                                         {
822                                                 /* DNS name is too big for the provided buffer. */
823                                                 pucByte = NULL;
824                                                 break;
825                                         }
826                                 }
827                         }
828
829                         /* Confirm that a fully formed name was found. */
830                         if( NULL != pucByte )
831                         {
832                                 if( 0x00 == *pucByte )
833                                 {
834                                         pucByte++;
835                                         uxSourceLen--;
836                                         pcName[ uxNameLen++ ] = '\0';
837                                 }
838                                 else
839                                 {
840                                         pucByte = NULL;
841                                 }
842                         }
843                 }
844
845                 return pucByte;
846         }
847 #endif /* ipconfigUSE_DNS_CACHE || ipconfigDNS_USE_CALLBACKS */
848 /*-----------------------------------------------------------*/
849
850 static uint8_t * prvSkipNameField( uint8_t *pucByte,
851                                                                    size_t uxSourceLen )
852 {
853 size_t uxChunkLength;
854
855         if( 0u == uxSourceLen )
856         {
857                 return NULL;
858         }
859
860         /* Determine if the name is the fully coded name, or an offset to the name
861         elsewhere in the message. */
862         if( ( *pucByte & dnsNAME_IS_OFFSET ) == dnsNAME_IS_OFFSET )
863         {
864                 /* Jump over the two byte offset. */
865                 if( uxSourceLen > sizeof( uint16_t ) )
866                 {
867                         pucByte += sizeof( uint16_t );
868                 }
869                 else
870                 {
871                         pucByte = NULL;
872                 }
873         }
874         else
875         {
876                 /* pucByte points to the full name. Walk over the string. */
877                 while( ( *pucByte != 0x00u ) && ( uxSourceLen > 1u ) )
878                 {
879                         uxChunkLength = *pucByte + 1u;
880
881                         if( uxSourceLen > uxChunkLength )
882                         {
883                                 uxSourceLen -= uxChunkLength;
884                                 pucByte += uxChunkLength;
885                         }
886                         else
887                         {
888                                 pucByte = NULL;
889                                 break;
890                         }
891                 }
892
893                 /* Confirm that a fully formed name was found. */
894                 if( NULL != pucByte )
895                 {
896                         if( 0x00u == *pucByte )
897                         {
898                                 pucByte++;
899                         }
900                         else
901                         {
902                                 pucByte = NULL;
903                         }
904                 }
905         }
906
907         return pucByte;
908 }
909 /*-----------------------------------------------------------*/
910
911 /* The function below will only be called :
912 when ipconfigDNS_USE_CALLBACKS == 1
913 when ipconfigUSE_LLMNR == 1
914 for testing purposes, by the module iot_test_freertos_tcp.c
915 */
916 uint32_t ulDNSHandlePacket( NetworkBufferDescriptor_t *pxNetworkBuffer )
917 {
918 DNSMessage_t *pxDNSMessageHeader;
919 size_t uxPayloadSize;
920
921     /* Only proceed if the payload length indicated in the header
922     appears to be valid. */
923     if( pxNetworkBuffer->xDataLength >= sizeof( UDPPacket_t ) )
924     {
925         uxPayloadSize = pxNetworkBuffer->xDataLength - sizeof( UDPPacket_t );
926
927         if( uxPayloadSize >= sizeof( DNSMessage_t ) )
928         {
929             pxDNSMessageHeader =
930                 ( DNSMessage_t * ) ( pxNetworkBuffer->pucEthernetBuffer + sizeof( UDPPacket_t ) );
931
932             /* The parameter pdFALSE indicates that the reply was not expected. */
933             prvParseDNSReply( ( uint8_t * ) pxDNSMessageHeader,
934                 uxPayloadSize,
935                 pdFALSE );
936         }
937     }
938
939         /* The packet was not consumed. */
940         return pdFAIL;
941 }
942 /*-----------------------------------------------------------*/
943
944 #if( ipconfigUSE_NBNS == 1 )
945
946         uint32_t ulNBNSHandlePacket( NetworkBufferDescriptor_t * pxNetworkBuffer )
947         {
948         UDPPacket_t *pxUDPPacket = ( UDPPacket_t * ) pxNetworkBuffer->pucEthernetBuffer;
949         uint8_t *pucUDPPayloadBuffer = pxNetworkBuffer->pucEthernetBuffer + sizeof( UDPPacket_t );
950         size_t uxPayloadSize = pxNetworkBuffer->xDataLength - sizeof( UDPPacket_t );
951
952                 /* The network buffer data length has already been set to the
953                 length of the UDP payload. */
954                 prvTreatNBNS( pucUDPPayloadBuffer,
955                                           uxPayloadSize,
956                                           pxUDPPacket->xIPHeader.ulSourceIPAddress );
957
958                 /* The packet was not consumed. */
959                 return pdFAIL;
960         }
961
962 #endif /* ipconfigUSE_NBNS */
963 /*-----------------------------------------------------------*/
964
965 static uint32_t prvParseDNSReply( uint8_t *pucUDPPayloadBuffer,
966                                                                   size_t uxBufferLength,
967                                                                   BaseType_t xExpected )
968 {
969 DNSMessage_t *pxDNSMessageHeader;
970 DNSAnswerRecord_t *pxDNSAnswerRecord;
971 uint32_t ulIPAddress = 0uL;
972 #if( ipconfigUSE_LLMNR == 1 )
973         char *pcRequestedName = NULL;
974 #endif
975 uint8_t *pucByte;
976 size_t uxSourceBytesRemaining;
977 uint16_t x, usDataLength, usQuestions;
978 BaseType_t xDoStore = xExpected;
979 #if( ipconfigUSE_LLMNR == 1 )
980         uint16_t usType = 0, usClass = 0;
981 #endif
982 #if( ipconfigUSE_DNS_CACHE == 1 ) || ( ipconfigDNS_USE_CALLBACKS == 1 )
983         char pcName[ ipconfigDNS_CACHE_NAME_LENGTH ] = "";
984 #endif
985
986         /* Ensure that the buffer is of at least minimal DNS message length. */
987         if( uxBufferLength < sizeof( DNSMessage_t ) )
988         {
989                 return dnsPARSE_ERROR;
990         }
991
992         uxSourceBytesRemaining = uxBufferLength;
993
994         /* Parse the DNS message header. */
995         pxDNSMessageHeader = ( DNSMessage_t * ) pucUDPPayloadBuffer;
996
997         /* Introduce a do {} while (0) to allow the use of breaks. */
998         do
999         {
1000                 /* Start at the first byte after the header. */
1001                 pucByte = pucUDPPayloadBuffer + sizeof( DNSMessage_t );
1002                 uxSourceBytesRemaining -= sizeof( DNSMessage_t );
1003
1004                 /* Skip any question records. */
1005                 usQuestions = FreeRTOS_ntohs( pxDNSMessageHeader->usQuestions );
1006
1007                 for( x = 0; x < usQuestions; x++ )
1008                 {
1009                         #if( ipconfigUSE_LLMNR == 1 )
1010                         {
1011                                 if( x == 0 )
1012                                 {
1013                                         pcRequestedName = ( char * ) pucByte;
1014                                 }
1015                         }
1016                         #endif
1017
1018 #if( ipconfigUSE_DNS_CACHE == 1 ) || ( ipconfigDNS_USE_CALLBACKS == 1 )
1019                         if( x == 0 )
1020                         {
1021                                 pucByte = prvReadNameField( pucByte,
1022                                                                                         uxSourceBytesRemaining,
1023                                                                                         pcName,
1024                                                                                         sizeof( pcName ) );
1025
1026                                 /* Check for a malformed response. */
1027                                 if( NULL == pucByte )
1028                                 {
1029                                         return dnsPARSE_ERROR;
1030                                 }
1031
1032                                 uxSourceBytesRemaining = ( pucUDPPayloadBuffer + uxBufferLength ) - pucByte;
1033                         }
1034                         else
1035 #endif /* ipconfigUSE_DNS_CACHE || ipconfigDNS_USE_CALLBACKS */
1036                         {
1037                                 /* Skip the variable length pcName field. */
1038                                 pucByte = prvSkipNameField( pucByte,
1039                                                                                         uxSourceBytesRemaining );
1040
1041                                 /* Check for a malformed response. */
1042                                 if( NULL == pucByte )
1043                                 {
1044                                         return dnsPARSE_ERROR;
1045                                 }
1046
1047                                 uxSourceBytesRemaining = ( size_t )
1048                                         ( pucUDPPayloadBuffer + uxBufferLength - pucByte );
1049                         }
1050
1051                         /* Check the remaining buffer size. */
1052                         if( uxSourceBytesRemaining >= sizeof( uint32_t ) )
1053                         {
1054                                 #if( ipconfigUSE_LLMNR == 1 )
1055                                 {
1056                                         /* usChar2u16 returns value in host endianness. */
1057                                         usType = usChar2u16( pucByte );
1058                                         usClass = usChar2u16( pucByte + 2 );
1059                                 }
1060                                 #endif /* ipconfigUSE_LLMNR */
1061
1062                                 /* Skip the type and class fields. */
1063                                 pucByte += sizeof( uint32_t );
1064                                 uxSourceBytesRemaining -= sizeof( uint32_t );
1065                         }
1066                         else
1067                         {
1068                                 /* Malformed response. */
1069                                 return dnsPARSE_ERROR;
1070                         }
1071                 }
1072
1073                 /* Search through the answer records. */
1074                 pxDNSMessageHeader->usAnswers = FreeRTOS_ntohs( pxDNSMessageHeader->usAnswers );
1075
1076                 if( ( pxDNSMessageHeader->usFlags & dnsRX_FLAGS_MASK ) == dnsEXPECTED_RX_FLAGS )
1077                 {
1078                         for( x = 0; x < pxDNSMessageHeader->usAnswers; x++ )
1079                         {
1080                                 pucByte = prvSkipNameField( pucByte,
1081                                                                                         uxSourceBytesRemaining );
1082
1083                                 /* Check for a malformed response. */
1084                                 if( NULL == pucByte )
1085                                 {
1086                                         return dnsPARSE_ERROR;
1087                                 }
1088
1089                                 uxSourceBytesRemaining = ( size_t )
1090                                         ( pucUDPPayloadBuffer + uxBufferLength - pucByte );
1091
1092                                 /* Is there enough data for an IPv4 A record answer and, if so,
1093                                 is this an A record? */
1094                                 if( ( uxSourceBytesRemaining >= ( sizeof( DNSAnswerRecord_t ) + sizeof( uint32_t ) ) ) &&
1095                                         ( usChar2u16( pucByte ) == dnsTYPE_A_HOST ) )
1096                                 {
1097                                         /* This is the required record type and is of sufficient size. */
1098                                         pxDNSAnswerRecord = ( DNSAnswerRecord_t * ) pucByte;
1099
1100                                         /* Sanity check the data length of an IPv4 answer. */
1101                                         if( FreeRTOS_ntohs( pxDNSAnswerRecord->usDataLength ) == sizeof( uint32_t ) )
1102                                         {
1103                                                 /* Copy the IP address out of the record. */
1104                                                 memcpy( &ulIPAddress,
1105                                                                 pucByte + sizeof( DNSAnswerRecord_t ),
1106                                                                 sizeof( uint32_t ) );
1107
1108                                                 #if( ipconfigDNS_USE_CALLBACKS == 1 )
1109                                                 {
1110                                                         /* See if any asynchronous call was made to FreeRTOS_gethostbyname_a() */
1111                                                         if( xDNSDoCallback( ( TickType_t ) pxDNSMessageHeader->usIdentifier, pcName, ulIPAddress ) != pdFALSE )
1112                                                         {
1113                                                                 /* This device has requested this DNS look-up.
1114                                                                 The result may be stored in the DNS cache. */
1115                                                                 xDoStore = pdTRUE;
1116                                                         }
1117                                                 }
1118                                                 #endif /* ipconfigDNS_USE_CALLBACKS == 1 */
1119                                                 #if( ipconfigUSE_DNS_CACHE == 1 )
1120                                                 {
1121                                                         /* The reply will only be stored in the DNS cache when the
1122                                                         request was issued by this device. */
1123                                                         if( xDoStore != pdFALSE )
1124                                                         {
1125                                                                 prvProcessDNSCache( pcName, &ulIPAddress, pxDNSAnswerRecord->ulTTL, pdFALSE );
1126                                                         }
1127
1128                                                         /* Show what has happened. */
1129                                                         FreeRTOS_printf( ( "DNS[0x%04X]: The answer to '%s' (%xip) will%s be stored\n",
1130                                                                                            ( unsigned ) pxDNSMessageHeader->usIdentifier,
1131                                                                                            pcName,
1132                                                                                            ( unsigned ) FreeRTOS_ntohl( ulIPAddress ),
1133                                                                                            ( xDoStore != 0 ) ? "" : " NOT" ) );
1134                                                 }
1135                                                 #endif /* ipconfigUSE_DNS_CACHE */
1136                                         }
1137
1138                                         pucByte += sizeof( DNSAnswerRecord_t ) + sizeof( uint32_t );
1139                                         uxSourceBytesRemaining -= ( sizeof( DNSAnswerRecord_t ) + sizeof( uint32_t ) );
1140                                         break;
1141                                 }
1142                                 else if( uxSourceBytesRemaining >= sizeof( DNSAnswerRecord_t ) )
1143                                 {
1144                                         /* It's not an A record, so skip it. Get the header location
1145                                         and then jump over the header. */
1146                                         pxDNSAnswerRecord = ( DNSAnswerRecord_t * ) pucByte;
1147                                         pucByte += sizeof( DNSAnswerRecord_t );
1148                                         uxSourceBytesRemaining -= sizeof( DNSAnswerRecord_t );
1149
1150                                         /* Determine the length of the answer data from the header. */
1151                                         usDataLength = FreeRTOS_ntohs( pxDNSAnswerRecord->usDataLength );
1152
1153                                         /* Jump over the answer. */
1154                                         if( uxSourceBytesRemaining >= usDataLength )
1155                                         {
1156                                                 pucByte += usDataLength;
1157                                                 uxSourceBytesRemaining -= usDataLength;
1158                                         }
1159                                         else
1160                                         {
1161                                                 /* Malformed response. */
1162                                                 return dnsPARSE_ERROR;
1163                                         }
1164                                 }
1165                         }
1166                 }
1167
1168 #if( ipconfigUSE_LLMNR == 1 )
1169                 else if( usQuestions && ( usType == dnsTYPE_A_HOST ) && ( usClass == dnsCLASS_IN ) )
1170                 {
1171                         /* If this is not a reply to our DNS request, it might an LLMNR
1172                         request. */
1173                         if( xApplicationDNSQueryHook( ( pcRequestedName + 1 ) ) )
1174                         {
1175                         int16_t usLength;
1176                         NetworkBufferDescriptor_t *pxNewBuffer = NULL;
1177                         NetworkBufferDescriptor_t *pxNetworkBuffer = pxUDPPayloadBuffer_to_NetworkBuffer( pucUDPPayloadBuffer );
1178                         LLMNRAnswer_t *pxAnswer;
1179
1180                                 if( ( xBufferAllocFixedSize == pdFALSE ) && ( pxNetworkBuffer != NULL ) )
1181                                 {
1182                                 BaseType_t xDataLength = uxBufferLength + sizeof( UDPHeader_t ) + sizeof( EthernetHeader_t ) + sizeof( IPHeader_t );
1183
1184                                         /* Set the size of the outgoing packet. */
1185                                         pxNetworkBuffer->xDataLength = xDataLength;
1186                                         pxNewBuffer = pxDuplicateNetworkBufferWithDescriptor( pxNetworkBuffer, xDataLength + sizeof( LLMNRAnswer_t ) );
1187
1188                                         if( pxNewBuffer != NULL )
1189                                         {
1190                                         BaseType_t xOffset1, xOffset2;
1191
1192                                                 xOffset1 = ( BaseType_t ) ( pucByte - pucUDPPayloadBuffer );
1193                                                 xOffset2 = ( BaseType_t ) ( ( ( uint8_t * ) pcRequestedName ) - pucUDPPayloadBuffer );
1194
1195                                                 pxNetworkBuffer = pxNewBuffer;
1196                                                 pucUDPPayloadBuffer = pxNetworkBuffer->pucEthernetBuffer + ipUDP_PAYLOAD_OFFSET_IPv4;
1197
1198                                                 pucByte = pucUDPPayloadBuffer + xOffset1;
1199                                                 pcRequestedName = ( char * ) ( pucUDPPayloadBuffer + xOffset2 );
1200                                                 pxDNSMessageHeader = ( DNSMessage_t * ) pucUDPPayloadBuffer;
1201                                         }
1202                                         else
1203                                         {
1204                                                 /* Just to indicate that the message may not be answered. */
1205                                                 pxNetworkBuffer = NULL;
1206                                         }
1207                                 }
1208
1209                                 if( pxNetworkBuffer != NULL )
1210                                 {
1211                                         pxAnswer = ( LLMNRAnswer_t * ) pucByte;
1212
1213                                         /* We leave 'usIdentifier' and 'usQuestions' untouched */
1214                                         vSetField16( pxDNSMessageHeader, DNSMessage_t, usFlags, dnsLLMNR_FLAGS_IS_REPONSE ); /* Set the response flag */
1215                                         vSetField16( pxDNSMessageHeader, DNSMessage_t, usAnswers, 1 );                       /* Provide a single answer */
1216                                         vSetField16( pxDNSMessageHeader, DNSMessage_t, usAuthorityRRs, 0 );                  /* No authority */
1217                                         vSetField16( pxDNSMessageHeader, DNSMessage_t, usAdditionalRRs, 0 );                 /* No additional info */
1218
1219                                         pxAnswer->ucNameCode = dnsNAME_IS_OFFSET;
1220                                         pxAnswer->ucNameOffset = ( uint8_t ) ( pcRequestedName - ( char * ) pucUDPPayloadBuffer );
1221
1222                                         vSetField16( pxAnswer, LLMNRAnswer_t, usType, dnsTYPE_A_HOST ); /* Type A: host */
1223                                         vSetField16( pxAnswer, LLMNRAnswer_t, usClass, dnsCLASS_IN );   /* 1: Class IN */
1224                                         vSetField32( pxAnswer, LLMNRAnswer_t, ulTTL, dnsLLMNR_TTL_VALUE );
1225                                         vSetField16( pxAnswer, LLMNRAnswer_t, usDataLength, 4 );
1226                                         vSetField32( pxAnswer, LLMNRAnswer_t, ulIPAddress, FreeRTOS_ntohl( *ipLOCAL_IP_ADDRESS_POINTER ) );
1227
1228                                         usLength = ( int16_t ) ( sizeof( *pxAnswer ) + ( size_t ) ( pucByte - pucUDPPayloadBuffer ) );
1229
1230                                         prvReplyDNSMessage( pxNetworkBuffer, usLength );
1231
1232                                         if( pxNewBuffer != NULL )
1233                                         {
1234                                                 vReleaseNetworkBufferAndDescriptor( pxNewBuffer );
1235                                         }
1236                                 }
1237                         }
1238                 }
1239 #endif /* ipconfigUSE_LLMNR == 1 */
1240         } while( 0 );
1241
1242         if( xExpected == pdFALSE )
1243         {
1244                 /* Do not return a valid IP-address in case the reply was not expected. */
1245                 ulIPAddress = 0uL;
1246         }
1247
1248         return ulIPAddress;
1249 }
1250 /*-----------------------------------------------------------*/
1251
1252 #if( ipconfigUSE_NBNS == 1 )
1253
1254         static void prvTreatNBNS( uint8_t *pucUDPPayloadBuffer,
1255                                                           size_t uxBufferLength,
1256                                                           uint32_t ulIPAddress )
1257         {
1258         uint16_t usFlags, usType, usClass;
1259         uint8_t *pucSource, *pucTarget;
1260         uint8_t ucByte;
1261         uint8_t ucNBNSName[ 17 ];
1262
1263                 /* Check for minimum buffer size. */
1264                 if( uxBufferLength < sizeof( NBNSRequest_t ) )
1265                 {
1266                         return;
1267                 }
1268
1269                 /* Read the request flags in host endianness. */
1270                 usFlags = usChar2u16( pucUDPPayloadBuffer + offsetof( NBNSRequest_t, usFlags ) );
1271
1272                 if( ( usFlags & dnsNBNS_FLAGS_OPCODE_MASK ) == dnsNBNS_FLAGS_OPCODE_QUERY )
1273                 {
1274                         usType  = usChar2u16( pucUDPPayloadBuffer + offsetof( NBNSRequest_t, usType ) );
1275                         usClass = usChar2u16( pucUDPPayloadBuffer + offsetof( NBNSRequest_t, usClass ) );
1276
1277                         /* Not used for now */
1278                         ( void ) usClass;
1279
1280                         /* For NBNS a name is 16 bytes long, written with capitals only.
1281                         Make sure that the copy is terminated with a zero. */
1282                         pucTarget = ucNBNSName + sizeof( ucNBNSName ) - 2;
1283                         pucTarget[ 1 ] = '\0';
1284
1285                         /* Start with decoding the last 2 bytes. */
1286                         pucSource = pucUDPPayloadBuffer + ( offsetof( NBNSRequest_t, ucName ) + ( dnsNBNS_ENCODED_NAME_LENGTH - 2 ) );
1287
1288                         for( ;; )
1289                         {
1290                                 ucByte = ( uint8_t ) ( ( ( pucSource[ 0 ] - 0x41 ) << 4 ) | ( pucSource[ 1 ] - 0x41 ) );
1291
1292                                 /* Make sure there are no trailing spaces in the name. */
1293                                 if( ( ucByte == ' ' ) && ( pucTarget[ 1 ] == '\0' ) )
1294                                 {
1295                                         ucByte = '\0';
1296                                 }
1297
1298                                 *pucTarget = ucByte;
1299
1300                                 if( pucTarget == ucNBNSName )
1301                                 {
1302                                         break;
1303                                 }
1304
1305                                 pucTarget -= 1;
1306                                 pucSource -= 2;
1307                         }
1308
1309                         #if( ipconfigUSE_DNS_CACHE == 1 )
1310                         {
1311                                 if( ( usFlags & dnsNBNS_FLAGS_RESPONSE ) != 0 )
1312                                 {
1313                                         /* If this is a response from another device,
1314                                         add the name to the DNS cache */
1315                                         prvProcessDNSCache( ( char * ) ucNBNSName, &ulIPAddress, 0, pdFALSE );
1316                                 }
1317                         }
1318                         #else
1319                         {
1320                                 /* Avoid compiler warnings. */
1321                                 ( void ) ulIPAddress;
1322                         }
1323                         #endif /* ipconfigUSE_DNS_CACHE */
1324
1325                         if( ( ( usFlags & dnsNBNS_FLAGS_RESPONSE ) == 0 ) &&
1326                                 ( usType == dnsNBNS_TYPE_NET_BIOS ) &&
1327                                 ( xApplicationDNSQueryHook( ( const char * ) ucNBNSName ) != pdFALSE ) )
1328                         {
1329                         uint16_t usLength;
1330                         DNSMessage_t *pxMessage;
1331                         NBNSAnswer_t *pxAnswer;
1332
1333                                 /* Someone is looking for a device with ucNBNSName,
1334                                 prepare a positive reply. */
1335                                 NetworkBufferDescriptor_t *pxNetworkBuffer = pxUDPPayloadBuffer_to_NetworkBuffer( pucUDPPayloadBuffer );
1336
1337                                 if( ( xBufferAllocFixedSize == pdFALSE ) && ( pxNetworkBuffer != NULL ) )
1338                                 {
1339                                 NetworkBufferDescriptor_t *pxNewBuffer;
1340
1341                                         /* The field xDataLength was set to the total length of the UDP packet,
1342                                         i.e. the payload size plus sizeof( UDPPacket_t ). */
1343                                         pxNewBuffer = pxDuplicateNetworkBufferWithDescriptor( pxNetworkBuffer, pxNetworkBuffer->xDataLength + sizeof( NBNSAnswer_t ) );
1344
1345                                         if( pxNewBuffer != NULL )
1346                                         {
1347                                                 pucUDPPayloadBuffer = pxNewBuffer->pucEthernetBuffer + sizeof( UDPPacket_t );
1348                                                 pxNetworkBuffer = pxNewBuffer;
1349                                         }
1350                                         else
1351                                         {
1352                                                 /* Just prevent that a reply will be sent */
1353                                                 pxNetworkBuffer = NULL;
1354                                         }
1355                                 }
1356
1357                                 /* Should not occur: pucUDPPayloadBuffer is part of a xNetworkBufferDescriptor */
1358                                 if( pxNetworkBuffer != NULL )
1359                                 {
1360                                         pxMessage = ( DNSMessage_t * ) pucUDPPayloadBuffer;
1361
1362                                         /* As the fields in the structures are not word-aligned, we have to
1363                                         copy the values byte-by-byte using macro's vSetField16() and vSetField32() */
1364                                         vSetField16( pxMessage, DNSMessage_t, usFlags, dnsNBNS_QUERY_RESPONSE_FLAGS ); /* 0x8500 */
1365                                         vSetField16( pxMessage, DNSMessage_t, usQuestions, 0 );
1366                                         vSetField16( pxMessage, DNSMessage_t, usAnswers, 1 );
1367                                         vSetField16( pxMessage, DNSMessage_t, usAuthorityRRs, 0 );
1368                                         vSetField16( pxMessage, DNSMessage_t, usAdditionalRRs, 0 );
1369
1370                                         pxAnswer = ( NBNSAnswer_t * ) ( pucUDPPayloadBuffer + offsetof( NBNSRequest_t, usType ) );
1371
1372                                         vSetField16( pxAnswer, NBNSAnswer_t, usType, usType );            /* Type */
1373                                         vSetField16( pxAnswer, NBNSAnswer_t, usClass, dnsNBNS_CLASS_IN ); /* Class */
1374                                         vSetField32( pxAnswer, NBNSAnswer_t, ulTTL, dnsNBNS_TTL_VALUE );
1375                                         vSetField16( pxAnswer, NBNSAnswer_t, usDataLength, 6 );           /* 6 bytes including the length field */
1376                                         vSetField16( pxAnswer, NBNSAnswer_t, usNbFlags, dnsNBNS_NAME_FLAGS );
1377                                         vSetField32( pxAnswer, NBNSAnswer_t, ulIPAddress, FreeRTOS_ntohl( *ipLOCAL_IP_ADDRESS_POINTER ) );
1378
1379                                         usLength = ( uint16_t ) ( offsetof( NBNSRequest_t, usType ) + sizeof( NBNSAnswer_t ) );
1380
1381                                         prvReplyDNSMessage( pxNetworkBuffer, usLength );
1382                                 }
1383                         }
1384                 }
1385         }
1386
1387 #endif /* ipconfigUSE_NBNS */
1388 /*-----------------------------------------------------------*/
1389
1390 static Socket_t prvCreateDNSSocket( void )
1391 {
1392 Socket_t xSocket = NULL;
1393 struct freertos_sockaddr xAddress;
1394 BaseType_t xReturn;
1395
1396         /* This must be the first time this function has been called.  Create
1397         the socket. */
1398         xSocket = FreeRTOS_socket( FREERTOS_AF_INET, FREERTOS_SOCK_DGRAM, FREERTOS_IPPROTO_UDP );
1399
1400         /* Auto bind the port. */
1401         xAddress.sin_port = 0u;
1402         xReturn = FreeRTOS_bind( xSocket, &xAddress, sizeof( xAddress ) );
1403
1404         /* Check the bind was successful, and clean up if not. */
1405         if( xReturn != 0 )
1406         {
1407                 FreeRTOS_closesocket( xSocket );
1408                 xSocket = NULL;
1409         }
1410         else
1411         {
1412                 /* The send and receive timeouts will be set later on. */
1413         }
1414
1415         return xSocket;
1416 }
1417 /*-----------------------------------------------------------*/
1418
1419 #if( ( ipconfigUSE_NBNS == 1 ) || ( ipconfigUSE_LLMNR == 1 ) )
1420
1421         static void prvReplyDNSMessage( NetworkBufferDescriptor_t *pxNetworkBuffer,
1422                                                                         BaseType_t lNetLength )
1423         {
1424         UDPPacket_t *pxUDPPacket;
1425         IPHeader_t *pxIPHeader;
1426         UDPHeader_t *pxUDPHeader;
1427
1428                 pxUDPPacket = ( UDPPacket_t * ) pxNetworkBuffer->pucEthernetBuffer;
1429                 pxIPHeader = &pxUDPPacket->xIPHeader;
1430                 pxUDPHeader = &pxUDPPacket->xUDPHeader;
1431                 /* HT: started using defines like 'ipSIZE_OF_xxx' */
1432                 pxIPHeader->usLength                       = FreeRTOS_htons( lNetLength + ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_UDP_HEADER );
1433                 /* HT:endian: should not be translated, copying from packet to packet */
1434                 pxIPHeader->ulDestinationIPAddress = pxIPHeader->ulSourceIPAddress;
1435                 pxIPHeader->ulSourceIPAddress      = *ipLOCAL_IP_ADDRESS_POINTER;
1436                 pxIPHeader->ucTimeToLive                   = ipconfigUDP_TIME_TO_LIVE;
1437                 pxIPHeader->usIdentification       = FreeRTOS_htons( usPacketIdentifier );
1438                 usPacketIdentifier++;
1439                 pxUDPHeader->usLength                      = FreeRTOS_htons( lNetLength + ipSIZE_OF_UDP_HEADER );
1440                 vFlip_16( pxUDPPacket->xUDPHeader.usSourcePort, pxUDPPacket->xUDPHeader.usDestinationPort );
1441
1442                 /* Important: tell NIC driver how many bytes must be sent */
1443                 pxNetworkBuffer->xDataLength = ( size_t ) ( lNetLength + ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_UDP_HEADER + ipSIZE_OF_ETH_HEADER );
1444
1445                 #if( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 0 )
1446                 {
1447                         /* calculate the IP header checksum */
1448                         pxIPHeader->usHeaderChecksum = 0x00;
1449                         pxIPHeader->usHeaderChecksum = usGenerateChecksum( 0uL, ( uint8_t * ) &( pxIPHeader->ucVersionHeaderLength ), ipSIZE_OF_IPv4_HEADER );
1450                         pxIPHeader->usHeaderChecksum = ~FreeRTOS_htons( pxIPHeader->usHeaderChecksum );
1451
1452                         /* calculate the UDP checksum for outgoing package */
1453                         usGenerateProtocolChecksum( ( uint8_t* ) pxUDPPacket, pxNetworkBuffer->xDataLength, pdTRUE );
1454                 }
1455                 #endif
1456
1457                 /* This function will fill in the eth addresses and send the packet */
1458                 vReturnEthernetFrame( pxNetworkBuffer, pdFALSE );
1459         }
1460
1461 #endif /* ipconfigUSE_NBNS == 1 || ipconfigUSE_LLMNR == 1 */
1462 /*-----------------------------------------------------------*/
1463
1464 #if( ipconfigUSE_DNS_CACHE == 1 )
1465
1466         static void prvProcessDNSCache( const char *pcName,
1467                                                                         uint32_t *pulIP,
1468                                                                         uint32_t ulTTL,
1469                                                                         BaseType_t xLookUp )
1470         {
1471         BaseType_t x;
1472         BaseType_t xFound = pdFALSE;
1473         uint32_t ulCurrentTimeSeconds = ( xTaskGetTickCount() / portTICK_PERIOD_MS ) / 1000;
1474         static BaseType_t xFreeEntry = 0;
1475                 configASSERT(pcName);
1476
1477                 /* For each entry in the DNS cache table. */
1478                 for( x = 0; x < ipconfigDNS_CACHE_ENTRIES; x++ )
1479                 {
1480                         if( xDNSCache[ x ].pcName[ 0 ] == 0 )
1481                         {
1482                                 continue;
1483                         }
1484
1485                         if( 0 == strcmp( xDNSCache[ x ].pcName, pcName ) )
1486                         {
1487                                 /* Is this function called for a lookup or to add/update an IP address? */
1488                                 if( xLookUp != pdFALSE )
1489                                 {
1490                                         /* Confirm that the record is still fresh. */
1491                                         if( ulCurrentTimeSeconds < ( xDNSCache[ x ].ulTimeWhenAddedInSeconds + FreeRTOS_ntohl( xDNSCache[ x ].ulTTL ) ) )
1492                                         {
1493                                                 *pulIP = xDNSCache[ x ].ulIPAddress;
1494                                         }
1495                                         else
1496                                         {
1497                                                 /* Age out the old cached record. */
1498                                                 xDNSCache[ x ].pcName[ 0 ] = 0;
1499                                         }
1500                                 }
1501                                 else
1502                                 {
1503                                         xDNSCache[ x ].ulIPAddress = *pulIP;
1504                                         xDNSCache[ x ].ulTTL = ulTTL;
1505                                         xDNSCache[ x ].ulTimeWhenAddedInSeconds = ulCurrentTimeSeconds;
1506                                 }
1507
1508                                 xFound = pdTRUE;
1509                                 break;
1510                         }
1511                 }
1512
1513                 if( xFound == pdFALSE )
1514                 {
1515                         if( xLookUp != pdFALSE )
1516                         {
1517                                 *pulIP = 0;
1518                         }
1519                         else
1520                         {
1521                                 /* Add or update the item. */
1522                                 if( strlen( pcName ) < ipconfigDNS_CACHE_NAME_LENGTH )
1523                                 {
1524                                         strcpy( xDNSCache[ xFreeEntry ].pcName, pcName );
1525
1526                                         xDNSCache[ xFreeEntry ].ulIPAddress = *pulIP;
1527                                         xDNSCache[ xFreeEntry ].ulTTL = ulTTL;
1528                                         xDNSCache[ xFreeEntry ].ulTimeWhenAddedInSeconds = ulCurrentTimeSeconds;
1529
1530                                         xFreeEntry++;
1531
1532                                         if( xFreeEntry == ipconfigDNS_CACHE_ENTRIES )
1533                                         {
1534                                                 xFreeEntry = 0;
1535                                         }
1536                                 }
1537                         }
1538                 }
1539
1540                 if( ( xLookUp == 0 ) || ( *pulIP != 0 ) )
1541                 {
1542                         FreeRTOS_debug_printf( ( "prvProcessDNSCache: %s: '%s' @ %lxip\n", xLookUp ? "look-up" : "add", pcName, FreeRTOS_ntohl( *pulIP ) ) );
1543                 }
1544         }
1545
1546 #endif /* ipconfigUSE_DNS_CACHE */
1547
1548 #endif /* ipconfigUSE_DNS != 0 */
1549
1550 /*-----------------------------------------------------------*/
1551
1552 /* Provide access to private members for testing. */
1553 #ifdef AMAZON_FREERTOS_ENABLE_UNIT_TESTS
1554         #include "iot_freertos_tcp_test_access_dns_define.h"
1555 #endif
1556