2 * FreeRTOS+TCP 191100 experimental
\r
3 * Copyright (C) 2018 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 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
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
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 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
81 #define dnsTYPE_A_HOST 0x01
\r
82 #define dnsCLASS_IN 0x01
\r
84 /* LLMNR constants. */
\r
85 #define dnsLLMNR_TTL_VALUE 300000
\r
86 #define dnsLLMNR_FLAGS_IS_REPONSE 0x8000
\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
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
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, const char *pcHostName, TickType_t xIdentifier );
\r
115 * Simple routine that jumps over the NAME field of a resource record.
\r
117 static uint8_t *prvSkipNameField( uint8_t *pucByte, size_t xSourceLen );
\r
120 * Process a response packet from a DNS server.
\r
122 static uint32_t prvParseDNSReply( uint8_t *pucUDPPayloadBuffer, size_t xBufferLength, TickType_t xIdentifier );
\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
128 static uint32_t prvGetHostByName( const char *pcHostName, TickType_t xIdentifier, TickType_t xReadTimeOut_ms );
\r
131 * The NBNS and the LLMNR protocol share this reply function.
\r
133 #if( ( ipconfigUSE_NBNS == 1 ) || ( ipconfigUSE_LLMNR == 1 ) )
\r
134 static void prvReplyDNSMessage( NetworkBufferDescriptor_t *pxNetworkBuffer, BaseType_t lNetLength );
\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
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
145 typedef struct xDNS_CACHE_TABLE_ROW
\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
153 static DNSCacheRow_t xDNSCache[ ipconfigDNS_CACHE_ENTRIES ];
\r
155 void FreeRTOS_dnsclear()
\r
157 memset( xDNSCache, 0x0, sizeof( xDNSCache ) );
\r
159 #endif /* ipconfigUSE_DNS_CACHE == 1 */
\r
161 #if( ipconfigUSE_LLMNR == 1 )
\r
162 const MACAddress_t xLLMNR_MacAdress = { { 0x01, 0x00, 0x5e, 0x00, 0x00, 0xfc } };
\r
163 #endif /* ipconfigUSE_LLMNR == 1 */
\r
165 /*-----------------------------------------------------------*/
\r
167 #include "pack_struct_start.h"
\r
170 uint16_t usIdentifier;
\r
172 uint16_t usQuestions;
\r
173 uint16_t usAnswers;
\r
174 uint16_t usAuthorityRRs;
\r
175 uint16_t usAdditionalRRs;
\r
177 #include "pack_struct_end.h"
\r
178 typedef struct xDNSMessage DNSMessage_t;
\r
180 /* A DNS query consists of a header, as described in 'struct xDNSMessage'
\r
181 It is followed by 1 or more queries, each one consisting of a name and a tail,
\r
182 with two fields: type and class
\r
184 #include "pack_struct_start.h"
\r
190 #include "pack_struct_end.h"
\r
191 typedef struct xDNSTail DNSTail_t;
\r
193 /* DNS answer record header. */
\r
194 #include "pack_struct_start.h"
\r
195 struct xDNSAnswerRecord
\r
200 uint16_t usDataLength;
\r
202 #include "pack_struct_end.h"
\r
203 typedef struct xDNSAnswerRecord DNSAnswerRecord_t;
\r
205 #if( ipconfigUSE_LLMNR == 1 )
\r
207 #include "pack_struct_start.h"
\r
208 struct xLLMNRAnswer
\r
210 uint8_t ucNameCode;
\r
211 uint8_t ucNameOffset; /* The name is not repeated in the answer, only the offset is given with "0xc0 <offs>" */
\r
215 uint16_t usDataLength;
\r
216 uint32_t ulIPAddress;
\r
218 #include "pack_struct_end.h"
\r
219 typedef struct xLLMNRAnswer LLMNRAnswer_t;
\r
221 #endif /* ipconfigUSE_LLMNR == 1 */
\r
223 #if( ipconfigUSE_NBNS == 1 )
\r
225 #include "pack_struct_start.h"
\r
226 struct xNBNSRequest
\r
228 uint16_t usRequestId;
\r
230 uint16_t ulRequestCount;
\r
231 uint16_t usAnswerRSS;
\r
232 uint16_t usAuthRSS;
\r
233 uint16_t usAdditionalRSS;
\r
234 uint8_t ucNameSpace;
\r
235 uint8_t ucName[ dnsNBNS_ENCODED_NAME_LENGTH ];
\r
236 uint8_t ucNameZero;
\r
240 #include "pack_struct_end.h"
\r
241 typedef struct xNBNSRequest NBNSRequest_t;
\r
243 #include "pack_struct_start.h"
\r
249 uint16_t usDataLength;
\r
250 uint16_t usNbFlags; /* NetBIOS flags 0x6000 : IP-address, big-endian */
\r
251 uint32_t ulIPAddress;
\r
253 #include "pack_struct_end.h"
\r
254 typedef struct xNBNSAnswer NBNSAnswer_t;
\r
256 #endif /* ipconfigUSE_NBNS == 1 */
\r
258 /*-----------------------------------------------------------*/
\r
260 #if( ipconfigUSE_DNS_CACHE == 1 )
\r
261 uint32_t FreeRTOS_dnslookup( const char *pcHostName )
\r
263 uint32_t ulIPAddress = 0UL;
\r
264 prvProcessDNSCache( pcHostName, &ulIPAddress, 0, pdTRUE );
\r
265 return ulIPAddress;
\r
267 #endif /* ipconfigUSE_DNS_CACHE == 1 */
\r
268 /*-----------------------------------------------------------*/
\r
270 #if( ipconfigDNS_USE_CALLBACKS != 0 )
\r
272 typedef struct xDNS_Callback {
\r
273 TickType_t xRemaningTime; /* Timeout in ms */
\r
274 FOnDNSEvent pCallbackFunction; /* Function to be called when the address has been found or when a timeout has beeen reached */
\r
275 TimeOut_t xTimeoutState;
\r
277 struct xLIST_ITEM xListItem;
\r
281 static List_t xCallbackList;
\r
283 /* Define FreeRTOS_gethostbyname() as a normal blocking call. */
\r
284 uint32_t FreeRTOS_gethostbyname( const char *pcHostName )
\r
286 return FreeRTOS_gethostbyname_a( pcHostName, ( FOnDNSEvent ) NULL, ( void* )NULL, 0 );
\r
288 /*-----------------------------------------------------------*/
\r
290 /* Initialise the list of call-back structures. */
\r
291 void vDNSInitialise( void );
\r
292 void vDNSInitialise( void )
\r
294 vListInitialise( &xCallbackList );
\r
296 /*-----------------------------------------------------------*/
\r
298 /* Iterate through the list of call-back structures and remove
\r
299 old entries which have reached a timeout.
\r
300 As soon as the list hase become empty, the DNS timer will be stopped
\r
301 In case pvSearchID is supplied, the user wants to cancel a DNS request
\r
303 void vDNSCheckCallBack( void *pvSearchID );
\r
304 void vDNSCheckCallBack( void *pvSearchID )
\r
306 const ListItem_t *pxIterator;
\r
307 const MiniListItem_t* xEnd = ( const MiniListItem_t* )listGET_END_MARKER( &xCallbackList );
\r
311 for( pxIterator = ( const ListItem_t * ) listGET_NEXT( xEnd );
\r
312 pxIterator != ( const ListItem_t * ) xEnd;
\r
315 DNSCallback_t *pxCallback = ( DNSCallback_t * ) listGET_LIST_ITEM_OWNER( pxIterator );
\r
316 /* Move to the next item because we might remove this item */
\r
317 pxIterator = ( const ListItem_t * ) listGET_NEXT( pxIterator );
\r
318 if( ( pvSearchID != NULL ) && ( pvSearchID == pxCallback->pvSearchID ) )
\r
320 uxListRemove( &pxCallback->xListItem );
\r
321 vPortFree( pxCallback );
\r
323 else if( xTaskCheckForTimeOut( &pxCallback->xTimeoutState, &pxCallback->xRemaningTime ) != pdFALSE )
\r
325 pxCallback->pCallbackFunction( pxCallback->pcName, pxCallback->pvSearchID, 0 );
\r
326 uxListRemove( &pxCallback->xListItem );
\r
327 vPortFree( ( void * ) pxCallback );
\r
333 if( listLIST_IS_EMPTY( &xCallbackList ) )
\r
335 vIPSetDnsTimerEnableState( pdFALSE );
\r
338 /*-----------------------------------------------------------*/
\r
340 void FreeRTOS_gethostbyname_cancel( void *pvSearchID )
\r
342 /* _HT_ Should better become a new API call to have the IP-task remove the callback */
\r
343 vDNSCheckCallBack( pvSearchID );
\r
345 /*-----------------------------------------------------------*/
\r
347 /* FreeRTOS_gethostbyname_a() was called along with callback parameters.
\r
348 Store them in a list for later reference. */
\r
349 static void vDNSSetCallBack( const char *pcHostName, void *pvSearchID, FOnDNSEvent pCallbackFunction, TickType_t xTimeout, TickType_t xIdentifier );
\r
350 static void vDNSSetCallBack( const char *pcHostName, void *pvSearchID, FOnDNSEvent pCallbackFunction, TickType_t xTimeout, TickType_t xIdentifier )
\r
352 size_t lLength = strlen( pcHostName );
\r
353 DNSCallback_t *pxCallback = ( DNSCallback_t * )pvPortMalloc( sizeof( *pxCallback ) + lLength );
\r
355 /* Translate from ms to number of clock ticks. */
\r
356 xTimeout /= portTICK_PERIOD_MS;
\r
357 if( pxCallback != NULL )
\r
359 if( listLIST_IS_EMPTY( &xCallbackList ) )
\r
361 /* This is the first one, start the DNS timer to check for timeouts */
\r
362 vIPReloadDNSTimer( FreeRTOS_min_uint32( 1000U, xTimeout ) );
\r
364 strcpy( pxCallback->pcName, pcHostName );
\r
365 pxCallback->pCallbackFunction = pCallbackFunction;
\r
366 pxCallback->pvSearchID = pvSearchID;
\r
367 pxCallback->xRemaningTime = xTimeout;
\r
368 vTaskSetTimeOutState( &pxCallback->xTimeoutState );
\r
369 listSET_LIST_ITEM_OWNER( &( pxCallback->xListItem ), ( void* ) pxCallback );
\r
370 listSET_LIST_ITEM_VALUE( &( pxCallback->xListItem ), xIdentifier );
\r
373 vListInsertEnd( &xCallbackList, &pxCallback->xListItem );
\r
378 /*-----------------------------------------------------------*/
\r
380 /* A DNS reply was received, see if there is any matching entry and
\r
381 call the handler. */
\r
382 static void vDNSDoCallback( TickType_t xIdentifier, const char *pcName, uint32_t ulIPAddress );
\r
383 static void vDNSDoCallback( TickType_t xIdentifier, const char *pcName, uint32_t ulIPAddress )
\r
385 const ListItem_t *pxIterator;
\r
386 const MiniListItem_t* xEnd = ( const MiniListItem_t* )listGET_END_MARKER( &xCallbackList );
\r
390 for( pxIterator = ( const ListItem_t * ) listGET_NEXT( xEnd );
\r
391 pxIterator != ( const ListItem_t * ) xEnd;
\r
392 pxIterator = ( const ListItem_t * ) listGET_NEXT( pxIterator ) )
\r
394 if( listGET_LIST_ITEM_VALUE( pxIterator ) == xIdentifier )
\r
396 DNSCallback_t *pxCallback = ( DNSCallback_t * ) listGET_LIST_ITEM_OWNER( pxIterator );
\r
397 pxCallback->pCallbackFunction( pcName, pxCallback->pvSearchID, ulIPAddress );
\r
398 uxListRemove( &pxCallback->xListItem );
\r
399 vPortFree( pxCallback );
\r
400 if( listLIST_IS_EMPTY( &xCallbackList ) )
\r
402 vIPSetDnsTimerEnableState( pdFALSE );
\r
411 #endif /* ipconfigDNS_USE_CALLBACKS != 0 */
\r
412 /*-----------------------------------------------------------*/
\r
414 #if( ipconfigDNS_USE_CALLBACKS == 0 )
\r
415 uint32_t FreeRTOS_gethostbyname( const char *pcHostName )
\r
417 uint32_t FreeRTOS_gethostbyname_a( const char *pcHostName, FOnDNSEvent pCallback, void *pvSearchID, TickType_t xTimeout )
\r
420 uint32_t ulIPAddress = 0UL;
\r
421 TickType_t xReadTimeOut_ms = ipconfigSOCK_DEFAULT_RECEIVE_BLOCK_TIME;
\r
422 TickType_t xIdentifier = 0;
\r
424 /* If the supplied hostname is IP address, convert it to uint32_t
\r
426 #if( ipconfigINCLUDE_FULL_INET_ADDR == 1 )
\r
428 ulIPAddress = FreeRTOS_inet_addr( pcHostName );
\r
430 #endif /* ipconfigINCLUDE_FULL_INET_ADDR == 1 */
\r
432 /* If a DNS cache is used then check the cache before issuing another DNS
\r
434 #if( ipconfigUSE_DNS_CACHE == 1 )
\r
436 if( ulIPAddress == 0UL )
\r
438 ulIPAddress = FreeRTOS_dnslookup( pcHostName );
\r
439 if( ulIPAddress != 0 )
\r
441 FreeRTOS_debug_printf( ( "FreeRTOS_gethostbyname: found '%s' in cache: %lxip\n", pcHostName, ulIPAddress ) );
\r
445 /* prvGetHostByName will be called to start a DNS lookup */
\r
449 #endif /* ipconfigUSE_DNS_CACHE == 1 */
\r
451 /* Generate a unique identifier. */
\r
452 if( 0 == ulIPAddress )
\r
454 xIdentifier = ( TickType_t )ipconfigRAND32( );
\r
457 #if( ipconfigDNS_USE_CALLBACKS != 0 )
\r
459 if( pCallback != NULL )
\r
461 if( ulIPAddress == 0UL )
\r
463 /* The user has provided a callback function, so do not block on recvfrom() */
\r
464 if( 0 != xIdentifier )
\r
466 xReadTimeOut_ms = 0;
\r
467 vDNSSetCallBack( pcHostName, pvSearchID, pCallback, xTimeout, ( TickType_t )xIdentifier );
\r
472 /* The IP address is known, do the call-back now. */
\r
473 pCallback( pcHostName, pvSearchID, ulIPAddress );
\r
479 if( ( ulIPAddress == 0UL ) && ( 0 != xIdentifier ) )
\r
481 ulIPAddress = prvGetHostByName( pcHostName, xIdentifier, xReadTimeOut_ms );
\r
484 return ulIPAddress;
\r
486 /*-----------------------------------------------------------*/
\r
488 static uint32_t prvGetHostByName( const char *pcHostName, TickType_t xIdentifier, TickType_t xReadTimeOut_ms )
\r
490 struct freertos_sockaddr xAddress;
\r
491 Socket_t xDNSSocket;
\r
492 uint32_t ulIPAddress = 0UL;
\r
493 uint8_t *pucUDPPayloadBuffer;
\r
494 uint32_t ulAddressLength = sizeof( struct freertos_sockaddr );
\r
495 BaseType_t xAttempt;
\r
497 size_t xPayloadLength, xExpectedPayloadLength;
\r
498 TickType_t xWriteTimeOut_ms = ipconfigSOCK_DEFAULT_SEND_BLOCK_TIME;
\r
500 #if( ipconfigUSE_LLMNR == 1 )
\r
501 BaseType_t bHasDot = pdFALSE;
\r
502 #endif /* ipconfigUSE_LLMNR == 1 */
\r
504 /* If LLMNR is being used then determine if the host name includes a '.' -
\r
505 if not then LLMNR can be used as the lookup method. */
\r
506 #if( ipconfigUSE_LLMNR == 1 )
\r
508 const char *pucPtr;
\r
509 for( pucPtr = pcHostName; *pucPtr; pucPtr++ )
\r
511 if( *pucPtr == '.' )
\r
518 #endif /* ipconfigUSE_LLMNR == 1 */
\r
520 /* Two is added at the end for the count of characters in the first
\r
521 subdomain part and the string end byte. */
\r
522 xExpectedPayloadLength = sizeof( DNSMessage_t ) + strlen( pcHostName ) + sizeof( uint16_t ) + sizeof( uint16_t ) + 2u;
\r
524 xDNSSocket = prvCreateDNSSocket();
\r
526 if( xDNSSocket != NULL )
\r
528 FreeRTOS_setsockopt( xDNSSocket, 0, FREERTOS_SO_SNDTIMEO, ( void * ) &xWriteTimeOut_ms, sizeof( TickType_t ) );
\r
529 FreeRTOS_setsockopt( xDNSSocket, 0, FREERTOS_SO_RCVTIMEO, ( void * ) &xReadTimeOut_ms, sizeof( TickType_t ) );
\r
531 for( xAttempt = 0; xAttempt < ipconfigDNS_REQUEST_ATTEMPTS; xAttempt++ )
\r
533 /* Get a buffer. This uses a maximum delay, but the delay will be
\r
534 capped to ipconfigUDP_MAX_SEND_BLOCK_TIME_TICKS so the return value
\r
535 still needs to be tested. */
\r
536 pucUDPPayloadBuffer = ( uint8_t * ) FreeRTOS_GetUDPPayloadBuffer( xExpectedPayloadLength, portMAX_DELAY );
\r
538 if( pucUDPPayloadBuffer != NULL )
\r
540 /* Create the message in the obtained buffer. */
\r
541 xPayloadLength = prvCreateDNSMessage( pucUDPPayloadBuffer, pcHostName, xIdentifier );
\r
543 iptraceSENDING_DNS_REQUEST();
\r
545 /* Obtain the DNS server address. */
\r
546 FreeRTOS_GetAddressConfiguration( NULL, NULL, NULL, &ulIPAddress );
\r
548 /* Send the DNS message. */
\r
549 #if( ipconfigUSE_LLMNR == 1 )
\r
550 if( bHasDot == pdFALSE )
\r
552 /* Use LLMNR addressing. */
\r
553 ( ( DNSMessage_t * ) pucUDPPayloadBuffer) -> usFlags = 0;
\r
554 xAddress.sin_addr = ipLLMNR_IP_ADDR; /* Is in network byte order. */
\r
555 xAddress.sin_port = FreeRTOS_ntohs( ipLLMNR_PORT );
\r
560 /* Use DNS server. */
\r
561 xAddress.sin_addr = ulIPAddress;
\r
562 xAddress.sin_port = dnsDNS_PORT;
\r
567 if( FreeRTOS_sendto( xDNSSocket, pucUDPPayloadBuffer, xPayloadLength, FREERTOS_ZERO_COPY, &xAddress, sizeof( xAddress ) ) != 0 )
\r
569 /* Wait for the reply. */
\r
570 lBytes = FreeRTOS_recvfrom( xDNSSocket, &pucUDPPayloadBuffer, 0, FREERTOS_ZERO_COPY, &xAddress, &ulAddressLength );
\r
574 /* The reply was received. Process it. */
\r
575 ulIPAddress = prvParseDNSReply( pucUDPPayloadBuffer, lBytes, xIdentifier );
\r
577 /* Finished with the buffer. The zero copy interface
\r
578 is being used, so the buffer must be freed by the
\r
580 FreeRTOS_ReleaseUDPPayloadBuffer( ( void * ) pucUDPPayloadBuffer );
\r
582 if( ulIPAddress != 0UL )
\r
591 /* The message was not sent so the stack will not be
\r
592 releasing the zero copy - it must be released here. */
\r
593 FreeRTOS_ReleaseUDPPayloadBuffer( ( void * ) pucUDPPayloadBuffer );
\r
598 /* Finished with the socket. */
\r
599 FreeRTOS_closesocket( xDNSSocket );
\r
602 return ulIPAddress;
\r
604 /*-----------------------------------------------------------*/
\r
606 static size_t prvCreateDNSMessage( uint8_t *pucUDPPayloadBuffer, const char *pcHostName, TickType_t xIdentifier )
\r
608 DNSMessage_t *pxDNSMessageHeader;
\r
609 uint8_t *pucStart, *pucByte;
\r
611 static const DNSMessage_t xDefaultPartDNSHeader =
\r
613 0, /* The identifier will be overwritten. */
\r
614 dnsOUTGOING_FLAGS, /* Flags set for standard query. */
\r
615 dnsONE_QUESTION, /* One question is being asked. */
\r
616 0, /* No replies are included. */
\r
617 0, /* No authorities. */
\r
618 0 /* No additional authorities. */
\r
621 /* Copy in the const part of the header. */
\r
622 memcpy( ( void * ) pucUDPPayloadBuffer, ( void * ) &xDefaultPartDNSHeader, sizeof( xDefaultPartDNSHeader ) );
\r
624 /* Write in a unique identifier. */
\r
625 pxDNSMessageHeader = ( DNSMessage_t * ) pucUDPPayloadBuffer;
\r
626 pxDNSMessageHeader->usIdentifier = ( uint16_t ) xIdentifier;
\r
628 /* Create the resource record at the end of the header. First
\r
629 find the end of the header. */
\r
630 pucStart = pucUDPPayloadBuffer + sizeof( xDefaultPartDNSHeader );
\r
632 /* Leave a gap for the first length bytes. */
\r
633 pucByte = pucStart + 1;
\r
635 /* Copy in the host name. */
\r
636 strcpy( ( char * ) pucByte, pcHostName );
\r
638 /* Mark the end of the string. */
\r
639 pucByte += strlen( pcHostName );
\r
642 /* Walk the string to replace the '.' characters with byte counts.
\r
643 pucStart holds the address of the byte count. Walking the string
\r
644 starts after the byte count position. */
\r
645 pucByte = pucStart;
\r
651 while( ( *pucByte != 0x00 ) && ( *pucByte != '.' ) )
\r
656 /* Fill in the byte count, then move the pucStart pointer up to
\r
657 the found byte position. */
\r
658 *pucStart = ( uint8_t ) ( ( uint32_t ) pucByte - ( uint32_t ) pucStart );
\r
661 pucStart = pucByte;
\r
663 } while( *pucByte != 0x00 );
\r
665 /* Finish off the record. */
\r
667 pxTail = (DNSTail_t *)( pucByte + 1 );
\r
669 vSetField16( pxTail, DNSTail_t, usType, dnsTYPE_A_HOST ); /* Type A: host */
\r
670 vSetField16( pxTail, DNSTail_t, usClass, dnsCLASS_IN ); /* 1: Class IN */
\r
672 /* Return the total size of the generated message, which is the space from
\r
673 the last written byte to the beginning of the buffer. */
\r
674 return ( ( uint32_t ) pucByte - ( uint32_t ) pucUDPPayloadBuffer + 1 ) + sizeof( *pxTail );
\r
676 /*-----------------------------------------------------------*/
\r
678 #if( ipconfigUSE_DNS_CACHE == 1 )
\r
680 static uint8_t *prvReadNameField( uint8_t *pucByte, size_t xSourceLen, char *pcName, size_t xDestLen )
\r
682 size_t xNameLen = 0;
\r
685 if( 0 == xSourceLen )
\r
690 /* Determine if the name is the fully coded name, or an offset to the name
\r
691 elsewhere in the message. */
\r
692 if( ( *pucByte & dnsNAME_IS_OFFSET ) == dnsNAME_IS_OFFSET )
\r
694 /* Jump over the two byte offset. */
\r
695 if( xSourceLen > sizeof( uint16_t ) )
\r
697 pucByte += sizeof( uint16_t );
\r
706 /* pucByte points to the full name. Walk over the string. */
\r
707 while( ( NULL != pucByte ) && ( *pucByte != 0x00 ) && ( xSourceLen > 1 ) )
\r
709 /* If this is not the first time through the loop, then add a
\r
710 separator in the output. */
\r
711 if( ( xNameLen > 0 ) && ( xNameLen < ( xDestLen - 1 ) ) )
\r
713 pcName[ xNameLen++ ] = '.';
\r
716 /* Process the first/next sub-string. */
\r
717 for( xCount = *(pucByte++), xSourceLen--;
\r
718 xCount-- && xSourceLen > 1;
\r
719 pucByte++, xSourceLen-- )
\r
721 if( xNameLen < xDestLen - 1 )
\r
723 pcName[ xNameLen++ ] = *( ( char * )pucByte );
\r
727 /* DNS name is too big for the provided buffer. */
\r
734 /* Confirm that a fully formed name was found. */
\r
735 if( NULL != pucByte )
\r
737 if( 0x00 == *pucByte )
\r
741 pcName[ xNameLen++ ] = '\0';
\r
752 #endif /* ipconfigUSE_DNS_CACHE == 1 */
\r
753 /*-----------------------------------------------------------*/
\r
755 static uint8_t *prvSkipNameField( uint8_t *pucByte, size_t xSourceLen )
\r
757 size_t xChunkLength;
\r
759 if( 0 == xSourceLen )
\r
764 /* Determine if the name is the fully coded name, or an offset to the name
\r
765 elsewhere in the message. */
\r
766 if( ( *pucByte & dnsNAME_IS_OFFSET ) == dnsNAME_IS_OFFSET )
\r
768 /* Jump over the two byte offset. */
\r
769 if( xSourceLen > sizeof( uint16_t ) )
\r
771 pucByte += sizeof( uint16_t );
\r
780 /* pucByte points to the full name. Walk over the string. */
\r
781 while( ( *pucByte != 0x00 ) && ( xSourceLen > 1 ) )
\r
783 xChunkLength = *pucByte + 1;
\r
785 if( xSourceLen > xChunkLength )
\r
787 xSourceLen -= xChunkLength;
\r
788 pucByte += xChunkLength;
\r
797 /* Confirm that a fully formed name was found. */
\r
798 if( NULL != pucByte )
\r
800 if( 0x00 == *pucByte )
\r
813 /*-----------------------------------------------------------*/
\r
815 uint32_t ulDNSHandlePacket( NetworkBufferDescriptor_t *pxNetworkBuffer )
\r
817 DNSMessage_t *pxDNSMessageHeader;
\r
819 if( pxNetworkBuffer->xDataLength >= sizeof( DNSMessage_t ) )
\r
821 pxDNSMessageHeader =
\r
822 ( DNSMessage_t * )( pxNetworkBuffer->pucEthernetBuffer + sizeof( UDPPacket_t ) );
\r
824 prvParseDNSReply( ( uint8_t * )pxDNSMessageHeader,
\r
825 pxNetworkBuffer->xDataLength,
\r
826 ( uint32_t )pxDNSMessageHeader->usIdentifier );
\r
829 /* The packet was not consumed. */
\r
832 /*-----------------------------------------------------------*/
\r
834 #if( ipconfigUSE_NBNS == 1 )
\r
836 uint32_t ulNBNSHandlePacket (NetworkBufferDescriptor_t *pxNetworkBuffer )
\r
838 UDPPacket_t *pxUDPPacket = ( UDPPacket_t * ) pxNetworkBuffer->pucEthernetBuffer;
\r
839 uint8_t *pucUDPPayloadBuffer = pxNetworkBuffer->pucEthernetBuffer + sizeof( UDPPacket_t );
\r
841 /* The network buffer data length has already been set to the
\r
842 length of the UDP payload. */
\r
843 prvTreatNBNS( pucUDPPayloadBuffer,
\r
844 pxNetworkBuffer->xDataLength,
\r
845 pxUDPPacket->xIPHeader.ulSourceIPAddress );
\r
847 /* The packet was not consumed. */
\r
851 #endif /* ipconfigUSE_NBNS */
\r
852 /*-----------------------------------------------------------*/
\r
854 static uint32_t prvParseDNSReply( uint8_t *pucUDPPayloadBuffer, size_t xBufferLength, TickType_t xIdentifier )
\r
856 DNSMessage_t *pxDNSMessageHeader;
\r
857 DNSAnswerRecord_t *pxDNSAnswerRecord;
\r
858 uint32_t ulIPAddress = 0UL;
\r
859 #if( ipconfigUSE_LLMNR == 1 )
\r
860 char *pcRequestedName = NULL;
\r
863 size_t xSourceBytesRemaining;
\r
864 uint16_t x, usDataLength, usQuestions;
\r
865 #if( ipconfigUSE_LLMNR == 1 )
\r
866 uint16_t usType = 0, usClass = 0;
\r
868 #if( ipconfigUSE_DNS_CACHE == 1 )
\r
869 char pcName[ ipconfigDNS_CACHE_NAME_LENGTH ] = "";
\r
872 /* Ensure that the buffer is of at least minimal DNS message length. */
\r
873 if( xBufferLength < sizeof( DNSMessage_t ) )
\r
875 return dnsPARSE_ERROR;
\r
879 xSourceBytesRemaining = xBufferLength;
\r
882 /* Parse the DNS message header. */
\r
883 pxDNSMessageHeader = ( DNSMessage_t * ) pucUDPPayloadBuffer;
\r
885 if( pxDNSMessageHeader->usIdentifier == ( uint16_t ) xIdentifier )
\r
887 /* Start at the first byte after the header. */
\r
888 pucByte = pucUDPPayloadBuffer + sizeof( DNSMessage_t );
\r
889 xSourceBytesRemaining -= sizeof( DNSMessage_t );
\r
891 /* Skip any question records. */
\r
892 usQuestions = FreeRTOS_ntohs( pxDNSMessageHeader->usQuestions );
\r
893 for( x = 0; x < usQuestions; x++ )
\r
895 #if( ipconfigUSE_LLMNR == 1 )
\r
899 pcRequestedName = ( char * ) pucByte;
\r
904 #if( ipconfigUSE_DNS_CACHE == 1 )
\r
907 pucByte = prvReadNameField( pucByte,
\r
908 xSourceBytesRemaining,
\r
910 sizeof( pcName ) );
\r
912 /* Check for a malformed response. */
\r
913 if( NULL == pucByte )
\r
915 return dnsPARSE_ERROR;
\r
919 xSourceBytesRemaining = ( pucUDPPayloadBuffer + xBufferLength ) - pucByte;
\r
923 #endif /* ipconfigUSE_DNS_CACHE */
\r
925 /* Skip the variable length pcName field. */
\r
926 pucByte = prvSkipNameField( pucByte,
\r
927 xSourceBytesRemaining );
\r
929 /* Check for a malformed response. */
\r
930 if( NULL == pucByte )
\r
932 return dnsPARSE_ERROR;
\r
936 xSourceBytesRemaining = pucUDPPayloadBuffer + xBufferLength - pucByte;
\r
940 /* Check the remaining buffer size. */
\r
941 if( xSourceBytesRemaining >= sizeof( uint32_t ) )
\r
943 #if( ipconfigUSE_LLMNR == 1 )
\r
945 /* usChar2u16 returns value in host endianness */
\r
946 usType = usChar2u16( pucByte );
\r
947 usClass = usChar2u16( pucByte + 2 );
\r
949 #endif /* ipconfigUSE_LLMNR */
\r
951 /* Skip the type and class fields. */
\r
952 pucByte += sizeof( uint32_t );
\r
953 xSourceBytesRemaining -= sizeof( uint32_t );
\r
957 /* Malformed response. */
\r
958 return dnsPARSE_ERROR;
\r
962 /* Search through the answer records. */
\r
963 pxDNSMessageHeader->usAnswers = FreeRTOS_ntohs( pxDNSMessageHeader->usAnswers );
\r
965 if( ( pxDNSMessageHeader->usFlags & dnsRX_FLAGS_MASK ) == dnsEXPECTED_RX_FLAGS )
\r
967 for( x = 0; x < pxDNSMessageHeader->usAnswers; x++ )
\r
969 pucByte = prvSkipNameField( pucByte,
\r
970 xSourceBytesRemaining );
\r
972 /* Check for a malformed response. */
\r
973 if( NULL == pucByte )
\r
975 return dnsPARSE_ERROR;
\r
979 xSourceBytesRemaining = pucUDPPayloadBuffer + xBufferLength - pucByte;
\r
982 /* Is there enough data for an IPv4 A record answer and, if so,
\r
983 is this an A record? */
\r
984 if( xSourceBytesRemaining >= sizeof( DNSAnswerRecord_t ) + sizeof( uint32_t ) &&
\r
985 usChar2u16( pucByte ) == dnsTYPE_A_HOST )
\r
987 /* This is the required record type and is of sufficient size. */
\r
988 pxDNSAnswerRecord = ( DNSAnswerRecord_t * )pucByte;
\r
990 /* Sanity check the data length of an IPv4 answer. */
\r
991 if( FreeRTOS_ntohs( pxDNSAnswerRecord->usDataLength ) == sizeof( uint32_t ) )
\r
993 /* Copy the IP address out of the record. */
\r
994 memcpy( &ulIPAddress,
\r
995 pucByte + sizeof( DNSAnswerRecord_t ),
\r
996 sizeof( uint32_t ) );
\r
998 #if( ipconfigUSE_DNS_CACHE == 1 )
\r
1000 prvProcessDNSCache( pcName, &ulIPAddress, pxDNSAnswerRecord->ulTTL, pdFALSE );
\r
1002 #endif /* ipconfigUSE_DNS_CACHE */
\r
1003 #if( ipconfigDNS_USE_CALLBACKS != 0 )
\r
1005 /* See if any asynchronous call was made to FreeRTOS_gethostbyname_a() */
\r
1006 vDNSDoCallback( ( TickType_t ) pxDNSMessageHeader->usIdentifier, pcName, ulIPAddress );
\r
1008 #endif /* ipconfigDNS_USE_CALLBACKS != 0 */
\r
1011 pucByte += sizeof( DNSAnswerRecord_t ) + sizeof( uint32_t );
\r
1012 xSourceBytesRemaining -= ( sizeof( DNSAnswerRecord_t ) + sizeof( uint32_t ) );
\r
1015 else if( xSourceBytesRemaining >= sizeof( DNSAnswerRecord_t ) )
\r
1017 /* It's not an A record, so skip it. Get the header location
\r
1018 and then jump over the header. */
\r
1019 pxDNSAnswerRecord = ( DNSAnswerRecord_t * )pucByte;
\r
1020 pucByte += sizeof( DNSAnswerRecord_t );
\r
1021 xSourceBytesRemaining -= sizeof( DNSAnswerRecord_t );
\r
1023 /* Determine the length of the answer data from the header. */
\r
1024 usDataLength = FreeRTOS_ntohs( pxDNSAnswerRecord->usDataLength );
\r
1026 /* Jump over the answer. */
\r
1027 if( xSourceBytesRemaining >= usDataLength )
\r
1029 pucByte += usDataLength;
\r
1030 xSourceBytesRemaining -= usDataLength;
\r
1034 /* Malformed response. */
\r
1035 return dnsPARSE_ERROR;
\r
1040 #if( ipconfigUSE_LLMNR == 1 )
\r
1041 else if( usQuestions && ( usType == dnsTYPE_A_HOST ) && ( usClass == dnsCLASS_IN ) )
\r
1043 /* If this is not a reply to our DNS request, it might an LLMNR
\r
1045 if( xApplicationDNSQueryHook ( ( pcRequestedName + 1 ) ) )
\r
1048 NetworkBufferDescriptor_t *pxNewBuffer = NULL;
\r
1049 NetworkBufferDescriptor_t *pxNetworkBuffer = pxUDPPayloadBuffer_to_NetworkBuffer( pucUDPPayloadBuffer );
\r
1050 LLMNRAnswer_t *pxAnswer;
\r
1052 if( ( xBufferAllocFixedSize == pdFALSE ) && ( pxNetworkBuffer != NULL ) )
\r
1054 BaseType_t xDataLength = xBufferLength + sizeof( UDPHeader_t ) + sizeof( EthernetHeader_t ) + sizeof( IPHeader_t );
\r
1056 /* The field xDataLength was set to the length of the UDP payload.
\r
1057 The answer (reply) will be longer than the request, so the packet
\r
1058 must be duplicaed into a bigger buffer */
\r
1059 pxNetworkBuffer->xDataLength = xDataLength;
\r
1060 pxNewBuffer = pxDuplicateNetworkBufferWithDescriptor( pxNetworkBuffer, xDataLength + 16 );
\r
1061 if( pxNewBuffer != NULL )
\r
1063 BaseType_t xOffset1, xOffset2;
\r
1065 xOffset1 = ( BaseType_t ) ( pucByte - pucUDPPayloadBuffer );
\r
1066 xOffset2 = ( BaseType_t ) ( ( ( uint8_t * ) pcRequestedName ) - pucUDPPayloadBuffer );
\r
1068 pxNetworkBuffer = pxNewBuffer;
\r
1069 pucUDPPayloadBuffer = pxNetworkBuffer->pucEthernetBuffer + ipUDP_PAYLOAD_OFFSET_IPv4;
\r
1071 pucByte = pucUDPPayloadBuffer + xOffset1;
\r
1072 pcRequestedName = ( char * ) ( pucUDPPayloadBuffer + xOffset2 );
\r
1073 pxDNSMessageHeader = ( DNSMessage_t * ) pucUDPPayloadBuffer;
\r
1078 /* Just to indicate that the message may not be answered. */
\r
1079 pxNetworkBuffer = NULL;
\r
1082 if( pxNetworkBuffer != NULL )
\r
1084 pxAnswer = (LLMNRAnswer_t *)pucByte;
\r
1086 /* We leave 'usIdentifier' and 'usQuestions' untouched */
\r
1087 vSetField16( pxDNSMessageHeader, DNSMessage_t, usFlags, dnsLLMNR_FLAGS_IS_REPONSE ); /* Set the response flag */
\r
1088 vSetField16( pxDNSMessageHeader, DNSMessage_t, usAnswers, 1 ); /* Provide a single answer */
\r
1089 vSetField16( pxDNSMessageHeader, DNSMessage_t, usAuthorityRRs, 0 ); /* No authority */
\r
1090 vSetField16( pxDNSMessageHeader, DNSMessage_t, usAdditionalRRs, 0 ); /* No additional info */
\r
1092 pxAnswer->ucNameCode = dnsNAME_IS_OFFSET;
\r
1093 pxAnswer->ucNameOffset = ( uint8_t )( pcRequestedName - ( char * ) pucUDPPayloadBuffer );
\r
1095 vSetField16( pxAnswer, LLMNRAnswer_t, usType, dnsTYPE_A_HOST ); /* Type A: host */
\r
1096 vSetField16( pxAnswer, LLMNRAnswer_t, usClass, dnsCLASS_IN ); /* 1: Class IN */
\r
1097 vSetField32( pxAnswer, LLMNRAnswer_t, ulTTL, dnsLLMNR_TTL_VALUE );
\r
1098 vSetField16( pxAnswer, LLMNRAnswer_t, usDataLength, 4 );
\r
1099 vSetField32( pxAnswer, LLMNRAnswer_t, ulIPAddress, FreeRTOS_ntohl( *ipLOCAL_IP_ADDRESS_POINTER ) );
\r
1101 usLength = ( int16_t ) ( sizeof( *pxAnswer ) + ( size_t ) ( pucByte - pucUDPPayloadBuffer ) );
\r
1103 prvReplyDNSMessage( pxNetworkBuffer, usLength );
\r
1105 if( pxNewBuffer != NULL )
\r
1107 vReleaseNetworkBufferAndDescriptor( pxNewBuffer );
\r
1112 #endif /* ipconfigUSE_LLMNR == 1 */
\r
1115 return ulIPAddress;
\r
1117 /*-----------------------------------------------------------*/
\r
1119 #if( ipconfigUSE_NBNS == 1 )
\r
1121 static void prvTreatNBNS( uint8_t *pucUDPPayloadBuffer, size_t xBufferLength, uint32_t ulIPAddress )
\r
1123 uint16_t usFlags, usType, usClass;
\r
1124 uint8_t *pucSource, *pucTarget;
\r
1126 uint8_t ucNBNSName[ 17 ];
\r
1128 /* Check for minimum buffer size. */
\r
1129 if( xBufferLength < sizeof( NBNSRequest_t ) )
\r
1134 /* Read the request flags in host endianness. */
\r
1135 usFlags = usChar2u16( pucUDPPayloadBuffer + offsetof( NBNSRequest_t, usFlags ) );
\r
1137 if( ( usFlags & dnsNBNS_FLAGS_OPCODE_MASK ) == dnsNBNS_FLAGS_OPCODE_QUERY )
\r
1139 usType = usChar2u16( pucUDPPayloadBuffer + offsetof( NBNSRequest_t, usType ) );
\r
1140 usClass = usChar2u16( pucUDPPayloadBuffer + offsetof( NBNSRequest_t, usClass ) );
\r
1142 /* Not used for now */
\r
1144 /* For NBNS a name is 16 bytes long, written with capitals only.
\r
1145 Make sure that the copy is terminated with a zero. */
\r
1146 pucTarget = ucNBNSName + sizeof(ucNBNSName ) - 2;
\r
1147 pucTarget[ 1 ] = '\0';
\r
1149 /* Start with decoding the last 2 bytes. */
\r
1150 pucSource = pucUDPPayloadBuffer + ( offsetof( NBNSRequest_t, ucName ) + ( dnsNBNS_ENCODED_NAME_LENGTH - 2 ) );
\r
1154 ucByte = ( uint8_t ) ( ( ( pucSource[ 0 ] - 0x41 ) << 4 ) | ( pucSource[ 1 ] - 0x41 ) );
\r
1156 /* Make sure there are no trailing spaces in the name. */
\r
1157 if( ( ucByte == ' ' ) && ( pucTarget[ 1 ] == '\0' ) )
\r
1162 *pucTarget = ucByte;
\r
1164 if( pucTarget == ucNBNSName )
\r
1173 #if( ipconfigUSE_DNS_CACHE == 1 )
\r
1175 if( ( usFlags & dnsNBNS_FLAGS_RESPONSE ) != 0 )
\r
1177 /* If this is a response from another device,
\r
1178 add the name to the DNS cache */
\r
1179 prvProcessDNSCache( ( char * ) ucNBNSName, &ulIPAddress, 0, pdFALSE );
\r
1184 /* Avoid compiler warnings. */
\r
1185 ( void ) ulIPAddress;
\r
1187 #endif /* ipconfigUSE_DNS_CACHE */
\r
1189 if( ( ( usFlags & dnsNBNS_FLAGS_RESPONSE ) == 0 ) &&
\r
1190 ( usType == dnsNBNS_TYPE_NET_BIOS ) &&
\r
1191 ( xApplicationDNSQueryHook( ( const char * ) ucNBNSName ) != pdFALSE ) )
\r
1193 uint16_t usLength;
\r
1194 DNSMessage_t *pxMessage;
\r
1195 NBNSAnswer_t *pxAnswer;
\r
1197 /* Someone is looking for a device with ucNBNSName,
\r
1198 prepare a positive reply. */
\r
1199 NetworkBufferDescriptor_t *pxNetworkBuffer = pxUDPPayloadBuffer_to_NetworkBuffer( pucUDPPayloadBuffer );
\r
1201 if( ( xBufferAllocFixedSize == pdFALSE ) && ( pxNetworkBuffer != NULL ) )
\r
1203 NetworkBufferDescriptor_t *pxNewBuffer;
\r
1204 BaseType_t xDataLength = pxNetworkBuffer->xDataLength + sizeof( UDPHeader_t ) +
\r
1205 sizeof( EthernetHeader_t ) + sizeof( IPHeader_t );
\r
1207 /* The field xDataLength was set to the length of the UDP payload.
\r
1208 The answer (reply) will be longer than the request, so the packet
\r
1209 must be duplicated into a bigger buffer */
\r
1210 pxNetworkBuffer->xDataLength = xDataLength;
\r
1211 pxNewBuffer = pxDuplicateNetworkBufferWithDescriptor( pxNetworkBuffer, xDataLength + 16 );
\r
1212 if( pxNewBuffer != NULL )
\r
1214 pucUDPPayloadBuffer = pxNewBuffer->pucEthernetBuffer + sizeof( UDPPacket_t );
\r
1215 pxNetworkBuffer = pxNewBuffer;
\r
1219 /* Just prevent that a reply will be sent */
\r
1220 pxNetworkBuffer = NULL;
\r
1224 /* Should not occur: pucUDPPayloadBuffer is part of a xNetworkBufferDescriptor */
\r
1225 if( pxNetworkBuffer != NULL )
\r
1227 pxMessage = (DNSMessage_t *)pucUDPPayloadBuffer;
\r
1229 /* As the fields in the structures are not word-aligned, we have to
\r
1230 copy the values byte-by-byte using macro's vSetField16() and vSetField32() */
\r
1231 vSetField16( pxMessage, DNSMessage_t, usFlags, dnsNBNS_QUERY_RESPONSE_FLAGS ); /* 0x8500 */
\r
1232 vSetField16( pxMessage, DNSMessage_t, usQuestions, 0 );
\r
1233 vSetField16( pxMessage, DNSMessage_t, usAnswers, 1 );
\r
1234 vSetField16( pxMessage, DNSMessage_t, usAuthorityRRs, 0 );
\r
1235 vSetField16( pxMessage, DNSMessage_t, usAdditionalRRs, 0 );
\r
1237 pxAnswer = (NBNSAnswer_t *)( pucUDPPayloadBuffer + offsetof( NBNSRequest_t, usType ) );
\r
1239 vSetField16( pxAnswer, NBNSAnswer_t, usType, usType ); /* Type */
\r
1240 vSetField16( pxAnswer, NBNSAnswer_t, usClass, dnsNBNS_CLASS_IN ); /* Class */
\r
1241 vSetField32( pxAnswer, NBNSAnswer_t, ulTTL, dnsNBNS_TTL_VALUE );
\r
1242 vSetField16( pxAnswer, NBNSAnswer_t, usDataLength, 6 ); /* 6 bytes including the length field */
\r
1243 vSetField16( pxAnswer, NBNSAnswer_t, usNbFlags, dnsNBNS_NAME_FLAGS );
\r
1244 vSetField32( pxAnswer, NBNSAnswer_t, ulIPAddress, FreeRTOS_ntohl( *ipLOCAL_IP_ADDRESS_POINTER ) );
\r
1246 usLength = ( uint16_t ) ( offsetof( NBNSRequest_t, usType ) + sizeof( NBNSAnswer_t ) );
\r
1248 prvReplyDNSMessage( pxNetworkBuffer, usLength );
\r
1254 #endif /* ipconfigUSE_NBNS */
\r
1255 /*-----------------------------------------------------------*/
\r
1257 static Socket_t prvCreateDNSSocket( void )
\r
1259 Socket_t xSocket = NULL;
\r
1260 struct freertos_sockaddr xAddress;
\r
1261 BaseType_t xReturn;
\r
1262 TickType_t xTimeoutTime = pdMS_TO_TICKS( 200 );
\r
1264 /* This must be the first time this function has been called. Create
\r
1266 xSocket = FreeRTOS_socket( FREERTOS_AF_INET, FREERTOS_SOCK_DGRAM, FREERTOS_IPPROTO_UDP );
\r
1268 /* Auto bind the port. */
\r
1269 xAddress.sin_port = 0u;
\r
1270 xReturn = FreeRTOS_bind( xSocket, &xAddress, sizeof( xAddress ) );
\r
1272 /* Check the bind was successful, and clean up if not. */
\r
1273 if( xReturn != 0 )
\r
1275 FreeRTOS_closesocket( xSocket );
\r
1280 /* Set the send and receive timeouts. */
\r
1281 FreeRTOS_setsockopt( xSocket, 0, FREERTOS_SO_RCVTIMEO, ( void * ) &xTimeoutTime, sizeof( TickType_t ) );
\r
1282 FreeRTOS_setsockopt( xSocket, 0, FREERTOS_SO_SNDTIMEO, ( void * ) &xTimeoutTime, sizeof( TickType_t ) );
\r
1287 /*-----------------------------------------------------------*/
\r
1289 #if( ( ipconfigUSE_NBNS == 1 ) || ( ipconfigUSE_LLMNR == 1 ) )
\r
1291 static void prvReplyDNSMessage( NetworkBufferDescriptor_t *pxNetworkBuffer, BaseType_t lNetLength )
\r
1293 UDPPacket_t *pxUDPPacket;
\r
1294 IPHeader_t *pxIPHeader;
\r
1295 UDPHeader_t *pxUDPHeader;
\r
1297 pxUDPPacket = (UDPPacket_t *) pxNetworkBuffer->pucEthernetBuffer;
\r
1298 pxIPHeader = &pxUDPPacket->xIPHeader;
\r
1299 pxUDPHeader = &pxUDPPacket->xUDPHeader;
\r
1300 /* HT: started using defines like 'ipSIZE_OF_xxx' */
\r
1301 pxIPHeader->usLength = FreeRTOS_htons( lNetLength + ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_UDP_HEADER );
\r
1302 /* HT:endian: should not be translated, copying from packet to packet */
\r
1303 pxIPHeader->ulDestinationIPAddress = pxIPHeader->ulSourceIPAddress;
\r
1304 pxIPHeader->ulSourceIPAddress = *ipLOCAL_IP_ADDRESS_POINTER;
\r
1305 pxIPHeader->ucTimeToLive = ipconfigUDP_TIME_TO_LIVE;
\r
1306 pxIPHeader->usIdentification = FreeRTOS_htons( usPacketIdentifier );
\r
1307 usPacketIdentifier++;
\r
1308 pxUDPHeader->usLength = FreeRTOS_htons( lNetLength + ipSIZE_OF_UDP_HEADER );
\r
1309 vFlip_16( pxUDPPacket->xUDPHeader.usSourcePort, pxUDPPacket->xUDPHeader.usDestinationPort );
\r
1311 /* Important: tell NIC driver how many bytes must be sent */
\r
1312 pxNetworkBuffer->xDataLength = ( size_t ) ( lNetLength + ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_UDP_HEADER + ipSIZE_OF_ETH_HEADER );
\r
1314 #if( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 0 )
\r
1316 /* calculate the IP header checksum */
\r
1317 pxIPHeader->usHeaderChecksum = 0x00;
\r
1318 pxIPHeader->usHeaderChecksum = usGenerateChecksum( 0UL, ( uint8_t * ) &( pxIPHeader->ucVersionHeaderLength ), ipSIZE_OF_IPv4_HEADER );
\r
1319 pxIPHeader->usHeaderChecksum = ~FreeRTOS_htons( pxIPHeader->usHeaderChecksum );
\r
1321 /* calculate the UDP checksum for outgoing package */
\r
1322 usGenerateProtocolChecksum( ( uint8_t* ) pxUDPPacket, pxNetworkBuffer->xDataLength, pdTRUE );
\r
1326 /* This function will fill in the eth addresses and send the packet */
\r
1327 vReturnEthernetFrame( pxNetworkBuffer, pdFALSE );
\r
1330 #endif /* ipconfigUSE_NBNS == 1 || ipconfigUSE_LLMNR == 1 */
\r
1331 /*-----------------------------------------------------------*/
\r
1333 #if( ipconfigUSE_DNS_CACHE == 1 )
\r
1335 static void prvProcessDNSCache( const char *pcName, uint32_t *pulIP, uint32_t ulTTL, BaseType_t xLookUp )
\r
1338 BaseType_t xFound = pdFALSE;
\r
1339 uint32_t ulCurrentTimeSeconds = ( xTaskGetTickCount() / portTICK_PERIOD_MS ) / 1000;
\r
1340 static BaseType_t xFreeEntry = 0;
\r
1341 configASSERT(pcName);
\r
1344 /* For each entry in the DNS cache table. */
\r
1345 for( x = 0; x < ipconfigDNS_CACHE_ENTRIES; x++ )
\r
1347 if( xDNSCache[ x ].pcName[ 0 ] == 0 )
\r
1352 if( 0 == strcmp( xDNSCache[ x ].pcName, pcName ) )
\r
1354 /* Is this function called for a lookup or to add/update an IP address? */
\r
1355 if( xLookUp != pdFALSE )
\r
1357 /* Confirm that the record is still fresh. */
\r
1358 if( ulCurrentTimeSeconds < ( xDNSCache[ x ].ulTimeWhenAddedInSeconds + FreeRTOS_ntohl( xDNSCache[ x ].ulTTL ) ) )
\r
1360 *pulIP = xDNSCache[ x ].ulIPAddress;
\r
1364 /* Age out the old cached record. */
\r
1365 xDNSCache[ x ].pcName[ 0 ] = 0;
\r
1370 xDNSCache[ x ].ulIPAddress = *pulIP;
\r
1371 xDNSCache[ x ].ulTTL = ulTTL;
\r
1372 xDNSCache[ x ].ulTimeWhenAddedInSeconds = ulCurrentTimeSeconds;
\r
1380 if( xFound == pdFALSE )
\r
1382 if( xLookUp != pdFALSE )
\r
1388 /* Add or update the item. */
\r
1389 if( strlen( pcName ) < ipconfigDNS_CACHE_NAME_LENGTH )
\r
1391 strcpy( xDNSCache[ xFreeEntry ].pcName, pcName );
\r
1393 xDNSCache[ xFreeEntry ].ulIPAddress = *pulIP;
\r
1394 xDNSCache[ xFreeEntry ].ulTTL = ulTTL;
\r
1395 xDNSCache[ xFreeEntry ].ulTimeWhenAddedInSeconds = ulCurrentTimeSeconds;
\r
1398 if( xFreeEntry == ipconfigDNS_CACHE_ENTRIES )
\r
1406 if( ( xLookUp == 0 ) || ( *pulIP != 0 ) )
\r
1408 FreeRTOS_debug_printf( ( "prvProcessDNSCache: %s: '%s' @ %lxip\n", xLookUp ? "look-up" : "add", pcName, FreeRTOS_ntohl( *pulIP ) ) );
\r
1412 #endif /* ipconfigUSE_DNS_CACHE */
\r
1414 #endif /* ipconfigUSE_DNS != 0 */
\r
1416 /*-----------------------------------------------------------*/
\r
1418 /* Provide access to private members for testing. */
\r
1419 #ifdef AMAZON_FREERTOS_ENABLE_UNIT_TESTS
\r
1420 #include "iot_freertos_tcp_test_access_dns_define.h"
\r