2 * FreeRTOS+UDP V1.0.4
\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://www.FreeRTOS.org
\r
23 * http://aws.amazon.com/freertos
\r
25 * 1 tab == 4 spaces!
\r
28 /* Standard includes. */
\r
31 /* FreeRTOS includes. */
\r
32 #include "FreeRTOS.h"
\r
37 /* FreeRTOS+UDP includes. */
\r
38 #include "FreeRTOS_UDP_IP.h"
\r
39 #include "FreeRTOS_IP_Private.h"
\r
40 #include "FreeRTOS_DNS.h"
\r
41 #include "FreeRTOS_Sockets.h"
\r
42 #include "NetworkInterface.h"
\r
43 #include "IPTraceMacroDefaults.h"
\r
45 /* Exclude the entire file if DNS is not enabled. */
\r
46 #if ipconfigUSE_DNS != 0
\r
48 #if( ipconfigBYTE_ORDER == FREERTOS_LITTLE_ENDIAN )
\r
49 #define dnsOUTGOING_FLAGS 0x0001 /* Standard query. */
\r
50 #define dnsTYPE 0x0100 /* A record (host address. */
\r
51 #define dnsCLASS 0x0100 /* IN */
\r
52 #define dnsDNS_PORT 0x3500
\r
53 #define dnsONE_QUESTION 0x0100
\r
54 #define dnsRX_FLAGS_MASK 0x0f80 /* The bits of interest in the flags field of incoming DNS messages. */
\r
55 #define dnsEXPECTED_RX_FLAGS 0x0080 /* Should be a response, without any errors. */
\r
57 #define dnsDNS_PORT 0x35
\r
58 #define dnsONE_QUESTION 0x01
\r
59 #define dnsFLAG_QUERY_RESPONSE_BIT 0x8000
\r
60 #define dnsFLAG_OPERATION_CODE_BITS 0x7800
\r
61 #define dnsFLAG_TRUNCATION_BIT 0x0200
\r
62 #define dnsFLAG_RESPONSE_CODE_BITS 0x000f
\r
63 #define dnsOUTGOING_FLAGS 0x0100 /* Standard query. */
\r
64 #define dnsTYPE 0x0001 /* A record (host address. */
\r
65 #define dnsCLASS 0x0001 /* IN */
\r
66 #define dnsRX_FLAGS_MASK 0x800f /* The bits of interest in the flags field of incoming DNS messages. */
\r
67 #define dnsEXPECTED_RX_FLAGS 0x8000 /* Should be a response, without any errors. */
\r
68 #endif /* ipconfigBYTE_ORDER */
\r
70 /* The maximum number of times a DNS request should be sent out if a response
\r
71 is not received, before giving up. */
\r
72 #define dnsMAX_REQUEST_ATTEMPTS 5
\r
74 /* If the top two bits in the first character of a name field are set then the
\r
75 name field is an offset to the string, rather than the string itself. */
\r
76 #define dnsNAME_IS_OFFSET ( ( uint8_t ) 0xc0 )
\r
79 * Create a socket and bind it to the standard DNS port number. Return the
\r
80 * the created socket - or NULL if the socket could not be created or bound.
\r
82 static xSocket_t prvCreateDNSSocket( void );
\r
85 * Create the DNS message in the zero copy buffer passed in the first parameter.
\r
87 static size_t prvCreateDNSMessage( uint8_t *pucUDPPayloadBuffer, const char *pcHostName, uint16_t usIdentifier );
\r
90 * Simple routine that jumps over the NAME field of a resource record.
\r
92 static uint8_t *prvSkipNameField( uint8_t *pucByte );
\r
95 * Process a response packet from a DNS server.
\r
97 static uint32_t prvParseDNSReply( uint8_t *pucUDPPayloadBuffer, uint16_t usIdentifier );
\r
99 /*-----------------------------------------------------------*/
\r
101 #include "pack_struct_start.h"
\r
104 uint16_t usIdentifier;
\r
106 uint16_t usQuestions;
\r
107 uint16_t usAnswers;
\r
108 uint16_t usAuthorityRRs;
\r
109 uint16_t usAdditionalRRs;
\r
111 #include "pack_struct_end.h"
\r
112 typedef struct xDNSMessage xDNSMessage_t;
\r
114 /*-----------------------------------------------------------*/
\r
116 uint32_t FreeRTOS_gethostbyname( const char *pcHostName )
\r
118 static uint16_t usIdentifier = 0;
\r
119 struct freertos_sockaddr xAddress;
\r
120 static xSocket_t xDNSSocket = NULL;
\r
121 uint32_t ulIPAddress = 0UL;
\r
122 uint8_t *pucUDPPayloadBuffer;
\r
123 static uint32_t ulAddressLength;
\r
124 BaseType_t xAttempt;
\r
126 size_t xPayloadLength;
\r
127 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
129 if( xDNSSocket == NULL )
\r
131 xDNSSocket = prvCreateDNSSocket();
\r
134 if( xDNSSocket != NULL )
\r
136 /* Generate a unique identifier for this query. */
\r
139 for( xAttempt = 0; xAttempt < dnsMAX_REQUEST_ATTEMPTS; xAttempt++ )
\r
141 /* Get a buffer. This uses a maximum delay, but the delay will be
\r
142 capped to ipconfigMAX_SEND_BLOCK_TIME_TICKS so the return value
\r
143 still needs to be tested. */
\r
144 pucUDPPayloadBuffer = ( uint8_t * ) FreeRTOS_GetUDPPayloadBuffer( xExpectedPayloadLength, portMAX_DELAY );
\r
145 if( pucUDPPayloadBuffer != NULL )
\r
147 /* Create the message in the obtained buffer. */
\r
148 xPayloadLength = prvCreateDNSMessage( pucUDPPayloadBuffer, pcHostName, usIdentifier );
\r
149 iptraceSENDING_DNS_REQUEST();
\r
151 /* Obtain the DNS server address. */
\r
152 FreeRTOS_GetAddressConfiguration( NULL, NULL, NULL, &ulIPAddress );
\r
154 /* Send the DNS message. */
\r
155 xAddress.sin_addr = ulIPAddress;
\r
156 xAddress.sin_port = dnsDNS_PORT;
\r
159 if( FreeRTOS_sendto( xDNSSocket, pucUDPPayloadBuffer, xPayloadLength, FREERTOS_ZERO_COPY, &xAddress, sizeof( xAddress ) ) != 0 )
\r
161 /* Wait for the reply. */
\r
162 lBytes = FreeRTOS_recvfrom( xDNSSocket, &pucUDPPayloadBuffer, 0, FREERTOS_ZERO_COPY, &xAddress, &ulAddressLength );
\r
166 /* The reply was received. Process it. */
\r
167 ulIPAddress = prvParseDNSReply( pucUDPPayloadBuffer, usIdentifier );
\r
169 /* Finished with the buffer. The zero copy interface
\r
170 is being used, so the buffer must be freed by the
\r
172 FreeRTOS_ReleaseUDPPayloadBuffer( ( void * ) pucUDPPayloadBuffer );
\r
174 if( ulIPAddress != 0 )
\r
183 /* The message was not sent so the stack will not be
\r
184 releasing the zero copy - it must be released here. */
\r
185 FreeRTOS_ReleaseUDPPayloadBuffer( ( void * ) pucUDPPayloadBuffer );
\r
191 return ulIPAddress;
\r
193 /*-----------------------------------------------------------*/
\r
195 static size_t prvCreateDNSMessage( uint8_t *pucUDPPayloadBuffer, const char *pcHostName, uint16_t usIdentifier )
\r
197 xDNSMessage_t *pxDNSMessageHeader;
\r
198 uint8_t *pucStart, *pucByte;
\r
199 const uint16_t usARecordType = dnsTYPE, usClass = dnsCLASS;
\r
200 static const xDNSMessage_t xDefaultPartDNSHeader =
\r
202 0, /* The identifier will be overwritten. */
\r
203 dnsOUTGOING_FLAGS, /* Flags set for standard query. */
\r
204 dnsONE_QUESTION, /* One question is being asked. */
\r
205 0, /* No replies are included. */
\r
206 0, /* No authorities. */
\r
207 0 /* No additional authorities. */
\r
210 /* Copy in the const part of the header. */
\r
211 memcpy( ( void * ) pucUDPPayloadBuffer, ( void * ) &xDefaultPartDNSHeader, sizeof( xDefaultPartDNSHeader ) );
\r
213 /* Write in a unique identifier. */
\r
214 pxDNSMessageHeader = ( xDNSMessage_t * ) pucUDPPayloadBuffer;
\r
215 pxDNSMessageHeader->usIdentifier = usIdentifier;
\r
217 /* Create the resource record at the end of the header. First
\r
218 find the end of the header. */
\r
219 pucStart = pucUDPPayloadBuffer + sizeof( xDefaultPartDNSHeader );
\r
221 /* Leave a gap for the first length bytes. */
\r
222 pucByte = pucStart + 1;
\r
224 /* Copy in the host name. */
\r
225 strcpy( ( char * ) pucByte, pcHostName );
\r
227 /* Mark the end of the string. */
\r
228 pucByte += strlen( pcHostName );
\r
231 /* Walk the string to replace the '.' characters with byte counts.
\r
232 pucStart holds the address of the byte count. Walking the string
\r
233 starts after the byte count position. */
\r
234 pucByte = pucStart;
\r
240 while( ( *pucByte != 0x00 ) && ( *pucByte != '.' ) )
\r
245 /* Fill in the byte count, then move the pucStart pointer up to
\r
246 the found byte position. */
\r
247 *pucStart = ( uint8_t ) ( ( uint32_t ) pucByte - ( uint32_t ) pucStart );
\r
250 pucStart = pucByte;
\r
252 } while( *pucByte != 0x00 );
\r
254 /* Finish off the record. */
\r
256 memcpy( ( void * ) pucByte, &usARecordType, sizeof( uint16_t ) );
\r
257 pucByte += sizeof( uint16_t );
\r
258 memcpy( ( void * ) pucByte, &usClass, sizeof( uint16_t ) );
\r
259 pucByte += sizeof( uint16_t );
\r
261 /* Return the total size of the generated message, which is the space from
\r
262 the last written byte to the beginning of the buffer. */
\r
263 return ( ( uint32_t ) pucByte - ( uint32_t ) pucUDPPayloadBuffer );
\r
265 /*-----------------------------------------------------------*/
\r
267 static uint8_t *prvSkipNameField( uint8_t *pucByte )
\r
269 /* Determine if the name is the fully coded name, or an offset to the name
\r
270 elsewhere in the message. */
\r
271 if( ( *pucByte & dnsNAME_IS_OFFSET ) == dnsNAME_IS_OFFSET )
\r
273 /* Jump over the two byte offset. */
\r
274 pucByte += sizeof( uint16_t );
\r
279 /* pucByte points to the full name. Walk over the string. */
\r
280 while( *pucByte != 0x00 )
\r
282 /* The number of bytes to jump for each name section is stored in the byte
\r
283 before the name section. */
\r
284 pucByte += ( *pucByte + 1 );
\r
292 /*-----------------------------------------------------------*/
\r
294 static uint32_t prvParseDNSReply( uint8_t *pucUDPPayloadBuffer, uint16_t usIdentifier )
\r
296 xDNSMessage_t *pxDNSMessageHeader;
\r
297 uint32_t ulIPAddress = 0UL;
\r
299 uint16_t x, usDataLength;
\r
300 const uint16_t usARecordType = dnsTYPE;
\r
302 pxDNSMessageHeader = ( xDNSMessage_t * ) pucUDPPayloadBuffer;
\r
304 if( pxDNSMessageHeader->usIdentifier == usIdentifier )
\r
306 if( ( pxDNSMessageHeader->usFlags & dnsRX_FLAGS_MASK ) == dnsEXPECTED_RX_FLAGS )
\r
308 /* Start at the first byte after the header. */
\r
309 pucByte = pucUDPPayloadBuffer + sizeof( xDNSMessage_t );
\r
311 /* Skip any question records. */
\r
312 pxDNSMessageHeader->usQuestions = FreeRTOS_ntohs( pxDNSMessageHeader->usQuestions );
\r
313 for( x = 0; x < pxDNSMessageHeader->usQuestions; x++ )
\r
315 /* Skip the variable length name field. */
\r
316 pucByte = prvSkipNameField( pucByte );
\r
318 /* Skip the type and class fields. */
\r
319 pucByte += sizeof( uint32_t );
\r
322 /* Search through the answers records. */
\r
323 pxDNSMessageHeader->usAnswers = FreeRTOS_ntohs( pxDNSMessageHeader->usAnswers );
\r
324 for( x = 0; x < pxDNSMessageHeader->usAnswers; x++ )
\r
326 pucByte = prvSkipNameField( pucByte );
\r
328 /* Is the type field that of an A record? */
\r
329 if( memcmp( ( void * ) pucByte, ( void * ) &usARecordType, sizeof( uint16_t ) ) == 0 )
\r
331 /* This is the required record. Skip the type, class, and
\r
332 time to live fields, plus the first byte of the data
\r
334 pucByte += ( sizeof( uint32_t ) + sizeof( uint32_t ) + sizeof( uint8_t ) );
\r
336 /* Sanity check the data length. */
\r
337 if( *pucByte == sizeof( uint32_t ) )
\r
339 /* Skip the second byte of the length. */
\r
342 /* Copy the IP address out of the record. */
\r
343 memcpy( ( void * ) &ulIPAddress, ( void * ) pucByte, sizeof( uint32_t ) );
\r
350 /* Skip the type, class and time to live fields. */
\r
351 pucByte += ( sizeof( uint32_t ) + sizeof( uint32_t ) );
\r
353 /* Determine the length of the data in the field. */
\r
354 memcpy( ( void * ) &usDataLength, ( void * ) pucByte, sizeof( uint16_t ) );
\r
355 usDataLength = FreeRTOS_ntohs( usDataLength );
\r
357 /* Jump over the data lenth bytes, and the data itself. */
\r
358 pucByte += usDataLength + sizeof( uint16_t );
\r
364 return ulIPAddress;
\r
366 /*-----------------------------------------------------------*/
\r
368 static xSocket_t prvCreateDNSSocket( void )
\r
370 static xSocket_t xSocket = NULL;
\r
371 struct freertos_sockaddr xAddress;
\r
372 BaseType_t xReturn;
\r
373 TickType_t xTimeoutTime = 200 / portTICK_RATE_MS;
\r
375 /* This must be the first time this function has been called. Create
\r
377 xSocket = FreeRTOS_socket( FREERTOS_AF_INET, FREERTOS_SOCK_DGRAM, FREERTOS_IPPROTO_UDP );
\r
379 /* Auto bind the port. */
\r
380 xAddress.sin_port = 0;
\r
381 xReturn = FreeRTOS_bind( xSocket, &xAddress, sizeof( xAddress ) );
\r
383 /* Check the bind was successful, and clean up if not. */
\r
386 FreeRTOS_closesocket( xSocket );
\r
391 /* Set the send and receive timeouts. */
\r
392 FreeRTOS_setsockopt( xSocket, 0, FREERTOS_SO_RCVTIMEO, ( void * ) &xTimeoutTime, sizeof( TickType_t ) );
\r
393 FreeRTOS_setsockopt( xSocket, 0, FREERTOS_SO_SNDTIMEO, ( void * ) &xTimeoutTime, sizeof( TickType_t ) );
\r
399 #endif /* ipconfigUSE_DNS != 0 */
\r