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