2 * FreeRTOS+TCP V2.2.0
\r
3 * Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
\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
12 * The above copyright notice and this permission notice shall be included in all
\r
13 * copies or substantial portions of the Software.
\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
22 * http://aws.amazon.com/freertos
\r
23 * http://www.FreeRTOS.org
\r
26 /* Standard includes. */
\r
29 /* FreeRTOS includes. */
\r
30 #include "FreeRTOS.h"
\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
46 /* Exclude the entire file if DNS is not enabled. */
\r
47 #if( ipconfigUSE_DNS != 0 )
\r
49 #if( ipconfigBYTE_ORDER == pdFREERTOS_LITTLE_ENDIAN )
\r
50 #define dnsDNS_PORT 0x3500u
\r
51 #define dnsONE_QUESTION 0x0100u
\r
52 #define dnsOUTGOING_FLAGS 0x0001u /* Standard query. */
\r
53 #define dnsRX_FLAGS_MASK 0x0f80u /* The bits of interest in the flags field of incoming DNS messages. */
\r
54 #define dnsEXPECTED_RX_FLAGS 0x0080u /* Should be a response, without any errors. */
\r
56 #define dnsDNS_PORT 0x0035u
\r
57 #define dnsONE_QUESTION 0x0001u
\r
58 #define dnsOUTGOING_FLAGS 0x0100u /* Standard query. */
\r
59 #define dnsRX_FLAGS_MASK 0x800fu /* The bits of interest in the flags field of incoming DNS messages. */
\r
60 #define dnsEXPECTED_RX_FLAGS 0x8000u /* Should be a response, without any errors. */
\r
62 #endif /* ipconfigBYTE_ORDER */
\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
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
75 #define dnsNBNS_FLAGS_RESPONSE 0x8000u
\r
76 #define dnsNBNS_FLAGS_OPCODE_MASK 0x7800u
\r
77 #define dnsNBNS_FLAGS_OPCODE_QUERY 0x0000u
\r
78 #define dnsNBNS_FLAGS_OPCODE_REGISTRATION 0x2800u
\r
81 #define dnsTYPE_A_HOST 0x01u
\r
82 #define dnsCLASS_IN 0x01u
\r
84 /* LLMNR constants. */
\r
85 #define dnsLLMNR_TTL_VALUE 300000uL
\r
86 #define dnsLLMNR_FLAGS_IS_REPONSE 0x8000u
\r
88 /* NBNS constants. */
\r
89 #define dnsNBNS_TTL_VALUE 3600uL /* 1 hour valid */
\r
90 #define dnsNBNS_TYPE_NET_BIOS 0x0020u
\r
91 #define dnsNBNS_CLASS_IN 0x01u
\r
92 #define dnsNBNS_NAME_FLAGS 0x6000u
\r
93 #define dnsNBNS_ENCODED_NAME_LENGTH 32
\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 ( 0x8500u )
\r
99 /* Flag DNS parsing errors in situations where an IPv4 address is the return
\r
101 #define dnsPARSE_ERROR 0uL
\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
107 static Socket_t prvCreateDNSSocket( void );
\r
110 * Create the DNS message in the zero copy buffer passed in the first parameter.
\r
112 static size_t prvCreateDNSMessage( uint8_t *pucUDPPayloadBuffer,
\r
113 const char *pcHostName,
\r
114 TickType_t uxIdentifier );
\r
117 * Simple routine that jumps over the NAME field of a resource record.
\r
119 static uint8_t * prvSkipNameField( uint8_t *pucByte,
\r
120 size_t uxSourceLen );
\r
123 * Process a response packet from a DNS server.
\r
124 * The parameter 'xExpected' indicates whether the identifier in the reply
\r
125 * was expected, and thus if the DNS cache may be updated with the reply.
\r
127 static uint32_t prvParseDNSReply( uint8_t *pucUDPPayloadBuffer,
\r
128 size_t uxBufferLength,
\r
129 BaseType_t xExpected );
\r
132 * Prepare and send a message to a DNS server. 'uxReadTimeOut_ticks' will be passed as
\r
133 * zero, in case the user has supplied a call-back function.
\r
135 static uint32_t prvGetHostByName( const char *pcHostName,
\r
136 TickType_t uxIdentifier,
\r
137 TickType_t uxReadTimeOut_ticks );
\r
140 * The NBNS and the LLMNR protocol share this reply function.
\r
142 #if( ( ipconfigUSE_NBNS == 1 ) || ( ipconfigUSE_LLMNR == 1 ) )
\r
143 static void prvReplyDNSMessage( NetworkBufferDescriptor_t *pxNetworkBuffer,
\r
144 BaseType_t lNetLength );
\r
147 #if( ipconfigUSE_NBNS == 1 )
\r
148 static portINLINE void prvTreatNBNS( uint8_t *pucUDPPayloadBuffer,
\r
149 size_t uxBufferLength,
\r
150 uint32_t ulIPAddress );
\r
151 #endif /* ipconfigUSE_NBNS */
\r
154 #if( ipconfigUSE_DNS_CACHE == 1 ) || ( ipconfigDNS_USE_CALLBACKS == 1 )
\r
155 static uint8_t * prvReadNameField( uint8_t *pucByte,
\r
156 size_t uxSourceLen,
\r
159 #endif /* ipconfigUSE_DNS_CACHE || ipconfigDNS_USE_CALLBACKS */
\r
161 #if( ipconfigUSE_DNS_CACHE == 1 )
\r
162 static void prvProcessDNSCache( const char *pcName,
\r
165 BaseType_t xLookUp );
\r
167 typedef struct xDNS_CACHE_TABLE_ROW
\r
169 uint32_t ulIPAddress; /* The IP address of an ARP cache entry. */
\r
170 char pcName[ ipconfigDNS_CACHE_NAME_LENGTH ]; /* The name of the host */
\r
171 uint32_t ulTTL; /* Time-to-Live (in seconds) from the DNS server. */
\r
172 uint32_t ulTimeWhenAddedInSeconds;
\r
175 static DNSCacheRow_t xDNSCache[ ipconfigDNS_CACHE_ENTRIES ];
\r
177 void FreeRTOS_dnsclear()
\r
179 memset( xDNSCache, 0x0, sizeof( xDNSCache ) );
\r
181 #endif /* ipconfigUSE_DNS_CACHE == 1 */
\r
183 #if( ipconfigUSE_LLMNR == 1 )
\r
184 const MACAddress_t xLLMNR_MacAdress = { { 0x01, 0x00, 0x5e, 0x00, 0x00, 0xfc } };
\r
185 #endif /* ipconfigUSE_LLMNR == 1 */
\r
187 /*-----------------------------------------------------------*/
\r
189 #include "pack_struct_start.h"
\r
192 uint16_t usIdentifier;
\r
194 uint16_t usQuestions;
\r
195 uint16_t usAnswers;
\r
196 uint16_t usAuthorityRRs;
\r
197 uint16_t usAdditionalRRs;
\r
199 #include "pack_struct_end.h"
\r
200 typedef struct xDNSMessage DNSMessage_t;
\r
202 /* A DNS query consists of a header, as described in 'struct xDNSMessage'
\r
203 It is followed by 1 or more queries, each one consisting of a name and a tail,
\r
204 with two fields: type and class
\r
206 #include "pack_struct_start.h"
\r
212 #include "pack_struct_end.h"
\r
213 typedef struct xDNSTail DNSTail_t;
\r
215 /* DNS answer record header. */
\r
216 #include "pack_struct_start.h"
\r
217 struct xDNSAnswerRecord
\r
222 uint16_t usDataLength;
\r
224 #include "pack_struct_end.h"
\r
225 typedef struct xDNSAnswerRecord DNSAnswerRecord_t;
\r
227 #if( ipconfigUSE_LLMNR == 1 )
\r
229 #include "pack_struct_start.h"
\r
230 struct xLLMNRAnswer
\r
232 uint8_t ucNameCode;
\r
233 uint8_t ucNameOffset; /* The name is not repeated in the answer, only the offset is given with "0xc0 <offs>" */
\r
237 uint16_t usDataLength;
\r
238 uint32_t ulIPAddress;
\r
240 #include "pack_struct_end.h"
\r
241 typedef struct xLLMNRAnswer LLMNRAnswer_t;
\r
243 #endif /* ipconfigUSE_LLMNR == 1 */
\r
245 #if( ipconfigUSE_NBNS == 1 )
\r
247 #include "pack_struct_start.h"
\r
248 struct xNBNSRequest
\r
250 uint16_t usRequestId;
\r
252 uint16_t ulRequestCount;
\r
253 uint16_t usAnswerRSS;
\r
254 uint16_t usAuthRSS;
\r
255 uint16_t usAdditionalRSS;
\r
256 uint8_t ucNameSpace;
\r
257 uint8_t ucName[ dnsNBNS_ENCODED_NAME_LENGTH ];
\r
258 uint8_t ucNameZero;
\r
262 #include "pack_struct_end.h"
\r
263 typedef struct xNBNSRequest NBNSRequest_t;
\r
265 #include "pack_struct_start.h"
\r
271 uint16_t usDataLength;
\r
272 uint16_t usNbFlags; /* NetBIOS flags 0x6000 : IP-address, big-endian */
\r
273 uint32_t ulIPAddress;
\r
275 #include "pack_struct_end.h"
\r
276 typedef struct xNBNSAnswer NBNSAnswer_t;
\r
278 #endif /* ipconfigUSE_NBNS == 1 */
\r
280 /*-----------------------------------------------------------*/
\r
282 #if( ipconfigUSE_DNS_CACHE == 1 )
\r
283 uint32_t FreeRTOS_dnslookup( const char *pcHostName )
\r
285 uint32_t ulIPAddress = 0uL;
\r
287 prvProcessDNSCache( pcHostName, &ulIPAddress, 0, pdTRUE );
\r
288 return ulIPAddress;
\r
290 #endif /* ipconfigUSE_DNS_CACHE == 1 */
\r
291 /*-----------------------------------------------------------*/
\r
293 #if( ipconfigDNS_USE_CALLBACKS == 1 )
\r
295 typedef struct xDNS_Callback
\r
297 TickType_t uxRemaningTime; /* Timeout in ms */
\r
298 FOnDNSEvent pCallbackFunction; /* Function to be called when the address has been found or when a timeout has beeen reached */
\r
299 TimeOut_t uxTimeoutState;
\r
301 struct xLIST_ITEM xListItem;
\r
305 static List_t xCallbackList;
\r
307 /* Define FreeRTOS_gethostbyname() as a normal blocking call. */
\r
308 uint32_t FreeRTOS_gethostbyname( const char *pcHostName )
\r
310 return FreeRTOS_gethostbyname_a( pcHostName, ( FOnDNSEvent ) NULL, ( void * ) NULL, 0 );
\r
312 /*-----------------------------------------------------------*/
\r
314 /* Initialise the list of call-back structures. */
\r
315 void vDNSInitialise( void );
\r
316 void vDNSInitialise( void )
\r
318 vListInitialise( &xCallbackList );
\r
320 /*-----------------------------------------------------------*/
\r
322 /* Iterate through the list of call-back structures and remove
\r
323 old entries which have reached a timeout.
\r
324 As soon as the list hase become empty, the DNS timer will be stopped
\r
325 In case pvSearchID is supplied, the user wants to cancel a DNS request
\r
327 void vDNSCheckCallBack( void *pvSearchID );
\r
328 void vDNSCheckCallBack( void *pvSearchID )
\r
330 const ListItem_t *pxIterator;
\r
331 const MiniListItem_t * xEnd = ( const MiniListItem_t * ) listGET_END_MARKER( &xCallbackList );
\r
335 for( pxIterator = ( const ListItem_t * ) listGET_NEXT( xEnd );
\r
336 pxIterator != ( const ListItem_t * ) xEnd;
\r
339 DNSCallback_t *pxCallback = ( DNSCallback_t * ) listGET_LIST_ITEM_OWNER( pxIterator );
\r
341 /* Move to the next item because we might remove this item */
\r
342 pxIterator = ( const ListItem_t * ) listGET_NEXT( pxIterator );
\r
344 if( ( pvSearchID != NULL ) && ( pvSearchID == pxCallback->pvSearchID ) )
\r
346 uxListRemove( &pxCallback->xListItem );
\r
347 vPortFree( pxCallback );
\r
349 else if( xTaskCheckForTimeOut( &pxCallback->uxTimeoutState, &pxCallback->uxRemaningTime ) != pdFALSE )
\r
351 pxCallback->pCallbackFunction( pxCallback->pcName, pxCallback->pvSearchID, 0 );
\r
352 uxListRemove( &pxCallback->xListItem );
\r
353 vPortFree( ( void * ) pxCallback );
\r
359 if( listLIST_IS_EMPTY( &xCallbackList ) )
\r
361 vIPSetDnsTimerEnableState( pdFALSE );
\r
364 /*-----------------------------------------------------------*/
\r
366 void FreeRTOS_gethostbyname_cancel( void *pvSearchID )
\r
368 /* _HT_ Should better become a new API call to have the IP-task remove the callback */
\r
369 vDNSCheckCallBack( pvSearchID );
\r
371 /*-----------------------------------------------------------*/
\r
373 /* FreeRTOS_gethostbyname_a() was called along with callback parameters.
\r
374 Store them in a list for later reference. */
\r
375 static void vDNSSetCallBack( const char *pcHostName,
\r
377 FOnDNSEvent pCallbackFunction,
\r
378 TickType_t uxTimeout,
\r
379 TickType_t uxIdentifier );
\r
380 static void vDNSSetCallBack( const char *pcHostName,
\r
382 FOnDNSEvent pCallbackFunction,
\r
383 TickType_t uxTimeout,
\r
384 TickType_t uxIdentifier )
\r
386 size_t lLength = strlen( pcHostName );
\r
387 DNSCallback_t *pxCallback = ( DNSCallback_t * ) pvPortMalloc( sizeof( *pxCallback ) + lLength );
\r
389 /* Translate from ms to number of clock ticks. */
\r
390 uxTimeout /= portTICK_PERIOD_MS;
\r
392 if( pxCallback != NULL )
\r
394 if( listLIST_IS_EMPTY( &xCallbackList ) )
\r
396 /* This is the first one, start the DNS timer to check for timeouts */
\r
397 vIPReloadDNSTimer( FreeRTOS_min_uint32( 1000U, uxTimeout ) );
\r
400 strcpy( pxCallback->pcName, pcHostName );
\r
401 pxCallback->pCallbackFunction = pCallbackFunction;
\r
402 pxCallback->pvSearchID = pvSearchID;
\r
403 pxCallback->uxRemaningTime = uxTimeout;
\r
404 vTaskSetTimeOutState( &pxCallback->uxTimeoutState );
\r
405 listSET_LIST_ITEM_OWNER( &( pxCallback->xListItem ), ( void * ) pxCallback );
\r
406 listSET_LIST_ITEM_VALUE( &( pxCallback->xListItem ), uxIdentifier );
\r
409 vListInsertEnd( &xCallbackList, &pxCallback->xListItem );
\r
414 /*-----------------------------------------------------------*/
\r
416 /* A DNS reply was received, see if there is any matching entry and
\r
417 call the handler. Returns pdTRUE if uxIdentifier was recognised. */
\r
418 static BaseType_t xDNSDoCallback( TickType_t uxIdentifier,
\r
419 const char *pcName,
\r
420 uint32_t ulIPAddress );
\r
421 static BaseType_t xDNSDoCallback( TickType_t uxIdentifier,
\r
422 const char *pcName,
\r
423 uint32_t ulIPAddress )
\r
425 BaseType_t xResult = pdFALSE;
\r
426 const ListItem_t *pxIterator;
\r
427 const MiniListItem_t * xEnd = ( const MiniListItem_t * ) listGET_END_MARKER( &xCallbackList );
\r
431 for( pxIterator = ( const ListItem_t * ) listGET_NEXT( xEnd );
\r
432 pxIterator != ( const ListItem_t * ) xEnd;
\r
433 pxIterator = ( const ListItem_t * ) listGET_NEXT( pxIterator ) )
\r
435 /* The cast will take away the 'configLIST_VOLATILE' */
\r
436 if( uxIdentifier == ( TickType_t ) listGET_LIST_ITEM_VALUE( pxIterator ) )
\r
438 DNSCallback_t *pxCallback = ( DNSCallback_t * ) listGET_LIST_ITEM_OWNER( pxIterator );
\r
440 pxCallback->pCallbackFunction( pcName, pxCallback->pvSearchID, ulIPAddress );
\r
441 uxListRemove( &pxCallback->xListItem );
\r
442 vPortFree( pxCallback );
\r
444 if( listLIST_IS_EMPTY( &xCallbackList ) )
\r
446 /* The list of outstanding requests is empty. No need for periodic polling. */
\r
447 vIPSetDnsTimerEnableState( pdFALSE );
\r
459 #endif /* ipconfigDNS_USE_CALLBACKS == 1 */
\r
460 /*-----------------------------------------------------------*/
\r
462 #if( ipconfigDNS_USE_CALLBACKS == 0 )
\r
463 uint32_t FreeRTOS_gethostbyname( const char *pcHostName )
\r
465 uint32_t FreeRTOS_gethostbyname_a( const char *pcHostName,
\r
466 FOnDNSEvent pCallback,
\r
468 TickType_t uxTimeout )
\r
471 uint32_t ulIPAddress = 0uL;
\r
472 TickType_t uxReadTimeOut_ticks = ipconfigDNS_RECEIVE_BLOCK_TIME_TICKS;
\r
473 TickType_t uxIdentifier = 0u;
\r
474 BaseType_t xHasRandom = pdFALSE;
\r
476 if( pcHostName != NULL )
\r
478 /* If the supplied hostname is IP address, convert it to uint32_t
\r
480 #if( ipconfigINCLUDE_FULL_INET_ADDR == 1 )
\r
482 ulIPAddress = FreeRTOS_inet_addr( pcHostName );
\r
484 #endif /* ipconfigINCLUDE_FULL_INET_ADDR == 1 */
\r
486 /* If a DNS cache is used then check the cache before issuing another DNS
\r
488 #if( ipconfigUSE_DNS_CACHE == 1 )
\r
490 if( ulIPAddress == 0uL )
\r
492 ulIPAddress = FreeRTOS_dnslookup( pcHostName );
\r
494 if( ulIPAddress != 0 )
\r
496 FreeRTOS_debug_printf( ( "FreeRTOS_gethostbyname: found '%s' in cache: %lxip\n", pcHostName, ulIPAddress ) );
\r
500 /* prvGetHostByName will be called to start a DNS lookup. */
\r
504 #endif /* ipconfigUSE_DNS_CACHE == 1 */
\r
506 /* Generate a unique identifier. */
\r
507 if( ulIPAddress == 0uL )
\r
511 xHasRandom = xApplicationGetRandomNumber( &( ulNumber ) );
\r
512 /* DNS identifiers are 16-bit. */
\r
513 uxIdentifier = ( TickType_t ) ( ulNumber & 0xffffu );
\r
514 /* ipconfigRAND32() may not return a non-zero value. */
\r
517 #if( ipconfigDNS_USE_CALLBACKS == 1 )
\r
519 if( pCallback != NULL )
\r
521 if( ulIPAddress == 0uL )
\r
523 /* The user has provided a callback function, so do not block on recvfrom() */
\r
524 if( xHasRandom != pdFALSE )
\r
526 uxReadTimeOut_ticks = 0u;
\r
527 vDNSSetCallBack( pcHostName, pvSearchID, pCallback, uxTimeout, uxIdentifier );
\r
532 /* The IP address is known, do the call-back now. */
\r
533 pCallback( pcHostName, pvSearchID, ulIPAddress );
\r
537 #endif /* if ( ipconfigDNS_USE_CALLBACKS == 1 ) */
\r
539 if( ( ulIPAddress == 0uL ) && ( xHasRandom != pdFALSE ) )
\r
541 ulIPAddress = prvGetHostByName( pcHostName, uxIdentifier, uxReadTimeOut_ticks );
\r
544 return ulIPAddress;
\r
546 /*-----------------------------------------------------------*/
\r
548 static uint32_t prvGetHostByName( const char *pcHostName,
\r
549 TickType_t uxIdentifier,
\r
550 TickType_t uxReadTimeOut_ticks )
\r
552 struct freertos_sockaddr xAddress;
\r
553 Socket_t xDNSSocket;
\r
554 uint32_t ulIPAddress = 0uL;
\r
555 uint8_t *pucUDPPayloadBuffer;
\r
556 uint32_t ulAddressLength = sizeof( struct freertos_sockaddr );
\r
557 BaseType_t xAttempt;
\r
559 size_t uxPayloadLength, uxExpectedPayloadLength;
\r
560 TickType_t uxWriteTimeOut_ticks = ipconfigDNS_SEND_BLOCK_TIME_TICKS;
\r
562 #if( ipconfigUSE_LLMNR == 1 )
\r
563 BaseType_t bHasDot = pdFALSE;
\r
564 #endif /* ipconfigUSE_LLMNR == 1 */
\r
566 /* If LLMNR is being used then determine if the host name includes a '.' -
\r
567 if not then LLMNR can be used as the lookup method. */
\r
568 #if( ipconfigUSE_LLMNR == 1 )
\r
570 const char *pucPtr;
\r
572 for( pucPtr = pcHostName; *pucPtr; pucPtr++ )
\r
574 if( *pucPtr == '.' )
\r
581 #endif /* ipconfigUSE_LLMNR == 1 */
\r
583 /* Two is added at the end for the count of characters in the first
\r
584 subdomain part and the string end byte. */
\r
585 uxExpectedPayloadLength = sizeof( DNSMessage_t ) + strlen( pcHostName ) + sizeof( uint16_t ) + sizeof( uint16_t ) + 2u;
\r
587 xDNSSocket = prvCreateDNSSocket();
\r
589 if( xDNSSocket != NULL )
\r
591 FreeRTOS_setsockopt( xDNSSocket, 0, FREERTOS_SO_SNDTIMEO, ( void * ) &uxWriteTimeOut_ticks, sizeof( TickType_t ) );
\r
592 FreeRTOS_setsockopt( xDNSSocket, 0, FREERTOS_SO_RCVTIMEO, ( void * ) &uxReadTimeOut_ticks, sizeof( TickType_t ) );
\r
594 for( xAttempt = 0; xAttempt < ipconfigDNS_REQUEST_ATTEMPTS; xAttempt++ )
\r
596 /* Get a buffer. This uses a maximum delay, but the delay will be
\r
597 capped to ipconfigUDP_MAX_SEND_BLOCK_TIME_TICKS so the return value
\r
598 still needs to be tested. */
\r
599 pucUDPPayloadBuffer = ( uint8_t * ) FreeRTOS_GetUDPPayloadBuffer( uxExpectedPayloadLength, portMAX_DELAY );
\r
601 if( pucUDPPayloadBuffer != NULL )
\r
603 /* Create the message in the obtained buffer. */
\r
604 uxPayloadLength = prvCreateDNSMessage( pucUDPPayloadBuffer, pcHostName, uxIdentifier );
\r
606 iptraceSENDING_DNS_REQUEST();
\r
608 /* Obtain the DNS server address. */
\r
609 FreeRTOS_GetAddressConfiguration( NULL, NULL, NULL, &ulIPAddress );
\r
611 /* Send the DNS message. */
\r
612 #if( ipconfigUSE_LLMNR == 1 )
\r
613 if( bHasDot == pdFALSE )
\r
615 /* Use LLMNR addressing. */
\r
616 ( ( DNSMessage_t * ) pucUDPPayloadBuffer )->usFlags = 0;
\r
617 xAddress.sin_addr = ipLLMNR_IP_ADDR; /* Is in network byte order. */
\r
618 xAddress.sin_port = FreeRTOS_ntohs( ipLLMNR_PORT );
\r
623 /* Use DNS server. */
\r
624 xAddress.sin_addr = ulIPAddress;
\r
625 xAddress.sin_port = dnsDNS_PORT;
\r
630 if( FreeRTOS_sendto( xDNSSocket, pucUDPPayloadBuffer, uxPayloadLength, FREERTOS_ZERO_COPY, &xAddress, sizeof( xAddress ) ) != 0 )
\r
632 /* Wait for the reply. */
\r
633 lBytes = FreeRTOS_recvfrom( xDNSSocket, &pucUDPPayloadBuffer, 0, FREERTOS_ZERO_COPY, &xAddress, &ulAddressLength );
\r
637 BaseType_t xExpected;
\r
638 DNSMessage_t *pxDNSMessageHeader = ( DNSMessage_t * ) pucUDPPayloadBuffer;
\r
640 /* See if the identifiers match. */
\r
641 if( uxIdentifier == ( TickType_t ) pxDNSMessageHeader->usIdentifier )
\r
643 xExpected = pdTRUE;
\r
647 /* The reply was not expected. */
\r
648 xExpected = pdFALSE;
\r
651 /* The reply was received. Process it. */
\r
652 #if( ipconfigDNS_USE_CALLBACKS == 0 )
\r
653 /* It is useless to analyse the unexpected reply
\r
654 unless asynchronous look-ups are enabled. */
\r
655 if( xExpected != pdFALSE )
\r
656 #endif /* ipconfigDNS_USE_CALLBACKS == 0 */
\r
658 ulIPAddress = prvParseDNSReply( pucUDPPayloadBuffer, ( size_t ) lBytes, xExpected );
\r
661 /* Finished with the buffer. The zero copy interface
\r
662 is being used, so the buffer must be freed by the
\r
664 FreeRTOS_ReleaseUDPPayloadBuffer( ( void * ) pucUDPPayloadBuffer );
\r
666 if( ulIPAddress != 0uL )
\r
675 /* The message was not sent so the stack will not be
\r
676 releasing the zero copy - it must be released here. */
\r
677 FreeRTOS_ReleaseUDPPayloadBuffer( ( void * ) pucUDPPayloadBuffer );
\r
681 if( uxReadTimeOut_ticks == 0u )
\r
683 /* This DNS lookup is asynchronous, using a call-back:
\r
684 send the request only once. */
\r
689 /* Finished with the socket. */
\r
690 FreeRTOS_closesocket( xDNSSocket );
\r
693 return ulIPAddress;
\r
695 /*-----------------------------------------------------------*/
\r
697 static size_t prvCreateDNSMessage( uint8_t *pucUDPPayloadBuffer,
\r
698 const char *pcHostName,
\r
699 TickType_t uxIdentifier )
\r
701 DNSMessage_t *pxDNSMessageHeader;
\r
702 uint8_t *pucStart, *pucByte;
\r
704 static const DNSMessage_t xDefaultPartDNSHeader =
\r
706 0, /* The identifier will be overwritten. */
\r
707 dnsOUTGOING_FLAGS, /* Flags set for standard query. */
\r
708 dnsONE_QUESTION, /* One question is being asked. */
\r
709 0, /* No replies are included. */
\r
710 0, /* No authorities. */
\r
711 0 /* No additional authorities. */
\r
714 /* Copy in the const part of the header. */
\r
715 memcpy( ( void * ) pucUDPPayloadBuffer, ( void * ) &xDefaultPartDNSHeader, sizeof( xDefaultPartDNSHeader ) );
\r
717 /* Write in a unique identifier. */
\r
718 pxDNSMessageHeader = ( DNSMessage_t * ) pucUDPPayloadBuffer;
\r
719 pxDNSMessageHeader->usIdentifier = ( uint16_t ) uxIdentifier;
\r
721 /* Create the resource record at the end of the header. First
\r
722 find the end of the header. */
\r
723 pucStart = pucUDPPayloadBuffer + sizeof( xDefaultPartDNSHeader );
\r
725 /* Leave a gap for the first length bytes. */
\r
726 pucByte = pucStart + 1;
\r
728 /* Copy in the host name. */
\r
729 strcpy( ( char * ) pucByte, pcHostName );
\r
731 /* Mark the end of the string. */
\r
732 pucByte += strlen( pcHostName );
\r
735 /* Walk the string to replace the '.' characters with byte counts.
\r
736 pucStart holds the address of the byte count. Walking the string
\r
737 starts after the byte count position. */
\r
738 pucByte = pucStart;
\r
744 while( ( *pucByte != 0x00 ) && ( *pucByte != '.' ) )
\r
749 /* Fill in the byte count, then move the pucStart pointer up to
\r
750 the found byte position. */
\r
751 *pucStart = ( uint8_t ) ( ( uint32_t ) pucByte - ( uint32_t ) pucStart );
\r
754 pucStart = pucByte;
\r
755 } while( *pucByte != 0x00 );
\r
757 /* Finish off the record. */
\r
759 pxTail = ( DNSTail_t * ) ( pucByte + 1 );
\r
761 vSetField16( pxTail, DNSTail_t, usType, dnsTYPE_A_HOST ); /* Type A: host */
\r
762 vSetField16( pxTail, DNSTail_t, usClass, dnsCLASS_IN ); /* 1: Class IN */
\r
764 /* Return the total size of the generated message, which is the space from
\r
765 the last written byte to the beginning of the buffer. */
\r
766 return ( ( uint32_t ) pucByte - ( uint32_t ) pucUDPPayloadBuffer + 1 ) + sizeof( *pxTail );
\r
768 /*-----------------------------------------------------------*/
\r
770 #if( ipconfigUSE_DNS_CACHE == 1 ) || ( ipconfigDNS_USE_CALLBACKS == 1 )
\r
772 static uint8_t * prvReadNameField( uint8_t *pucByte,
\r
773 size_t uxSourceLen,
\r
777 size_t uxNameLen = 0;
\r
780 if( 0 == uxSourceLen )
\r
785 /* Determine if the name is the fully coded name, or an offset to the name
\r
786 elsewhere in the message. */
\r
787 if( ( *pucByte & dnsNAME_IS_OFFSET ) == dnsNAME_IS_OFFSET )
\r
789 /* Jump over the two byte offset. */
\r
790 if( uxSourceLen > sizeof( uint16_t ) )
\r
792 pucByte += sizeof( uint16_t );
\r
801 /* pucByte points to the full name. Walk over the string. */
\r
802 while( ( NULL != pucByte ) && ( *pucByte != 0x00u ) && ( uxSourceLen > 1u ) )
\r
804 /* If this is not the first time through the loop, then add a
\r
805 separator in the output. */
\r
806 if( ( uxNameLen > 0 ) && ( uxNameLen < ( uxDestLen - 1u ) ) )
\r
808 pcName[ uxNameLen++ ] = '.';
\r
811 /* Process the first/next sub-string. */
\r
812 for( xCount = *( pucByte++ ), uxSourceLen--;
\r
813 xCount-- && uxSourceLen > 1u;
\r
814 pucByte++, uxSourceLen-- )
\r
816 if( uxNameLen < uxDestLen - 1u )
\r
818 pcName[ uxNameLen++ ] = *( ( char * ) pucByte );
\r
822 /* DNS name is too big for the provided buffer. */
\r
829 /* Confirm that a fully formed name was found. */
\r
830 if( NULL != pucByte )
\r
832 if( 0x00 == *pucByte )
\r
836 pcName[ uxNameLen++ ] = '\0';
\r
847 #endif /* ipconfigUSE_DNS_CACHE || ipconfigDNS_USE_CALLBACKS */
\r
848 /*-----------------------------------------------------------*/
\r
850 static uint8_t * prvSkipNameField( uint8_t *pucByte,
\r
851 size_t uxSourceLen )
\r
853 size_t uxChunkLength;
\r
855 if( 0u == uxSourceLen )
\r
860 /* Determine if the name is the fully coded name, or an offset to the name
\r
861 elsewhere in the message. */
\r
862 if( ( *pucByte & dnsNAME_IS_OFFSET ) == dnsNAME_IS_OFFSET )
\r
864 /* Jump over the two byte offset. */
\r
865 if( uxSourceLen > sizeof( uint16_t ) )
\r
867 pucByte += sizeof( uint16_t );
\r
876 /* pucByte points to the full name. Walk over the string. */
\r
877 while( ( *pucByte != 0x00u ) && ( uxSourceLen > 1u ) )
\r
879 uxChunkLength = *pucByte + 1u;
\r
881 if( uxSourceLen > uxChunkLength )
\r
883 uxSourceLen -= uxChunkLength;
\r
884 pucByte += uxChunkLength;
\r
893 /* Confirm that a fully formed name was found. */
\r
894 if( NULL != pucByte )
\r
896 if( 0x00u == *pucByte )
\r
909 /*-----------------------------------------------------------*/
\r
911 /* The function below will only be called :
\r
912 when ipconfigDNS_USE_CALLBACKS == 1
\r
913 when ipconfigUSE_LLMNR == 1
\r
914 for testing purposes, by the module iot_test_freertos_tcp.c
\r
916 uint32_t ulDNSHandlePacket( NetworkBufferDescriptor_t *pxNetworkBuffer )
\r
918 DNSMessage_t *pxDNSMessageHeader;
\r
919 size_t uxPayloadSize;
\r
921 /* Only proceed if the payload length indicated in the header
\r
922 appears to be valid. */
\r
923 if( pxNetworkBuffer->xDataLength >= sizeof( UDPPacket_t ) )
\r
925 uxPayloadSize = pxNetworkBuffer->xDataLength - sizeof( UDPPacket_t );
\r
927 if( uxPayloadSize >= sizeof( DNSMessage_t ) )
\r
929 pxDNSMessageHeader =
\r
930 ( DNSMessage_t * ) ( pxNetworkBuffer->pucEthernetBuffer + sizeof( UDPPacket_t ) );
\r
932 /* The parameter pdFALSE indicates that the reply was not expected. */
\r
933 prvParseDNSReply( ( uint8_t * ) pxDNSMessageHeader,
\r
939 /* The packet was not consumed. */
\r
942 /*-----------------------------------------------------------*/
\r
944 #if( ipconfigUSE_NBNS == 1 )
\r
946 uint32_t ulNBNSHandlePacket( NetworkBufferDescriptor_t * pxNetworkBuffer )
\r
948 UDPPacket_t *pxUDPPacket = ( UDPPacket_t * ) pxNetworkBuffer->pucEthernetBuffer;
\r
949 uint8_t *pucUDPPayloadBuffer = pxNetworkBuffer->pucEthernetBuffer + sizeof( UDPPacket_t );
\r
950 size_t uxPayloadSize = pxNetworkBuffer->xDataLength - sizeof( UDPPacket_t );
\r
952 /* The network buffer data length has already been set to the
\r
953 length of the UDP payload. */
\r
954 prvTreatNBNS( pucUDPPayloadBuffer,
\r
956 pxUDPPacket->xIPHeader.ulSourceIPAddress );
\r
958 /* The packet was not consumed. */
\r
962 #endif /* ipconfigUSE_NBNS */
\r
963 /*-----------------------------------------------------------*/
\r
965 static uint32_t prvParseDNSReply( uint8_t *pucUDPPayloadBuffer,
\r
966 size_t uxBufferLength,
\r
967 BaseType_t xExpected )
\r
969 DNSMessage_t *pxDNSMessageHeader;
\r
970 DNSAnswerRecord_t *pxDNSAnswerRecord;
\r
971 uint32_t ulIPAddress = 0uL;
\r
972 #if( ipconfigUSE_LLMNR == 1 )
\r
973 char *pcRequestedName = NULL;
\r
976 size_t uxSourceBytesRemaining;
\r
977 uint16_t x, usDataLength, usQuestions;
\r
978 BaseType_t xDoStore = xExpected;
\r
979 #if( ipconfigUSE_LLMNR == 1 )
\r
980 uint16_t usType = 0, usClass = 0;
\r
982 #if( ipconfigUSE_DNS_CACHE == 1 ) || ( ipconfigDNS_USE_CALLBACKS == 1 )
\r
983 char pcName[ ipconfigDNS_CACHE_NAME_LENGTH ] = "";
\r
986 /* Ensure that the buffer is of at least minimal DNS message length. */
\r
987 if( uxBufferLength < sizeof( DNSMessage_t ) )
\r
989 return dnsPARSE_ERROR;
\r
992 uxSourceBytesRemaining = uxBufferLength;
\r
994 /* Parse the DNS message header. */
\r
995 pxDNSMessageHeader = ( DNSMessage_t * ) pucUDPPayloadBuffer;
\r
997 /* Introduce a do {} while (0) to allow the use of breaks. */
\r
1000 /* Start at the first byte after the header. */
\r
1001 pucByte = pucUDPPayloadBuffer + sizeof( DNSMessage_t );
\r
1002 uxSourceBytesRemaining -= sizeof( DNSMessage_t );
\r
1004 /* Skip any question records. */
\r
1005 usQuestions = FreeRTOS_ntohs( pxDNSMessageHeader->usQuestions );
\r
1007 for( x = 0; x < usQuestions; x++ )
\r
1009 #if( ipconfigUSE_LLMNR == 1 )
\r
1013 pcRequestedName = ( char * ) pucByte;
\r
1018 #if( ipconfigUSE_DNS_CACHE == 1 ) || ( ipconfigDNS_USE_CALLBACKS == 1 )
\r
1021 pucByte = prvReadNameField( pucByte,
\r
1022 uxSourceBytesRemaining,
\r
1024 sizeof( pcName ) );
\r
1026 /* Check for a malformed response. */
\r
1027 if( NULL == pucByte )
\r
1029 return dnsPARSE_ERROR;
\r
1032 uxSourceBytesRemaining = ( pucUDPPayloadBuffer + uxBufferLength ) - pucByte;
\r
1035 #endif /* ipconfigUSE_DNS_CACHE || ipconfigDNS_USE_CALLBACKS */
\r
1037 /* Skip the variable length pcName field. */
\r
1038 pucByte = prvSkipNameField( pucByte,
\r
1039 uxSourceBytesRemaining );
\r
1041 /* Check for a malformed response. */
\r
1042 if( NULL == pucByte )
\r
1044 return dnsPARSE_ERROR;
\r
1047 uxSourceBytesRemaining = ( size_t )
\r
1048 ( pucUDPPayloadBuffer + uxBufferLength - pucByte );
\r
1051 /* Check the remaining buffer size. */
\r
1052 if( uxSourceBytesRemaining >= sizeof( uint32_t ) )
\r
1054 #if( ipconfigUSE_LLMNR == 1 )
\r
1056 /* usChar2u16 returns value in host endianness. */
\r
1057 usType = usChar2u16( pucByte );
\r
1058 usClass = usChar2u16( pucByte + 2 );
\r
1060 #endif /* ipconfigUSE_LLMNR */
\r
1062 /* Skip the type and class fields. */
\r
1063 pucByte += sizeof( uint32_t );
\r
1064 uxSourceBytesRemaining -= sizeof( uint32_t );
\r
1068 /* Malformed response. */
\r
1069 return dnsPARSE_ERROR;
\r
1073 /* Search through the answer records. */
\r
1074 pxDNSMessageHeader->usAnswers = FreeRTOS_ntohs( pxDNSMessageHeader->usAnswers );
\r
1076 if( ( pxDNSMessageHeader->usFlags & dnsRX_FLAGS_MASK ) == dnsEXPECTED_RX_FLAGS )
\r
1078 for( x = 0; x < pxDNSMessageHeader->usAnswers; x++ )
\r
1080 pucByte = prvSkipNameField( pucByte,
\r
1081 uxSourceBytesRemaining );
\r
1083 /* Check for a malformed response. */
\r
1084 if( NULL == pucByte )
\r
1086 return dnsPARSE_ERROR;
\r
1089 uxSourceBytesRemaining = ( size_t )
\r
1090 ( pucUDPPayloadBuffer + uxBufferLength - pucByte );
\r
1092 /* Is there enough data for an IPv4 A record answer and, if so,
\r
1093 is this an A record? */
\r
1094 if( ( uxSourceBytesRemaining >= ( sizeof( DNSAnswerRecord_t ) + sizeof( uint32_t ) ) ) &&
\r
1095 ( usChar2u16( pucByte ) == dnsTYPE_A_HOST ) )
\r
1097 /* This is the required record type and is of sufficient size. */
\r
1098 pxDNSAnswerRecord = ( DNSAnswerRecord_t * ) pucByte;
\r
1100 /* Sanity check the data length of an IPv4 answer. */
\r
1101 if( FreeRTOS_ntohs( pxDNSAnswerRecord->usDataLength ) == sizeof( uint32_t ) )
\r
1103 /* Copy the IP address out of the record. */
\r
1104 memcpy( &ulIPAddress,
\r
1105 pucByte + sizeof( DNSAnswerRecord_t ),
\r
1106 sizeof( uint32_t ) );
\r
1108 #if( ipconfigDNS_USE_CALLBACKS == 1 )
\r
1110 /* See if any asynchronous call was made to FreeRTOS_gethostbyname_a() */
\r
1111 if( xDNSDoCallback( ( TickType_t ) pxDNSMessageHeader->usIdentifier, pcName, ulIPAddress ) != pdFALSE )
\r
1113 /* This device has requested this DNS look-up.
\r
1114 The result may be stored in the DNS cache. */
\r
1115 xDoStore = pdTRUE;
\r
1118 #endif /* ipconfigDNS_USE_CALLBACKS == 1 */
\r
1119 #if( ipconfigUSE_DNS_CACHE == 1 )
\r
1121 /* The reply will only be stored in the DNS cache when the
\r
1122 request was issued by this device. */
\r
1123 if( xDoStore != pdFALSE )
\r
1125 prvProcessDNSCache( pcName, &ulIPAddress, pxDNSAnswerRecord->ulTTL, pdFALSE );
\r
1128 /* Show what has happened. */
\r
1129 FreeRTOS_printf( ( "DNS[0x%04X]: The answer to '%s' (%xip) will%s be stored\n",
\r
1130 ( unsigned ) pxDNSMessageHeader->usIdentifier,
\r
1132 ( unsigned ) FreeRTOS_ntohl( ulIPAddress ),
\r
1133 ( xDoStore != 0 ) ? "" : " NOT" ) );
\r
1135 #endif /* ipconfigUSE_DNS_CACHE */
\r
1138 pucByte += sizeof( DNSAnswerRecord_t ) + sizeof( uint32_t );
\r
1139 uxSourceBytesRemaining -= ( sizeof( DNSAnswerRecord_t ) + sizeof( uint32_t ) );
\r
1142 else if( uxSourceBytesRemaining >= sizeof( DNSAnswerRecord_t ) )
\r
1144 /* It's not an A record, so skip it. Get the header location
\r
1145 and then jump over the header. */
\r
1146 pxDNSAnswerRecord = ( DNSAnswerRecord_t * ) pucByte;
\r
1147 pucByte += sizeof( DNSAnswerRecord_t );
\r
1148 uxSourceBytesRemaining -= sizeof( DNSAnswerRecord_t );
\r
1150 /* Determine the length of the answer data from the header. */
\r
1151 usDataLength = FreeRTOS_ntohs( pxDNSAnswerRecord->usDataLength );
\r
1153 /* Jump over the answer. */
\r
1154 if( uxSourceBytesRemaining >= usDataLength )
\r
1156 pucByte += usDataLength;
\r
1157 uxSourceBytesRemaining -= usDataLength;
\r
1161 /* Malformed response. */
\r
1162 return dnsPARSE_ERROR;
\r
1168 #if( ipconfigUSE_LLMNR == 1 )
\r
1169 else if( usQuestions && ( usType == dnsTYPE_A_HOST ) && ( usClass == dnsCLASS_IN ) )
\r
1171 /* If this is not a reply to our DNS request, it might an LLMNR
\r
1173 if( xApplicationDNSQueryHook( ( pcRequestedName + 1 ) ) )
\r
1176 NetworkBufferDescriptor_t *pxNewBuffer = NULL;
\r
1177 NetworkBufferDescriptor_t *pxNetworkBuffer = pxUDPPayloadBuffer_to_NetworkBuffer( pucUDPPayloadBuffer );
\r
1178 LLMNRAnswer_t *pxAnswer;
\r
1180 if( ( xBufferAllocFixedSize == pdFALSE ) && ( pxNetworkBuffer != NULL ) )
\r
1182 BaseType_t xDataLength = uxBufferLength + sizeof( UDPHeader_t ) + sizeof( EthernetHeader_t ) + sizeof( IPHeader_t );
\r
1184 /* Set the size of the outgoing packet. */
\r
1185 pxNetworkBuffer->xDataLength = xDataLength;
\r
1186 pxNewBuffer = pxDuplicateNetworkBufferWithDescriptor( pxNetworkBuffer, xDataLength + sizeof( LLMNRAnswer_t ) );
\r
1188 if( pxNewBuffer != NULL )
\r
1190 BaseType_t xOffset1, xOffset2;
\r
1192 xOffset1 = ( BaseType_t ) ( pucByte - pucUDPPayloadBuffer );
\r
1193 xOffset2 = ( BaseType_t ) ( ( ( uint8_t * ) pcRequestedName ) - pucUDPPayloadBuffer );
\r
1195 pxNetworkBuffer = pxNewBuffer;
\r
1196 pucUDPPayloadBuffer = pxNetworkBuffer->pucEthernetBuffer + ipUDP_PAYLOAD_OFFSET_IPv4;
\r
1198 pucByte = pucUDPPayloadBuffer + xOffset1;
\r
1199 pcRequestedName = ( char * ) ( pucUDPPayloadBuffer + xOffset2 );
\r
1200 pxDNSMessageHeader = ( DNSMessage_t * ) pucUDPPayloadBuffer;
\r
1204 /* Just to indicate that the message may not be answered. */
\r
1205 pxNetworkBuffer = NULL;
\r
1209 if( pxNetworkBuffer != NULL )
\r
1211 pxAnswer = ( LLMNRAnswer_t * ) pucByte;
\r
1213 /* We leave 'usIdentifier' and 'usQuestions' untouched */
\r
1214 vSetField16( pxDNSMessageHeader, DNSMessage_t, usFlags, dnsLLMNR_FLAGS_IS_REPONSE ); /* Set the response flag */
\r
1215 vSetField16( pxDNSMessageHeader, DNSMessage_t, usAnswers, 1 ); /* Provide a single answer */
\r
1216 vSetField16( pxDNSMessageHeader, DNSMessage_t, usAuthorityRRs, 0 ); /* No authority */
\r
1217 vSetField16( pxDNSMessageHeader, DNSMessage_t, usAdditionalRRs, 0 ); /* No additional info */
\r
1219 pxAnswer->ucNameCode = dnsNAME_IS_OFFSET;
\r
1220 pxAnswer->ucNameOffset = ( uint8_t ) ( pcRequestedName - ( char * ) pucUDPPayloadBuffer );
\r
1222 vSetField16( pxAnswer, LLMNRAnswer_t, usType, dnsTYPE_A_HOST ); /* Type A: host */
\r
1223 vSetField16( pxAnswer, LLMNRAnswer_t, usClass, dnsCLASS_IN ); /* 1: Class IN */
\r
1224 vSetField32( pxAnswer, LLMNRAnswer_t, ulTTL, dnsLLMNR_TTL_VALUE );
\r
1225 vSetField16( pxAnswer, LLMNRAnswer_t, usDataLength, 4 );
\r
1226 vSetField32( pxAnswer, LLMNRAnswer_t, ulIPAddress, FreeRTOS_ntohl( *ipLOCAL_IP_ADDRESS_POINTER ) );
\r
1228 usLength = ( int16_t ) ( sizeof( *pxAnswer ) + ( size_t ) ( pucByte - pucUDPPayloadBuffer ) );
\r
1230 prvReplyDNSMessage( pxNetworkBuffer, usLength );
\r
1232 if( pxNewBuffer != NULL )
\r
1234 vReleaseNetworkBufferAndDescriptor( pxNewBuffer );
\r
1239 #endif /* ipconfigUSE_LLMNR == 1 */
\r
1242 if( xExpected == pdFALSE )
\r
1244 /* Do not return a valid IP-address in case the reply was not expected. */
\r
1245 ulIPAddress = 0uL;
\r
1248 return ulIPAddress;
\r
1250 /*-----------------------------------------------------------*/
\r
1252 #if( ipconfigUSE_NBNS == 1 )
\r
1254 static void prvTreatNBNS( uint8_t *pucUDPPayloadBuffer,
\r
1255 size_t uxBufferLength,
\r
1256 uint32_t ulIPAddress )
\r
1258 uint16_t usFlags, usType, usClass;
\r
1259 uint8_t *pucSource, *pucTarget;
\r
1261 uint8_t ucNBNSName[ 17 ];
\r
1263 /* Check for minimum buffer size. */
\r
1264 if( uxBufferLength < sizeof( NBNSRequest_t ) )
\r
1269 /* Read the request flags in host endianness. */
\r
1270 usFlags = usChar2u16( pucUDPPayloadBuffer + offsetof( NBNSRequest_t, usFlags ) );
\r
1272 if( ( usFlags & dnsNBNS_FLAGS_OPCODE_MASK ) == dnsNBNS_FLAGS_OPCODE_QUERY )
\r
1274 usType = usChar2u16( pucUDPPayloadBuffer + offsetof( NBNSRequest_t, usType ) );
\r
1275 usClass = usChar2u16( pucUDPPayloadBuffer + offsetof( NBNSRequest_t, usClass ) );
\r
1277 /* Not used for now */
\r
1280 /* For NBNS a name is 16 bytes long, written with capitals only.
\r
1281 Make sure that the copy is terminated with a zero. */
\r
1282 pucTarget = ucNBNSName + sizeof( ucNBNSName ) - 2;
\r
1283 pucTarget[ 1 ] = '\0';
\r
1285 /* Start with decoding the last 2 bytes. */
\r
1286 pucSource = pucUDPPayloadBuffer + ( offsetof( NBNSRequest_t, ucName ) + ( dnsNBNS_ENCODED_NAME_LENGTH - 2 ) );
\r
1290 ucByte = ( uint8_t ) ( ( ( pucSource[ 0 ] - 0x41 ) << 4 ) | ( pucSource[ 1 ] - 0x41 ) );
\r
1292 /* Make sure there are no trailing spaces in the name. */
\r
1293 if( ( ucByte == ' ' ) && ( pucTarget[ 1 ] == '\0' ) )
\r
1298 *pucTarget = ucByte;
\r
1300 if( pucTarget == ucNBNSName )
\r
1309 #if( ipconfigUSE_DNS_CACHE == 1 )
\r
1311 if( ( usFlags & dnsNBNS_FLAGS_RESPONSE ) != 0 )
\r
1313 /* If this is a response from another device,
\r
1314 add the name to the DNS cache */
\r
1315 prvProcessDNSCache( ( char * ) ucNBNSName, &ulIPAddress, 0, pdFALSE );
\r
1320 /* Avoid compiler warnings. */
\r
1321 ( void ) ulIPAddress;
\r
1323 #endif /* ipconfigUSE_DNS_CACHE */
\r
1325 if( ( ( usFlags & dnsNBNS_FLAGS_RESPONSE ) == 0 ) &&
\r
1326 ( usType == dnsNBNS_TYPE_NET_BIOS ) &&
\r
1327 ( xApplicationDNSQueryHook( ( const char * ) ucNBNSName ) != pdFALSE ) )
\r
1329 uint16_t usLength;
\r
1330 DNSMessage_t *pxMessage;
\r
1331 NBNSAnswer_t *pxAnswer;
\r
1333 /* Someone is looking for a device with ucNBNSName,
\r
1334 prepare a positive reply. */
\r
1335 NetworkBufferDescriptor_t *pxNetworkBuffer = pxUDPPayloadBuffer_to_NetworkBuffer( pucUDPPayloadBuffer );
\r
1337 if( ( xBufferAllocFixedSize == pdFALSE ) && ( pxNetworkBuffer != NULL ) )
\r
1339 NetworkBufferDescriptor_t *pxNewBuffer;
\r
1341 /* The field xDataLength was set to the total length of the UDP packet,
\r
1342 i.e. the payload size plus sizeof( UDPPacket_t ). */
\r
1343 pxNewBuffer = pxDuplicateNetworkBufferWithDescriptor( pxNetworkBuffer, pxNetworkBuffer->xDataLength + sizeof( NBNSAnswer_t ) );
\r
1345 if( pxNewBuffer != NULL )
\r
1347 pucUDPPayloadBuffer = pxNewBuffer->pucEthernetBuffer + sizeof( UDPPacket_t );
\r
1348 pxNetworkBuffer = pxNewBuffer;
\r
1352 /* Just prevent that a reply will be sent */
\r
1353 pxNetworkBuffer = NULL;
\r
1357 /* Should not occur: pucUDPPayloadBuffer is part of a xNetworkBufferDescriptor */
\r
1358 if( pxNetworkBuffer != NULL )
\r
1360 pxMessage = ( DNSMessage_t * ) pucUDPPayloadBuffer;
\r
1362 /* As the fields in the structures are not word-aligned, we have to
\r
1363 copy the values byte-by-byte using macro's vSetField16() and vSetField32() */
\r
1364 vSetField16( pxMessage, DNSMessage_t, usFlags, dnsNBNS_QUERY_RESPONSE_FLAGS ); /* 0x8500 */
\r
1365 vSetField16( pxMessage, DNSMessage_t, usQuestions, 0 );
\r
1366 vSetField16( pxMessage, DNSMessage_t, usAnswers, 1 );
\r
1367 vSetField16( pxMessage, DNSMessage_t, usAuthorityRRs, 0 );
\r
1368 vSetField16( pxMessage, DNSMessage_t, usAdditionalRRs, 0 );
\r
1370 pxAnswer = ( NBNSAnswer_t * ) ( pucUDPPayloadBuffer + offsetof( NBNSRequest_t, usType ) );
\r
1372 vSetField16( pxAnswer, NBNSAnswer_t, usType, usType ); /* Type */
\r
1373 vSetField16( pxAnswer, NBNSAnswer_t, usClass, dnsNBNS_CLASS_IN ); /* Class */
\r
1374 vSetField32( pxAnswer, NBNSAnswer_t, ulTTL, dnsNBNS_TTL_VALUE );
\r
1375 vSetField16( pxAnswer, NBNSAnswer_t, usDataLength, 6 ); /* 6 bytes including the length field */
\r
1376 vSetField16( pxAnswer, NBNSAnswer_t, usNbFlags, dnsNBNS_NAME_FLAGS );
\r
1377 vSetField32( pxAnswer, NBNSAnswer_t, ulIPAddress, FreeRTOS_ntohl( *ipLOCAL_IP_ADDRESS_POINTER ) );
\r
1379 usLength = ( uint16_t ) ( offsetof( NBNSRequest_t, usType ) + sizeof( NBNSAnswer_t ) );
\r
1381 prvReplyDNSMessage( pxNetworkBuffer, usLength );
\r
1387 #endif /* ipconfigUSE_NBNS */
\r
1388 /*-----------------------------------------------------------*/
\r
1390 static Socket_t prvCreateDNSSocket( void )
\r
1392 Socket_t xSocket = NULL;
\r
1393 struct freertos_sockaddr xAddress;
\r
1394 BaseType_t xReturn;
\r
1396 /* This must be the first time this function has been called. Create
\r
1398 xSocket = FreeRTOS_socket( FREERTOS_AF_INET, FREERTOS_SOCK_DGRAM, FREERTOS_IPPROTO_UDP );
\r
1400 /* Auto bind the port. */
\r
1401 xAddress.sin_port = 0u;
\r
1402 xReturn = FreeRTOS_bind( xSocket, &xAddress, sizeof( xAddress ) );
\r
1404 /* Check the bind was successful, and clean up if not. */
\r
1405 if( xReturn != 0 )
\r
1407 FreeRTOS_closesocket( xSocket );
\r
1412 /* The send and receive timeouts will be set later on. */
\r
1417 /*-----------------------------------------------------------*/
\r
1419 #if( ( ipconfigUSE_NBNS == 1 ) || ( ipconfigUSE_LLMNR == 1 ) )
\r
1421 static void prvReplyDNSMessage( NetworkBufferDescriptor_t *pxNetworkBuffer,
\r
1422 BaseType_t lNetLength )
\r
1424 UDPPacket_t *pxUDPPacket;
\r
1425 IPHeader_t *pxIPHeader;
\r
1426 UDPHeader_t *pxUDPHeader;
\r
1428 pxUDPPacket = ( UDPPacket_t * ) pxNetworkBuffer->pucEthernetBuffer;
\r
1429 pxIPHeader = &pxUDPPacket->xIPHeader;
\r
1430 pxUDPHeader = &pxUDPPacket->xUDPHeader;
\r
1431 /* HT: started using defines like 'ipSIZE_OF_xxx' */
\r
1432 pxIPHeader->usLength = FreeRTOS_htons( lNetLength + ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_UDP_HEADER );
\r
1433 /* HT:endian: should not be translated, copying from packet to packet */
\r
1434 pxIPHeader->ulDestinationIPAddress = pxIPHeader->ulSourceIPAddress;
\r
1435 pxIPHeader->ulSourceIPAddress = *ipLOCAL_IP_ADDRESS_POINTER;
\r
1436 pxIPHeader->ucTimeToLive = ipconfigUDP_TIME_TO_LIVE;
\r
1437 pxIPHeader->usIdentification = FreeRTOS_htons( usPacketIdentifier );
\r
1438 usPacketIdentifier++;
\r
1439 pxUDPHeader->usLength = FreeRTOS_htons( lNetLength + ipSIZE_OF_UDP_HEADER );
\r
1440 vFlip_16( pxUDPPacket->xUDPHeader.usSourcePort, pxUDPPacket->xUDPHeader.usDestinationPort );
\r
1442 /* Important: tell NIC driver how many bytes must be sent */
\r
1443 pxNetworkBuffer->xDataLength = ( size_t ) ( lNetLength + ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_UDP_HEADER + ipSIZE_OF_ETH_HEADER );
\r
1445 #if( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 0 )
\r
1447 /* calculate the IP header checksum */
\r
1448 pxIPHeader->usHeaderChecksum = 0x00;
\r
1449 pxIPHeader->usHeaderChecksum = usGenerateChecksum( 0uL, ( uint8_t * ) &( pxIPHeader->ucVersionHeaderLength ), ipSIZE_OF_IPv4_HEADER );
\r
1450 pxIPHeader->usHeaderChecksum = ~FreeRTOS_htons( pxIPHeader->usHeaderChecksum );
\r
1452 /* calculate the UDP checksum for outgoing package */
\r
1453 usGenerateProtocolChecksum( ( uint8_t* ) pxUDPPacket, pxNetworkBuffer->xDataLength, pdTRUE );
\r
1457 /* This function will fill in the eth addresses and send the packet */
\r
1458 vReturnEthernetFrame( pxNetworkBuffer, pdFALSE );
\r
1461 #endif /* ipconfigUSE_NBNS == 1 || ipconfigUSE_LLMNR == 1 */
\r
1462 /*-----------------------------------------------------------*/
\r
1464 #if( ipconfigUSE_DNS_CACHE == 1 )
\r
1466 static void prvProcessDNSCache( const char *pcName,
\r
1469 BaseType_t xLookUp )
\r
1472 BaseType_t xFound = pdFALSE;
\r
1473 uint32_t ulCurrentTimeSeconds = ( xTaskGetTickCount() / portTICK_PERIOD_MS ) / 1000;
\r
1474 static BaseType_t xFreeEntry = 0;
\r
1475 configASSERT(pcName);
\r
1477 /* For each entry in the DNS cache table. */
\r
1478 for( x = 0; x < ipconfigDNS_CACHE_ENTRIES; x++ )
\r
1480 if( xDNSCache[ x ].pcName[ 0 ] == 0 )
\r
1485 if( 0 == strcmp( xDNSCache[ x ].pcName, pcName ) )
\r
1487 /* Is this function called for a lookup or to add/update an IP address? */
\r
1488 if( xLookUp != pdFALSE )
\r
1490 /* Confirm that the record is still fresh. */
\r
1491 if( ulCurrentTimeSeconds < ( xDNSCache[ x ].ulTimeWhenAddedInSeconds + FreeRTOS_ntohl( xDNSCache[ x ].ulTTL ) ) )
\r
1493 *pulIP = xDNSCache[ x ].ulIPAddress;
\r
1497 /* Age out the old cached record. */
\r
1498 xDNSCache[ x ].pcName[ 0 ] = 0;
\r
1503 xDNSCache[ x ].ulIPAddress = *pulIP;
\r
1504 xDNSCache[ x ].ulTTL = ulTTL;
\r
1505 xDNSCache[ x ].ulTimeWhenAddedInSeconds = ulCurrentTimeSeconds;
\r
1513 if( xFound == pdFALSE )
\r
1515 if( xLookUp != pdFALSE )
\r
1521 /* Add or update the item. */
\r
1522 if( strlen( pcName ) < ipconfigDNS_CACHE_NAME_LENGTH )
\r
1524 strcpy( xDNSCache[ xFreeEntry ].pcName, pcName );
\r
1526 xDNSCache[ xFreeEntry ].ulIPAddress = *pulIP;
\r
1527 xDNSCache[ xFreeEntry ].ulTTL = ulTTL;
\r
1528 xDNSCache[ xFreeEntry ].ulTimeWhenAddedInSeconds = ulCurrentTimeSeconds;
\r
1532 if( xFreeEntry == ipconfigDNS_CACHE_ENTRIES )
\r
1540 if( ( xLookUp == 0 ) || ( *pulIP != 0 ) )
\r
1542 FreeRTOS_debug_printf( ( "prvProcessDNSCache: %s: '%s' @ %lxip\n", xLookUp ? "look-up" : "add", pcName, FreeRTOS_ntohl( *pulIP ) ) );
\r
1546 #endif /* ipconfigUSE_DNS_CACHE */
\r
1548 #endif /* ipconfigUSE_DNS != 0 */
\r
1550 /*-----------------------------------------------------------*/
\r
1552 /* Provide access to private members for testing. */
\r
1553 #ifdef AMAZON_FREERTOS_ENABLE_UNIT_TESTS
\r
1554 #include "iot_freertos_tcp_test_access_dns_define.h"
\r