2 * FreeRTOS+UDP V1.0.0 (C) 2013 Real Time Engineers ltd.
\r
4 * This file is part of the FreeRTOS+UDP distribution. The FreeRTOS+UDP license
\r
5 * terms are different to the FreeRTOS license terms.
\r
7 * FreeRTOS+UDP uses a dual license model that allows the software to be used
\r
8 * under a standard GPL open source license, or a commercial license. The
\r
9 * standard GPL license (unlike the modified GPL license under which FreeRTOS
\r
10 * itself is distributed) requires that all software statically linked with
\r
11 * FreeRTOS+UDP is also distributed under the same GPL V2 license terms.
\r
12 * Details of both license options follow:
\r
14 * - Open source licensing -
\r
15 * FreeRTOS+UDP is a free download and may be used, modified, evaluated and
\r
16 * distributed without charge provided the user adheres to version two of the
\r
17 * GNU General Public License (GPL) and does not remove the copyright notice or
\r
18 * this text. The GPL V2 text is available on the gnu.org web site, and on the
\r
19 * following URL: http://www.FreeRTOS.org/gpl-2.0.txt.
\r
21 * - Commercial licensing -
\r
22 * Businesses and individuals that for commercial or other reasons cannot comply
\r
23 * with the terms of the GPL V2 license must obtain a commercial license before
\r
24 * incorporating FreeRTOS+UDP into proprietary software for distribution in any
\r
25 * form. Commercial licenses can be purchased from http://shop.freertos.org/udp
\r
26 * and do not require any source files to be changed.
\r
28 * FreeRTOS+UDP is distributed in the hope that it will be useful. You cannot
\r
29 * use FreeRTOS+UDP unless you agree that you use the software 'as is'.
\r
30 * FreeRTOS+UDP is provided WITHOUT ANY WARRANTY; without even the implied
\r
31 * warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A PARTICULAR
\r
32 * PURPOSE. Real Time Engineers Ltd. disclaims all conditions and terms, be they
\r
33 * implied, expressed, or statutory.
\r
35 * 1 tab == 4 spaces!
\r
37 * http://www.FreeRTOS.org
\r
38 * http://www.FreeRTOS.org/udp
\r
42 /* Standard includes. */
\r
45 /* FreeRTOS includes. */
\r
46 #include "FreeRTOS.h"
\r
51 /* FreeRTOS+UDP includes. */
\r
52 #include "FreeRTOS_UDP_IP.h"
\r
53 #include "FreeRTOS_IP_Private.h"
\r
54 #include "FreeRTOS_DNS.h"
\r
55 #include "FreeRTOS_Sockets.h"
\r
56 #include "NetworkInterface.h"
\r
57 #include "IPTraceMacroDefaults.h"
\r
59 /* Exclude the entire file if DNS is not enabled. */
\r
60 #if ipconfigUSE_DNS != 0
\r
62 #if( ipconfigBYTE_ORDER == FREERTOS_LITTLE_ENDIAN )
\r
63 #define dnsOUTGOING_FLAGS 0x0001 /* Standard query. */
\r
64 #define dnsTYPE 0x0100 /* A record (host address. */
\r
65 #define dnsCLASS 0x0100 /* IN */
\r
66 #define dnsDNS_PORT 0x3500
\r
67 #define dnsONE_QUESTION 0x0100
\r
68 #define dnsRX_FLAGS_MASK 0x0f80 /* The bits of interest in the flags field of incoming DNS messages. */
\r
69 #define dnsEXPECTED_RX_FLAGS 0x0080 /* Should be a response, without any errors. */
\r
71 #define dnsDNS_PORT 0x35
\r
72 #define dnsONE_QUESTION 0x01
\r
73 #define dnsFLAG_QUERY_RESPONSE_BIT 0x8000
\r
74 #define dnsFLAG_OPERATION_CODE_BITS 0x7800
\r
75 #define dnsFLAG_TRUNCATION_BIT 0x0200
\r
76 #define dnsFLAG_RESPONSE_CODE_BITS 0x000f
\r
77 #define dnsOUTGOING_FLAGS 0x0100 /* Standard query. */
\r
78 #define dnsTYPE 0x0001 /* A record (host address. */
\r
79 #define dnsCLASS 0x0001 /* IN */
\r
80 #define dnsRX_FLAGS_MASK 0x800f /* The bits of interest in the flags field of incoming DNS messages. */
\r
81 #define dnsEXPECTED_RX_FLAGS 0x8000 /* Should be a response, without any errors. */
\r
82 #endif /* ipconfigBYTE_ORDER */
\r
84 /* The maximum number of times a DNS request should be sent out if a response
\r
85 is not received, before giving up. */
\r
86 #define dnsMAX_REQUEST_ATTEMPTS 5
\r
88 /* If the top two bits in the first character of a name field are set then the
\r
89 name field is an offset to the string, rather than the string itself. */
\r
90 #define dnsNAME_IS_OFFSET ( ( uint8_t ) 0xc0 )
\r
93 * Create a socket and bind it to the standard DNS port number. Return the
\r
94 * the created socket - or NULL if the socket could not be created or bound.
\r
96 static xSocket_t prvCreateDNSSocket( void );
\r
99 * Create the DNS message in the zero copy buffer passed in the first parameter.
\r
101 static size_t prvCreateDNSMessage( uint8_t *pucUDPPayloadBuffer, const uint8_t *pcHostName, uint16_t usIdentifier );
\r
104 * Simple routine that jumps over the NAME field of a resource record.
\r
106 static uint8_t *prvSkipNameField( uint8_t *pucByte );
\r
109 * Process a response packet from a DNS server.
\r
111 static uint32_t prvParseDNSReply( uint8_t *pucUDPPayloadBuffer, uint16_t usIdentifier );
\r
113 /*-----------------------------------------------------------*/
\r
115 #include "pack_struct_start.h"
\r
118 uint16_t usIdentifier;
\r
120 uint16_t usQuestions;
\r
121 uint16_t usAnswers;
\r
122 uint16_t usAuthorityRRs;
\r
123 uint16_t usAdditionalRRs;
\r
125 #include "pack_struct_end.h"
\r
126 typedef struct xDNSMessage xDNSMessage_t;
\r
128 /*-----------------------------------------------------------*/
\r
130 uint32_t FreeRTOS_gethostbyname( const uint8_t *pcHostName )
\r
132 static uint16_t usIdentifier = 0;
\r
133 struct freertos_sockaddr xAddress;
\r
134 static xSocket_t xDNSSocket = NULL;
\r
135 uint32_t ulIPAddress = 0UL;
\r
136 uint8_t *pucUDPPayloadBuffer;
\r
137 static uint32_t ulAddressLength;
\r
138 portBASE_TYPE xAttempt;
\r
140 size_t xPayloadLength;
\r
141 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
143 if( xDNSSocket == NULL )
\r
145 xDNSSocket = prvCreateDNSSocket();
\r
148 if( xDNSSocket != NULL )
\r
150 /* Generate a unique identifier for this query. */
\r
153 for( xAttempt = 0; xAttempt < dnsMAX_REQUEST_ATTEMPTS; xAttempt++ )
\r
155 /* Get a buffer. This uses a maximum delay, but the delay will be
\r
156 capped to ipconfigMAX_SEND_BLOCK_TIME_TICKS so the return value
\r
157 still needs to be tested. */
\r
158 pucUDPPayloadBuffer = ( uint8_t * ) FreeRTOS_GetUDPPayloadBuffer( xExpectedPayloadLength, portMAX_DELAY );
\r
159 if( pucUDPPayloadBuffer != NULL )
\r
161 /* Create the message in the obtained buffer. */
\r
162 xPayloadLength = prvCreateDNSMessage( pucUDPPayloadBuffer, pcHostName, usIdentifier );
\r
163 iptraceSENDING_DNS_REQUEST();
\r
165 /* Obtain the DNS server address. */
\r
166 FreeRTOS_GetAddressConfiguration( NULL, NULL, NULL, &ulIPAddress );
\r
168 /* Send the DNS message. */
\r
169 xAddress.sin_addr = ulIPAddress;
\r
170 xAddress.sin_port = dnsDNS_PORT;
\r
173 if( FreeRTOS_sendto( xDNSSocket, pucUDPPayloadBuffer, xPayloadLength, FREERTOS_ZERO_COPY, &xAddress, sizeof( xAddress ) ) != 0 )
\r
175 /* Wait for the reply. */
\r
176 lBytes = FreeRTOS_recvfrom( xDNSSocket, &pucUDPPayloadBuffer, 0, FREERTOS_ZERO_COPY, &xAddress, &ulAddressLength );
\r
180 /* The reply was received. Process it. */
\r
181 ulIPAddress = prvParseDNSReply( pucUDPPayloadBuffer, usIdentifier );
\r
183 /* Finished with the buffer. The zero copy interface
\r
184 is being used, so the buffer must be freed by the
\r
186 FreeRTOS_ReleaseUDPPayloadBuffer( ( void * ) pucUDPPayloadBuffer );
\r
188 if( ulIPAddress != 0 )
\r
197 /* The message was not sent so the stack will not be
\r
198 releasing the zero copy - it must be released here. */
\r
199 FreeRTOS_ReleaseUDPPayloadBuffer( ( void * ) pucUDPPayloadBuffer );
\r
205 return ulIPAddress;
\r
207 /*-----------------------------------------------------------*/
\r
209 static size_t prvCreateDNSMessage( uint8_t *pucUDPPayloadBuffer, const uint8_t *pcHostName, uint16_t usIdentifier )
\r
211 xDNSMessage_t *pxDNSMessageHeader;
\r
212 uint8_t *pucStart, *pucByte;
\r
213 const uint16_t usARecordType = dnsTYPE, usClass = dnsCLASS;
\r
214 static const xDNSMessage_t xDefaultPartDNSHeader =
\r
216 0, /* The identifier will be overwritten. */
\r
217 dnsOUTGOING_FLAGS, /* Flags set for standard query. */
\r
218 dnsONE_QUESTION, /* One question is being asked. */
\r
219 0, /* No replies are included. */
\r
220 0, /* No authorities. */
\r
221 0 /* No additional authorities. */
\r
224 /* Copy in the const part of the header. */
\r
225 memcpy( ( void * ) pucUDPPayloadBuffer, ( void * ) &xDefaultPartDNSHeader, sizeof( xDefaultPartDNSHeader ) );
\r
227 /* Write in a unique identifier. */
\r
228 pxDNSMessageHeader = ( xDNSMessage_t * ) pucUDPPayloadBuffer;
\r
229 pxDNSMessageHeader->usIdentifier = usIdentifier;
\r
231 /* Create the resource record at the end of the header. First
\r
232 find the end of the header. */
\r
233 pucStart = pucUDPPayloadBuffer + sizeof( xDefaultPartDNSHeader );
\r
235 /* Leave a gap for the first length bytes. */
\r
236 pucByte = pucStart + 1;
\r
238 /* Copy in the host name. */
\r
239 strcpy( ( char * ) pucByte, ( const char * ) pcHostName );
\r
241 /* Mark the end of the string. */
\r
242 pucByte += strlen( ( const char * ) pcHostName );
\r
245 /* Walk the string to replace the '.' characters with byte counts.
\r
246 pucStart holds the address of the byte count. Walking the string
\r
247 starts after the byte count position. */
\r
248 pucByte = pucStart;
\r
254 while( ( *pucByte != 0x00 ) && ( *pucByte != '.' ) )
\r
259 /* Fill in the byte count, then move the pucStart pointer up to
\r
260 the found byte position. */
\r
261 *pucStart = ( uint8_t ) ( ( uint32_t ) pucByte - ( uint32_t ) pucStart );
\r
264 pucStart = pucByte;
\r
266 } while( *pucByte != 0x00 );
\r
268 /* Finish off the record. */
\r
270 memcpy( ( void * ) pucByte, &usARecordType, sizeof( uint16_t ) );
\r
271 pucByte += sizeof( uint16_t );
\r
272 memcpy( ( void * ) pucByte, &usClass, sizeof( uint16_t ) );
\r
273 pucByte += sizeof( uint16_t );
\r
275 /* Return the total size of the generated message, which is the space from
\r
276 the last written byte to the beginning of the buffer. */
\r
277 return ( ( uint32_t ) pucByte - ( uint32_t ) pucUDPPayloadBuffer );
\r
279 /*-----------------------------------------------------------*/
\r
281 static uint8_t *prvSkipNameField( uint8_t *pucByte )
\r
283 /* Determine if the name is the fully coded name, or an offset to the name
\r
284 elsewhere in the message. */
\r
285 if( ( *pucByte & dnsNAME_IS_OFFSET ) == dnsNAME_IS_OFFSET )
\r
287 /* Jump over the two byte offset. */
\r
288 pucByte += sizeof( uint16_t );
\r
293 /* pucByte points to the full name. Walk over the string. */
\r
294 while( *pucByte != 0x00 )
\r
296 /* The number of bytes to jump for each name section is stored in the byte
\r
297 before the name section. */
\r
298 pucByte += ( *pucByte + 1 );
\r
306 /*-----------------------------------------------------------*/
\r
308 static uint32_t prvParseDNSReply( uint8_t *pucUDPPayloadBuffer, uint16_t usIdentifier )
\r
310 xDNSMessage_t *pxDNSMessageHeader;
\r
311 uint32_t ulIPAddress = 0UL;
\r
313 uint16_t x, usDataLength;
\r
314 const uint16_t usARecordType = dnsTYPE;
\r
316 pxDNSMessageHeader = ( xDNSMessage_t * ) pucUDPPayloadBuffer;
\r
318 if( pxDNSMessageHeader->usIdentifier == usIdentifier )
\r
320 if( ( pxDNSMessageHeader->usFlags & dnsRX_FLAGS_MASK ) == dnsEXPECTED_RX_FLAGS )
\r
322 /* Start at the first byte after the header. */
\r
323 pucByte = pucUDPPayloadBuffer + sizeof( xDNSMessage_t );
\r
325 /* Skip any question records. */
\r
326 pxDNSMessageHeader->usQuestions = FreeRTOS_ntohs( pxDNSMessageHeader->usQuestions );
\r
327 for( x = 0; x < pxDNSMessageHeader->usQuestions; x++ )
\r
329 /* Skip the variable length name field. */
\r
330 pucByte = prvSkipNameField( pucByte );
\r
332 /* Skip the type and class fields. */
\r
333 pucByte += sizeof( uint32_t );
\r
336 /* Search through the answers records. */
\r
337 pxDNSMessageHeader->usAnswers = FreeRTOS_ntohs( pxDNSMessageHeader->usAnswers );
\r
338 for( x = 0; x < pxDNSMessageHeader->usAnswers; x++ )
\r
340 pucByte = prvSkipNameField( pucByte );
\r
342 /* Is the type field that of an A record? */
\r
343 if( memcmp( ( void * ) pucByte, ( void * ) &usARecordType, sizeof( uint16_t ) ) == 0 )
\r
345 /* This is the required record. Skip the type, class, and
\r
346 time to live fields, plus the first byte of the data
\r
348 pucByte += ( sizeof( uint32_t ) + sizeof( uint32_t ) + sizeof( uint8_t ) );
\r
350 /* Sanity check the data length. */
\r
351 if( *pucByte == sizeof( uint32_t ) )
\r
353 /* Skip the second byte of the length. */
\r
356 /* Copy the IP address out of the record. */
\r
357 memcpy( ( void * ) &ulIPAddress, ( void * ) pucByte, sizeof( uint32_t ) );
\r
364 /* Skip the type, class and time to live fields. */
\r
365 pucByte += ( sizeof( uint32_t ) + sizeof( uint32_t ) );
\r
367 /* Determine the length of the data in the field. */
\r
368 memcpy( ( void * ) &usDataLength, ( void * ) pucByte, sizeof( uint16_t ) );
\r
369 usDataLength = FreeRTOS_ntohs( usDataLength );
\r
371 /* Jump over the data lenth bytes, and the data itself. */
\r
372 pucByte += usDataLength + sizeof( uint16_t );
\r
378 return ulIPAddress;
\r
380 /*-----------------------------------------------------------*/
\r
382 static xSocket_t prvCreateDNSSocket( void )
\r
384 static xSocket_t xSocket = NULL;
\r
385 struct freertos_sockaddr xAddress;
\r
386 portBASE_TYPE xReturn;
\r
387 portTickType xTimeoutTime = 200 / portTICK_RATE_MS;
\r
389 /* This must be the first time this function has been called. Create
\r
391 xSocket = FreeRTOS_socket( FREERTOS_AF_INET, FREERTOS_SOCK_DGRAM, FREERTOS_IPPROTO_UDP );
\r
393 /* Auto bind the port. */
\r
394 xAddress.sin_port = 0;
\r
395 xReturn = FreeRTOS_bind( xSocket, &xAddress, sizeof( xAddress ) );
\r
397 /* Check the bind was successful, and clean up if not. */
\r
400 FreeRTOS_closesocket( xSocket );
\r
405 /* Set the send and receive timeouts. */
\r
406 FreeRTOS_setsockopt( xSocket, 0, FREERTOS_SO_RCVTIMEO, ( void * ) &xTimeoutTime, sizeof( portTickType ) );
\r
407 FreeRTOS_setsockopt( xSocket, 0, FREERTOS_SO_SNDTIMEO, ( void * ) &xTimeoutTime, sizeof( portTickType ) );
\r
413 #endif /* ipconfigUSE_DNS != 0 */
\r