2 * FreeRTOS+UDP V1.0.3 (C) 2014 Real Time Engineers ltd.
\r
3 * All rights reserved
\r
5 * This file is part of the FreeRTOS+UDP distribution. The FreeRTOS+UDP license
\r
6 * terms are different to the FreeRTOS license terms.
\r
8 * FreeRTOS+UDP uses a dual license model that allows the software to be used
\r
9 * under a standard GPL open source license, or a commercial license. The
\r
10 * standard GPL license (unlike the modified GPL license under which FreeRTOS
\r
11 * itself is distributed) requires that all software statically linked with
\r
12 * FreeRTOS+UDP is also distributed under the same GPL V2 license terms.
\r
13 * Details of both license options follow:
\r
15 * - Open source licensing -
\r
16 * FreeRTOS+UDP is a free download and may be used, modified, evaluated and
\r
17 * distributed without charge provided the user adheres to version two of the
\r
18 * GNU General Public License (GPL) and does not remove the copyright notice or
\r
19 * this text. The GPL V2 text is available on the gnu.org web site, and on the
\r
20 * following URL: http://www.FreeRTOS.org/gpl-2.0.txt.
\r
22 * - Commercial licensing -
\r
23 * Businesses and individuals that for commercial or other reasons cannot comply
\r
24 * with the terms of the GPL V2 license must obtain a commercial license before
\r
25 * incorporating FreeRTOS+UDP into proprietary software for distribution in any
\r
26 * form. Commercial licenses can be purchased from http://shop.freertos.org/udp
\r
27 * and do not require any source files to be changed.
\r
29 * FreeRTOS+UDP is distributed in the hope that it will be useful. You cannot
\r
30 * use FreeRTOS+UDP unless you agree that you use the software 'as is'.
\r
31 * FreeRTOS+UDP is provided WITHOUT ANY WARRANTY; without even the implied
\r
32 * warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A PARTICULAR
\r
33 * PURPOSE. Real Time Engineers Ltd. disclaims all conditions and terms, be they
\r
34 * implied, expressed, or statutory.
\r
36 * 1 tab == 4 spaces!
\r
38 * http://www.FreeRTOS.org
\r
39 * http://www.FreeRTOS.org/udp
\r
43 /* Standard includes. */
\r
46 /* FreeRTOS includes. */
\r
47 #include "FreeRTOS.h"
\r
52 /* FreeRTOS+UDP includes. */
\r
53 #include "FreeRTOS_UDP_IP.h"
\r
54 #include "FreeRTOS_IP_Private.h"
\r
55 #include "FreeRTOS_DNS.h"
\r
56 #include "FreeRTOS_Sockets.h"
\r
57 #include "NetworkInterface.h"
\r
58 #include "IPTraceMacroDefaults.h"
\r
60 /* Exclude the entire file if DNS is not enabled. */
\r
61 #if ipconfigUSE_DNS != 0
\r
63 #if( ipconfigBYTE_ORDER == FREERTOS_LITTLE_ENDIAN )
\r
64 #define dnsOUTGOING_FLAGS 0x0001 /* Standard query. */
\r
65 #define dnsTYPE 0x0100 /* A record (host address. */
\r
66 #define dnsCLASS 0x0100 /* IN */
\r
67 #define dnsDNS_PORT 0x3500
\r
68 #define dnsONE_QUESTION 0x0100
\r
69 #define dnsRX_FLAGS_MASK 0x0f80 /* The bits of interest in the flags field of incoming DNS messages. */
\r
70 #define dnsEXPECTED_RX_FLAGS 0x0080 /* Should be a response, without any errors. */
\r
72 #define dnsDNS_PORT 0x35
\r
73 #define dnsONE_QUESTION 0x01
\r
74 #define dnsFLAG_QUERY_RESPONSE_BIT 0x8000
\r
75 #define dnsFLAG_OPERATION_CODE_BITS 0x7800
\r
76 #define dnsFLAG_TRUNCATION_BIT 0x0200
\r
77 #define dnsFLAG_RESPONSE_CODE_BITS 0x000f
\r
78 #define dnsOUTGOING_FLAGS 0x0100 /* Standard query. */
\r
79 #define dnsTYPE 0x0001 /* A record (host address. */
\r
80 #define dnsCLASS 0x0001 /* IN */
\r
81 #define dnsRX_FLAGS_MASK 0x800f /* The bits of interest in the flags field of incoming DNS messages. */
\r
82 #define dnsEXPECTED_RX_FLAGS 0x8000 /* Should be a response, without any errors. */
\r
83 #endif /* ipconfigBYTE_ORDER */
\r
85 /* The maximum number of times a DNS request should be sent out if a response
\r
86 is not received, before giving up. */
\r
87 #define dnsMAX_REQUEST_ATTEMPTS 5
\r
89 /* If the top two bits in the first character of a name field are set then the
\r
90 name field is an offset to the string, rather than the string itself. */
\r
91 #define dnsNAME_IS_OFFSET ( ( uint8_t ) 0xc0 )
\r
94 * Create a socket and bind it to the standard DNS port number. Return the
\r
95 * the created socket - or NULL if the socket could not be created or bound.
\r
97 static xSocket_t prvCreateDNSSocket( void );
\r
100 * Create the DNS message in the zero copy buffer passed in the first parameter.
\r
102 static size_t prvCreateDNSMessage( uint8_t *pucUDPPayloadBuffer, const char *pcHostName, uint16_t usIdentifier );
\r
105 * Simple routine that jumps over the NAME field of a resource record.
\r
107 static uint8_t *prvSkipNameField( uint8_t *pucByte );
\r
110 * Process a response packet from a DNS server.
\r
112 static uint32_t prvParseDNSReply( uint8_t *pucUDPPayloadBuffer, uint16_t usIdentifier );
\r
114 /*-----------------------------------------------------------*/
\r
116 #include "pack_struct_start.h"
\r
119 uint16_t usIdentifier;
\r
121 uint16_t usQuestions;
\r
122 uint16_t usAnswers;
\r
123 uint16_t usAuthorityRRs;
\r
124 uint16_t usAdditionalRRs;
\r
126 #include "pack_struct_end.h"
\r
127 typedef struct xDNSMessage xDNSMessage_t;
\r
129 /*-----------------------------------------------------------*/
\r
131 uint32_t FreeRTOS_gethostbyname( const char *pcHostName )
\r
133 static uint16_t usIdentifier = 0;
\r
134 struct freertos_sockaddr xAddress;
\r
135 static xSocket_t xDNSSocket = NULL;
\r
136 uint32_t ulIPAddress = 0UL;
\r
137 uint8_t *pucUDPPayloadBuffer;
\r
138 static uint32_t ulAddressLength;
\r
139 BaseType_t xAttempt;
\r
141 size_t xPayloadLength;
\r
142 const size_t xExpectedPayloadLength = sizeof( xDNSMessage_t ) + strlen( 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
144 if( xDNSSocket == NULL )
\r
146 xDNSSocket = prvCreateDNSSocket();
\r
149 if( xDNSSocket != NULL )
\r
151 /* Generate a unique identifier for this query. */
\r
154 for( xAttempt = 0; xAttempt < dnsMAX_REQUEST_ATTEMPTS; xAttempt++ )
\r
156 /* Get a buffer. This uses a maximum delay, but the delay will be
\r
157 capped to ipconfigMAX_SEND_BLOCK_TIME_TICKS so the return value
\r
158 still needs to be tested. */
\r
159 pucUDPPayloadBuffer = ( uint8_t * ) FreeRTOS_GetUDPPayloadBuffer( xExpectedPayloadLength, portMAX_DELAY );
\r
160 if( pucUDPPayloadBuffer != NULL )
\r
162 /* Create the message in the obtained buffer. */
\r
163 xPayloadLength = prvCreateDNSMessage( pucUDPPayloadBuffer, pcHostName, usIdentifier );
\r
164 iptraceSENDING_DNS_REQUEST();
\r
166 /* Obtain the DNS server address. */
\r
167 FreeRTOS_GetAddressConfiguration( NULL, NULL, NULL, &ulIPAddress );
\r
169 /* Send the DNS message. */
\r
170 xAddress.sin_addr = ulIPAddress;
\r
171 xAddress.sin_port = dnsDNS_PORT;
\r
174 if( FreeRTOS_sendto( xDNSSocket, pucUDPPayloadBuffer, xPayloadLength, FREERTOS_ZERO_COPY, &xAddress, sizeof( xAddress ) ) != 0 )
\r
176 /* Wait for the reply. */
\r
177 lBytes = FreeRTOS_recvfrom( xDNSSocket, &pucUDPPayloadBuffer, 0, FREERTOS_ZERO_COPY, &xAddress, &ulAddressLength );
\r
181 /* The reply was received. Process it. */
\r
182 ulIPAddress = prvParseDNSReply( pucUDPPayloadBuffer, usIdentifier );
\r
184 /* Finished with the buffer. The zero copy interface
\r
185 is being used, so the buffer must be freed by the
\r
187 FreeRTOS_ReleaseUDPPayloadBuffer( ( void * ) pucUDPPayloadBuffer );
\r
189 if( ulIPAddress != 0 )
\r
198 /* The message was not sent so the stack will not be
\r
199 releasing the zero copy - it must be released here. */
\r
200 FreeRTOS_ReleaseUDPPayloadBuffer( ( void * ) pucUDPPayloadBuffer );
\r
206 return ulIPAddress;
\r
208 /*-----------------------------------------------------------*/
\r
210 static size_t prvCreateDNSMessage( uint8_t *pucUDPPayloadBuffer, const char *pcHostName, uint16_t usIdentifier )
\r
212 xDNSMessage_t *pxDNSMessageHeader;
\r
213 uint8_t *pucStart, *pucByte;
\r
214 const uint16_t usARecordType = dnsTYPE, usClass = dnsCLASS;
\r
215 static const xDNSMessage_t xDefaultPartDNSHeader =
\r
217 0, /* The identifier will be overwritten. */
\r
218 dnsOUTGOING_FLAGS, /* Flags set for standard query. */
\r
219 dnsONE_QUESTION, /* One question is being asked. */
\r
220 0, /* No replies are included. */
\r
221 0, /* No authorities. */
\r
222 0 /* No additional authorities. */
\r
225 /* Copy in the const part of the header. */
\r
226 memcpy( ( void * ) pucUDPPayloadBuffer, ( void * ) &xDefaultPartDNSHeader, sizeof( xDefaultPartDNSHeader ) );
\r
228 /* Write in a unique identifier. */
\r
229 pxDNSMessageHeader = ( xDNSMessage_t * ) pucUDPPayloadBuffer;
\r
230 pxDNSMessageHeader->usIdentifier = usIdentifier;
\r
232 /* Create the resource record at the end of the header. First
\r
233 find the end of the header. */
\r
234 pucStart = pucUDPPayloadBuffer + sizeof( xDefaultPartDNSHeader );
\r
236 /* Leave a gap for the first length bytes. */
\r
237 pucByte = pucStart + 1;
\r
239 /* Copy in the host name. */
\r
240 strcpy( ( char * ) pucByte, pcHostName );
\r
242 /* Mark the end of the string. */
\r
243 pucByte += strlen( pcHostName );
\r
246 /* Walk the string to replace the '.' characters with byte counts.
\r
247 pucStart holds the address of the byte count. Walking the string
\r
248 starts after the byte count position. */
\r
249 pucByte = pucStart;
\r
255 while( ( *pucByte != 0x00 ) && ( *pucByte != '.' ) )
\r
260 /* Fill in the byte count, then move the pucStart pointer up to
\r
261 the found byte position. */
\r
262 *pucStart = ( uint8_t ) ( ( uint32_t ) pucByte - ( uint32_t ) pucStart );
\r
265 pucStart = pucByte;
\r
267 } while( *pucByte != 0x00 );
\r
269 /* Finish off the record. */
\r
271 memcpy( ( void * ) pucByte, &usARecordType, sizeof( uint16_t ) );
\r
272 pucByte += sizeof( uint16_t );
\r
273 memcpy( ( void * ) pucByte, &usClass, sizeof( uint16_t ) );
\r
274 pucByte += sizeof( uint16_t );
\r
276 /* Return the total size of the generated message, which is the space from
\r
277 the last written byte to the beginning of the buffer. */
\r
278 return ( ( uint32_t ) pucByte - ( uint32_t ) pucUDPPayloadBuffer );
\r
280 /*-----------------------------------------------------------*/
\r
282 static uint8_t *prvSkipNameField( uint8_t *pucByte )
\r
284 /* Determine if the name is the fully coded name, or an offset to the name
\r
285 elsewhere in the message. */
\r
286 if( ( *pucByte & dnsNAME_IS_OFFSET ) == dnsNAME_IS_OFFSET )
\r
288 /* Jump over the two byte offset. */
\r
289 pucByte += sizeof( uint16_t );
\r
294 /* pucByte points to the full name. Walk over the string. */
\r
295 while( *pucByte != 0x00 )
\r
297 /* The number of bytes to jump for each name section is stored in the byte
\r
298 before the name section. */
\r
299 pucByte += ( *pucByte + 1 );
\r
307 /*-----------------------------------------------------------*/
\r
309 static uint32_t prvParseDNSReply( uint8_t *pucUDPPayloadBuffer, uint16_t usIdentifier )
\r
311 xDNSMessage_t *pxDNSMessageHeader;
\r
312 uint32_t ulIPAddress = 0UL;
\r
314 uint16_t x, usDataLength;
\r
315 const uint16_t usARecordType = dnsTYPE;
\r
317 pxDNSMessageHeader = ( xDNSMessage_t * ) pucUDPPayloadBuffer;
\r
319 if( pxDNSMessageHeader->usIdentifier == usIdentifier )
\r
321 if( ( pxDNSMessageHeader->usFlags & dnsRX_FLAGS_MASK ) == dnsEXPECTED_RX_FLAGS )
\r
323 /* Start at the first byte after the header. */
\r
324 pucByte = pucUDPPayloadBuffer + sizeof( xDNSMessage_t );
\r
326 /* Skip any question records. */
\r
327 pxDNSMessageHeader->usQuestions = FreeRTOS_ntohs( pxDNSMessageHeader->usQuestions );
\r
328 for( x = 0; x < pxDNSMessageHeader->usQuestions; x++ )
\r
330 /* Skip the variable length name field. */
\r
331 pucByte = prvSkipNameField( pucByte );
\r
333 /* Skip the type and class fields. */
\r
334 pucByte += sizeof( uint32_t );
\r
337 /* Search through the answers records. */
\r
338 pxDNSMessageHeader->usAnswers = FreeRTOS_ntohs( pxDNSMessageHeader->usAnswers );
\r
339 for( x = 0; x < pxDNSMessageHeader->usAnswers; x++ )
\r
341 pucByte = prvSkipNameField( pucByte );
\r
343 /* Is the type field that of an A record? */
\r
344 if( memcmp( ( void * ) pucByte, ( void * ) &usARecordType, sizeof( uint16_t ) ) == 0 )
\r
346 /* This is the required record. Skip the type, class, and
\r
347 time to live fields, plus the first byte of the data
\r
349 pucByte += ( sizeof( uint32_t ) + sizeof( uint32_t ) + sizeof( uint8_t ) );
\r
351 /* Sanity check the data length. */
\r
352 if( *pucByte == sizeof( uint32_t ) )
\r
354 /* Skip the second byte of the length. */
\r
357 /* Copy the IP address out of the record. */
\r
358 memcpy( ( void * ) &ulIPAddress, ( void * ) pucByte, sizeof( uint32_t ) );
\r
365 /* Skip the type, class and time to live fields. */
\r
366 pucByte += ( sizeof( uint32_t ) + sizeof( uint32_t ) );
\r
368 /* Determine the length of the data in the field. */
\r
369 memcpy( ( void * ) &usDataLength, ( void * ) pucByte, sizeof( uint16_t ) );
\r
370 usDataLength = FreeRTOS_ntohs( usDataLength );
\r
372 /* Jump over the data lenth bytes, and the data itself. */
\r
373 pucByte += usDataLength + sizeof( uint16_t );
\r
379 return ulIPAddress;
\r
381 /*-----------------------------------------------------------*/
\r
383 static xSocket_t prvCreateDNSSocket( void )
\r
385 static xSocket_t xSocket = NULL;
\r
386 struct freertos_sockaddr xAddress;
\r
387 BaseType_t xReturn;
\r
388 TickType_t xTimeoutTime = 200 / portTICK_RATE_MS;
\r
390 /* This must be the first time this function has been called. Create
\r
392 xSocket = FreeRTOS_socket( FREERTOS_AF_INET, FREERTOS_SOCK_DGRAM, FREERTOS_IPPROTO_UDP );
\r
394 /* Auto bind the port. */
\r
395 xAddress.sin_port = 0;
\r
396 xReturn = FreeRTOS_bind( xSocket, &xAddress, sizeof( xAddress ) );
\r
398 /* Check the bind was successful, and clean up if not. */
\r
401 FreeRTOS_closesocket( xSocket );
\r
406 /* Set the send and receive timeouts. */
\r
407 FreeRTOS_setsockopt( xSocket, 0, FREERTOS_SO_RCVTIMEO, ( void * ) &xTimeoutTime, sizeof( TickType_t ) );
\r
408 FreeRTOS_setsockopt( xSocket, 0, FREERTOS_SO_SNDTIMEO, ( void * ) &xTimeoutTime, sizeof( TickType_t ) );
\r
414 #endif /* ipconfigUSE_DNS != 0 */
\r