2 * FreeRTOS+UDP V1.0.0 (C) 2013 Real Time Engineers ltd.
\r
4 * FreeRTOS+UDP is an add-on component to FreeRTOS. It is not, in itself, part
\r
5 * of the FreeRTOS kernel. FreeRTOS+UDP is licensed separately from FreeRTOS,
\r
6 * and uses a different license to FreeRTOS. FreeRTOS+UDP uses a dual license
\r
7 * model, information on which is provided below:
\r
9 * - Open source licensing -
\r
10 * FreeRTOS+UDP is a free download and may be used, modified and distributed
\r
11 * without charge provided the user adheres to version two of the GNU General
\r
12 * Public license (GPL) and does not remove the copyright notice or this text.
\r
13 * The GPL V2 text is available on the gnu.org web site, and on the following
\r
14 * URL: http://www.FreeRTOS.org/gpl-2.0.txt
\r
16 * - Commercial licensing -
\r
17 * Businesses and individuals who wish to incorporate FreeRTOS+UDP into
\r
18 * proprietary software for redistribution in any form must first obtain a
\r
19 * (very) low cost commercial license - and in-so-doing support the maintenance,
\r
20 * support and further development of the FreeRTOS+UDP product. Commercial
\r
21 * licenses can be obtained from http://shop.freertos.org and do not require any
\r
22 * source files to be changed.
\r
24 * FreeRTOS+UDP is distributed in the hope that it will be useful. You cannot
\r
25 * use FreeRTOS+UDP unless you agree that you use the software 'as is'.
\r
26 * FreeRTOS+UDP is provided WITHOUT ANY WARRANTY; without even the implied
\r
27 * warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A PARTICULAR
\r
28 * PURPOSE. Real Time Engineers Ltd. disclaims all conditions and terms, be they
\r
29 * implied, expressed, or statutory.
\r
31 * 1 tab == 4 spaces!
\r
33 * http://www.FreeRTOS.org
\r
34 * http://www.FreeRTOS.org/udp
\r
38 /* Standard includes. */
\r
41 /* FreeRTOS includes. */
\r
42 #include "FreeRTOS.h"
\r
47 /* FreeRTOS+UDP includes. */
\r
48 #include "FreeRTOS_UDP_IP.h"
\r
49 #include "FreeRTOS_IP_Private.h"
\r
50 #include "FreeRTOS_DNS.h"
\r
51 #include "FreeRTOS_Sockets.h"
\r
52 #include "NetworkInterface.h"
\r
53 #include "IPTraceMacroDefaults.h"
\r
55 /* Exclude the entire file if DNS is not enabled. */
\r
56 #if ipconfigUSE_DNS != 0
\r
58 #if( ipconfigBYTE_ORDER == FREERTOS_LITTLE_ENDIAN )
\r
59 #define dnsOUTGOING_FLAGS 0x0001 /* Standard query. */
\r
60 #define dnsTYPE 0x0100 /* A record (host address. */
\r
61 #define dnsCLASS 0x0100 /* IN */
\r
62 #define dnsDNS_PORT 0x3500
\r
63 #define dnsONE_QUESTION 0x0100
\r
64 #define dnsRX_FLAGS_MASK 0x0f80 /* The bits of interest in the flags field of incoming DNS messages. */
\r
65 #define dnsEXPECTED_RX_FLAGS 0x0080 /* Should be a response, without any errors. */
\r
67 #define dnsDNS_PORT 0x35
\r
68 #define dnsONE_QUESTION 0x01
\r
69 #define dnsFLAG_QUERY_RESPONSE_BIT 0x8000
\r
70 #define dnsFLAG_OPERATION_CODE_BITS 0x7800
\r
71 #define dnsFLAG_TRUNCATION_BIT 0x0200
\r
72 #define dnsFLAG_RESPONSE_CODE_BITS 0x000f
\r
73 #define dnsOUTGOING_FLAGS 0x0100 /* Standard query. */
\r
74 #define dnsTYPE 0x0001 /* A record (host address. */
\r
75 #define dnsCLASS 0x0001 /* IN */
\r
76 #define dnsRX_FLAGS_MASK 0x800f /* The bits of interest in the flags field of incoming DNS messages. */
\r
77 #define dnsEXPECTED_RX_FLAGS 0x8000 /* Should be a response, without any errors. */
\r
78 #endif /* ipconfigBYTE_ORDER */
\r
80 /* The maximum number of times a DNS request should be sent out if a response
\r
81 is not received, before giving up. */
\r
82 #define dnsMAX_REQUEST_ATTEMPTS 5
\r
84 /* If the top two bits in the first character of a name field are set then the
\r
85 name field is an offset to the string, rather than the string itself. */
\r
86 #define dnsNAME_IS_OFFSET ( ( uint8_t ) 0xc0 )
\r
89 * Create a socket and bind it to the standard DNS port number. Return the
\r
90 * the created socket - or NULL if the socket could not be created or bound.
\r
92 static xSocket_t prvCreateDNSSocket( void );
\r
95 * Create the DNS message in the zero copy buffer passed in the first parameter.
\r
97 static size_t prvCreateDNSMessage( uint8_t *pucUDPPayloadBuffer, const uint8_t *pcHostName, uint16_t usIdentifier );
\r
100 * Simple routine that jumps over the NAME field of a resource record.
\r
102 static uint8_t *prvSkipNameField( uint8_t *pucByte );
\r
105 * Process a response packet from a DNS server.
\r
107 static uint32_t prvParseDNSReply( uint8_t *pucUDPPayloadBuffer, uint16_t usIdentifier );
\r
109 /*-----------------------------------------------------------*/
\r
111 #include "pack_struct_start.h"
\r
114 uint16_t usIdentifier;
\r
116 uint16_t usQuestions;
\r
117 uint16_t usAnswers;
\r
118 uint16_t usAuthorityRRs;
\r
119 uint16_t usAdditionalRRs;
\r
121 #include "pack_struct_end.h"
\r
122 typedef struct xDNSMessage xDNSMessage_t;
\r
124 /*-----------------------------------------------------------*/
\r
126 uint32_t FreeRTOS_gethostbyname( const uint8_t *pcHostName )
\r
128 static uint16_t usIdentifier = 0;
\r
129 struct freertos_sockaddr xAddress;
\r
130 static xSocket_t xDNSSocket = NULL;
\r
131 uint32_t ulIPAddress = 0UL;
\r
132 uint8_t *pucUDPPayloadBuffer;
\r
133 static uint32_t ulAddressLength;
\r
134 portBASE_TYPE xAttempt;
\r
136 size_t xPayloadLength;
\r
137 const size_t xExpectedPayloadLength = sizeof( xDNSMessage_t ) + strlen( ( const char * const ) pcHostName ) + sizeof( uint16_t ) + sizeof( uint16_t ) + 2; /* Two for the count of characters in the first subdomain part, and the string end byte */
\r
139 if( xDNSSocket == NULL )
\r
141 xDNSSocket = prvCreateDNSSocket();
\r
144 if( xDNSSocket != NULL )
\r
146 /* Generate a unique identifier for this query. */
\r
149 for( xAttempt = 0; xAttempt < dnsMAX_REQUEST_ATTEMPTS; xAttempt++ )
\r
151 /* Get a buffer. This uses a maximum delay, but the delay will be
\r
152 capped to ipconfigMAX_SEND_BLOCK_TIME_TICKS so the return value
\r
153 still needs to be tested. */
\r
154 pucUDPPayloadBuffer = ( uint8_t * ) FreeRTOS_GetUDPPayloadBuffer( xExpectedPayloadLength, portMAX_DELAY );
\r
155 if( pucUDPPayloadBuffer != NULL )
\r
157 /* Create the message in the obtained buffer. */
\r
158 xPayloadLength = prvCreateDNSMessage( pucUDPPayloadBuffer, pcHostName, usIdentifier );
\r
159 iptraceSENDING_DNS_REQUEST();
\r
161 /* Obtain the DNS server address. */
\r
162 FreeRTOS_GetAddressConfiguration( NULL, NULL, NULL, &ulIPAddress );
\r
164 /* Send the DNS message. */
\r
165 xAddress.sin_addr = ulIPAddress;
\r
166 xAddress.sin_port = dnsDNS_PORT;
\r
169 if( FreeRTOS_sendto( xDNSSocket, pucUDPPayloadBuffer, xPayloadLength, FREERTOS_ZERO_COPY, &xAddress, sizeof( xAddress ) ) != 0 )
\r
171 /* Wait for the reply. */
\r
172 lBytes = FreeRTOS_recvfrom( xDNSSocket, &pucUDPPayloadBuffer, 0, FREERTOS_ZERO_COPY, &xAddress, &ulAddressLength );
\r
176 /* The reply was received. Process it. */
\r
177 ulIPAddress = prvParseDNSReply( pucUDPPayloadBuffer, usIdentifier );
\r
179 /* Finished with the buffer. The zero copy interface
\r
180 is being used, so the buffer must be freed by the
\r
182 FreeRTOS_ReleaseUDPPayloadBuffer( ( void * ) pucUDPPayloadBuffer );
\r
184 if( ulIPAddress != 0 )
\r
193 /* The message was not sent so the stack will not be
\r
194 releasing the zero copy - it must be released here. */
\r
195 FreeRTOS_ReleaseUDPPayloadBuffer( ( void * ) pucUDPPayloadBuffer );
\r
201 return ulIPAddress;
\r
203 /*-----------------------------------------------------------*/
\r
205 static size_t prvCreateDNSMessage( uint8_t *pucUDPPayloadBuffer, const uint8_t *pcHostName, uint16_t usIdentifier )
\r
207 xDNSMessage_t *pxDNSMessageHeader;
\r
208 uint8_t *pucStart, *pucByte;
\r
209 const uint16_t usARecordType = dnsTYPE, usClass = dnsCLASS;
\r
210 static const xDNSMessage_t xDefaultPartDNSHeader =
\r
212 0, /* The identifier will be overwritten. */
\r
213 dnsOUTGOING_FLAGS, /* Flags set for standard query. */
\r
214 dnsONE_QUESTION, /* One question is being asked. */
\r
215 0, /* No replies are included. */
\r
216 0, /* No authorities. */
\r
217 0 /* No additional authorities. */
\r
220 /* Copy in the const part of the header. */
\r
221 memcpy( ( void * ) pucUDPPayloadBuffer, ( void * ) &xDefaultPartDNSHeader, sizeof( xDefaultPartDNSHeader ) );
\r
223 /* Write in a unique identifier. */
\r
224 pxDNSMessageHeader = ( xDNSMessage_t * ) pucUDPPayloadBuffer;
\r
225 pxDNSMessageHeader->usIdentifier = usIdentifier;
\r
227 /* Create the resource record at the end of the header. First
\r
228 find the end of the header. */
\r
229 pucStart = pucUDPPayloadBuffer + sizeof( xDefaultPartDNSHeader );
\r
231 /* Leave a gap for the first length bytes. */
\r
232 pucByte = pucStart + 1;
\r
234 /* Copy in the host name. */
\r
235 strcpy( ( char * ) pucByte, ( const char * ) pcHostName );
\r
237 /* Mark the end of the string. */
\r
238 pucByte += strlen( ( const char * ) pcHostName );
\r
241 /* Walk the string to replace the '.' characters with byte counts.
\r
242 pucStart holds the address of the byte count. Walking the string
\r
243 starts after the byte count position. */
\r
244 pucByte = pucStart;
\r
250 while( ( *pucByte != 0x00 ) && ( *pucByte != '.' ) )
\r
255 /* Fill in the byte count, then move the pucStart pointer up to
\r
256 the found byte position. */
\r
257 *pucStart = ( uint8_t ) ( ( uint32_t ) pucByte - ( uint32_t ) pucStart );
\r
260 pucStart = pucByte;
\r
262 } while( *pucByte != 0x00 );
\r
264 /* Finish off the record. */
\r
266 memcpy( ( void * ) pucByte, &usARecordType, sizeof( uint16_t ) );
\r
267 pucByte += sizeof( uint16_t );
\r
268 memcpy( ( void * ) pucByte, &usClass, sizeof( uint16_t ) );
\r
269 pucByte += sizeof( uint16_t );
\r
271 /* Return the total size of the generated message, which is the space from
\r
272 the last written byte to the beginning of the buffer. */
\r
273 return ( ( uint32_t ) pucByte - ( uint32_t ) pucUDPPayloadBuffer );
\r
275 /*-----------------------------------------------------------*/
\r
277 static uint8_t *prvSkipNameField( uint8_t *pucByte )
\r
279 /* Determine if the name is the fully coded name, or an offset to the name
\r
280 elsewhere in the message. */
\r
281 if( ( *pucByte & dnsNAME_IS_OFFSET ) == dnsNAME_IS_OFFSET )
\r
283 /* Jump over the two byte offset. */
\r
284 pucByte += sizeof( uint16_t );
\r
289 /* pucByte points to the full name. Walk over the string. */
\r
290 while( *pucByte != 0x00 )
\r
292 /* The number of bytes to jump for each name section is stored in the byte
\r
293 before the name section. */
\r
294 pucByte += ( *pucByte + 1 );
\r
302 /*-----------------------------------------------------------*/
\r
304 static uint32_t prvParseDNSReply( uint8_t *pucUDPPayloadBuffer, uint16_t usIdentifier )
\r
306 xDNSMessage_t *pxDNSMessageHeader;
\r
307 uint32_t ulIPAddress = 0UL;
\r
309 uint16_t x, usDataLength;
\r
310 const uint16_t usARecordType = dnsTYPE;
\r
312 pxDNSMessageHeader = ( xDNSMessage_t * ) pucUDPPayloadBuffer;
\r
314 if( pxDNSMessageHeader->usIdentifier == usIdentifier )
\r
316 if( ( pxDNSMessageHeader->usFlags & dnsRX_FLAGS_MASK ) == dnsEXPECTED_RX_FLAGS )
\r
318 /* Start at the first byte after the header. */
\r
319 pucByte = pucUDPPayloadBuffer + sizeof( xDNSMessage_t );
\r
321 /* Skip any question records. */
\r
322 pxDNSMessageHeader->usQuestions = FreeRTOS_ntohs( pxDNSMessageHeader->usQuestions );
\r
323 for( x = 0; x < pxDNSMessageHeader->usQuestions; x++ )
\r
325 /* Skip the variable length name field. */
\r
326 pucByte = prvSkipNameField( pucByte );
\r
328 /* Skip the type and class fields. */
\r
329 pucByte += sizeof( uint32_t );
\r
332 /* Search through the answers records. */
\r
333 pxDNSMessageHeader->usAnswers = FreeRTOS_ntohs( pxDNSMessageHeader->usAnswers );
\r
334 for( x = 0; x < pxDNSMessageHeader->usAnswers; x++ )
\r
336 pucByte = prvSkipNameField( pucByte );
\r
338 /* Is the type field that of an A record? */
\r
339 if( memcmp( ( void * ) pucByte, ( void * ) &usARecordType, sizeof( uint16_t ) ) == 0 )
\r
341 /* This is the required record. Skip the type, class, and
\r
342 time to live fields, plus the first byte of the data
\r
344 pucByte += ( sizeof( uint32_t ) + sizeof( uint32_t ) + sizeof( uint8_t ) );
\r
346 /* Sanity check the data length. */
\r
347 if( *pucByte == sizeof( uint32_t ) )
\r
349 /* Skip the second byte of the length. */
\r
352 /* Copy the IP address out of the record. */
\r
353 memcpy( ( void * ) &ulIPAddress, ( void * ) pucByte, sizeof( uint32_t ) );
\r
360 /* Skip the type, class and time to live fields. */
\r
361 pucByte += ( sizeof( uint32_t ) + sizeof( uint32_t ) );
\r
363 /* Determine the length of the data in the field. */
\r
364 memcpy( ( void * ) &usDataLength, ( void * ) pucByte, sizeof( uint16_t ) );
\r
365 usDataLength = FreeRTOS_ntohs( usDataLength );
\r
367 /* Jump over the data lenth bytes, and the data itself. */
\r
368 pucByte += usDataLength + sizeof( uint16_t );
\r
374 return ulIPAddress;
\r
376 /*-----------------------------------------------------------*/
\r
378 static xSocket_t prvCreateDNSSocket( void )
\r
380 static xSocket_t xSocket = NULL;
\r
381 struct freertos_sockaddr xAddress;
\r
382 portBASE_TYPE xReturn;
\r
383 portTickType xTimeoutTime = 200 / portTICK_RATE_MS;
\r
385 /* This must be the first time this function has been called. Create
\r
387 xSocket = FreeRTOS_socket( FREERTOS_AF_INET, FREERTOS_SOCK_DGRAM, FREERTOS_IPPROTO_UDP );
\r
389 /* Auto bind the port. */
\r
390 xAddress.sin_port = 0;
\r
391 xReturn = FreeRTOS_bind( xSocket, &xAddress, sizeof( xAddress ) );
\r
393 /* Check the bind was successful, and clean up if not. */
\r
396 FreeRTOS_closesocket( xSocket );
\r
401 /* Set the send and receive timeouts. */
\r
402 FreeRTOS_setsockopt( xSocket, 0, FREERTOS_SO_RCVTIMEO, ( void * ) &xTimeoutTime, sizeof( portTickType ) );
\r
403 FreeRTOS_setsockopt( xSocket, 0, FREERTOS_SO_SNDTIMEO, ( void * ) &xTimeoutTime, sizeof( portTickType ) );
\r
409 #endif /* ipconfigUSE_DNS != 0 */
\r