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. If you wish to use our Amazon
\r
14 * FreeRTOS name, please do so in a fair use way that does not cause confusion.
\r
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
\r
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
\r
18 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
\r
19 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
\r
20 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
\r
21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\r
23 * http://www.FreeRTOS.org
\r
24 * http://aws.amazon.com/freertos
\r
26 * 1 tab == 4 spaces!
\r
29 /* Standard includes. */
\r
32 /* FreeRTOS includes. */
\r
33 #include "FreeRTOS.h"
\r
38 /* FreeRTOS+UDP includes. */
\r
39 #include "FreeRTOS_UDP_IP.h"
\r
40 #include "FreeRTOS_IP_Private.h"
\r
41 #include "FreeRTOS_DNS.h"
\r
42 #include "FreeRTOS_Sockets.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 == FREERTOS_LITTLE_ENDIAN )
\r
50 #define dnsOUTGOING_FLAGS 0x0001 /* Standard query. */
\r
51 #define dnsTYPE 0x0100 /* A record (host address. */
\r
52 #define dnsCLASS 0x0100 /* IN */
\r
53 #define dnsDNS_PORT 0x3500
\r
54 #define dnsONE_QUESTION 0x0100
\r
55 #define dnsRX_FLAGS_MASK 0x0f80 /* The bits of interest in the flags field of incoming DNS messages. */
\r
56 #define dnsEXPECTED_RX_FLAGS 0x0080 /* Should be a response, without any errors. */
\r
58 #define dnsDNS_PORT 0x35
\r
59 #define dnsONE_QUESTION 0x01
\r
60 #define dnsFLAG_QUERY_RESPONSE_BIT 0x8000
\r
61 #define dnsFLAG_OPERATION_CODE_BITS 0x7800
\r
62 #define dnsFLAG_TRUNCATION_BIT 0x0200
\r
63 #define dnsFLAG_RESPONSE_CODE_BITS 0x000f
\r
64 #define dnsOUTGOING_FLAGS 0x0100 /* Standard query. */
\r
65 #define dnsTYPE 0x0001 /* A record (host address. */
\r
66 #define dnsCLASS 0x0001 /* IN */
\r
67 #define dnsRX_FLAGS_MASK 0x800f /* The bits of interest in the flags field of incoming DNS messages. */
\r
68 #define dnsEXPECTED_RX_FLAGS 0x8000 /* Should be a response, without any errors. */
\r
69 #endif /* ipconfigBYTE_ORDER */
\r
71 /* The maximum number of times a DNS request should be sent out if a response
\r
72 is not received, before giving up. */
\r
73 #define dnsMAX_REQUEST_ATTEMPTS 5
\r
75 /* If the top two bits in the first character of a name field are set then the
\r
76 name field is an offset to the string, rather than the string itself. */
\r
77 #define dnsNAME_IS_OFFSET ( ( uint8_t ) 0xc0 )
\r
80 * Create a socket and bind it to the standard DNS port number. Return the
\r
81 * the created socket - or NULL if the socket could not be created or bound.
\r
83 static xSocket_t prvCreateDNSSocket( void );
\r
86 * Create the DNS message in the zero copy buffer passed in the first parameter.
\r
88 static size_t prvCreateDNSMessage( uint8_t *pucUDPPayloadBuffer, const char *pcHostName, uint16_t usIdentifier );
\r
91 * Simple routine that jumps over the NAME field of a resource record.
\r
93 static uint8_t *prvSkipNameField( uint8_t *pucByte );
\r
96 * Process a response packet from a DNS server.
\r
98 static uint32_t prvParseDNSReply( uint8_t *pucUDPPayloadBuffer, uint16_t usIdentifier );
\r
100 /*-----------------------------------------------------------*/
\r
102 #include "pack_struct_start.h"
\r
105 uint16_t usIdentifier;
\r
107 uint16_t usQuestions;
\r
108 uint16_t usAnswers;
\r
109 uint16_t usAuthorityRRs;
\r
110 uint16_t usAdditionalRRs;
\r
112 #include "pack_struct_end.h"
\r
113 typedef struct xDNSMessage xDNSMessage_t;
\r
115 /*-----------------------------------------------------------*/
\r
117 uint32_t FreeRTOS_gethostbyname( const char *pcHostName )
\r
119 static uint16_t usIdentifier = 0;
\r
120 struct freertos_sockaddr xAddress;
\r
121 static xSocket_t xDNSSocket = NULL;
\r
122 uint32_t ulIPAddress = 0UL;
\r
123 uint8_t *pucUDPPayloadBuffer;
\r
124 static uint32_t ulAddressLength;
\r
125 BaseType_t xAttempt;
\r
127 size_t xPayloadLength;
\r
128 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
130 if( xDNSSocket == NULL )
\r
132 xDNSSocket = prvCreateDNSSocket();
\r
135 if( xDNSSocket != NULL )
\r
137 /* Generate a unique identifier for this query. */
\r
140 for( xAttempt = 0; xAttempt < dnsMAX_REQUEST_ATTEMPTS; xAttempt++ )
\r
142 /* Get a buffer. This uses a maximum delay, but the delay will be
\r
143 capped to ipconfigMAX_SEND_BLOCK_TIME_TICKS so the return value
\r
144 still needs to be tested. */
\r
145 pucUDPPayloadBuffer = ( uint8_t * ) FreeRTOS_GetUDPPayloadBuffer( xExpectedPayloadLength, portMAX_DELAY );
\r
146 if( pucUDPPayloadBuffer != NULL )
\r
148 /* Create the message in the obtained buffer. */
\r
149 xPayloadLength = prvCreateDNSMessage( pucUDPPayloadBuffer, pcHostName, usIdentifier );
\r
150 iptraceSENDING_DNS_REQUEST();
\r
152 /* Obtain the DNS server address. */
\r
153 FreeRTOS_GetAddressConfiguration( NULL, NULL, NULL, &ulIPAddress );
\r
155 /* Send the DNS message. */
\r
156 xAddress.sin_addr = ulIPAddress;
\r
157 xAddress.sin_port = dnsDNS_PORT;
\r
160 if( FreeRTOS_sendto( xDNSSocket, pucUDPPayloadBuffer, xPayloadLength, FREERTOS_ZERO_COPY, &xAddress, sizeof( xAddress ) ) != 0 )
\r
162 /* Wait for the reply. */
\r
163 lBytes = FreeRTOS_recvfrom( xDNSSocket, &pucUDPPayloadBuffer, 0, FREERTOS_ZERO_COPY, &xAddress, &ulAddressLength );
\r
167 /* The reply was received. Process it. */
\r
168 ulIPAddress = prvParseDNSReply( pucUDPPayloadBuffer, usIdentifier );
\r
170 /* Finished with the buffer. The zero copy interface
\r
171 is being used, so the buffer must be freed by the
\r
173 FreeRTOS_ReleaseUDPPayloadBuffer( ( void * ) pucUDPPayloadBuffer );
\r
175 if( ulIPAddress != 0 )
\r
184 /* The message was not sent so the stack will not be
\r
185 releasing the zero copy - it must be released here. */
\r
186 FreeRTOS_ReleaseUDPPayloadBuffer( ( void * ) pucUDPPayloadBuffer );
\r
192 return ulIPAddress;
\r
194 /*-----------------------------------------------------------*/
\r
196 static size_t prvCreateDNSMessage( uint8_t *pucUDPPayloadBuffer, const char *pcHostName, uint16_t usIdentifier )
\r
198 xDNSMessage_t *pxDNSMessageHeader;
\r
199 uint8_t *pucStart, *pucByte;
\r
200 const uint16_t usARecordType = dnsTYPE, usClass = dnsCLASS;
\r
201 static const xDNSMessage_t xDefaultPartDNSHeader =
\r
203 0, /* The identifier will be overwritten. */
\r
204 dnsOUTGOING_FLAGS, /* Flags set for standard query. */
\r
205 dnsONE_QUESTION, /* One question is being asked. */
\r
206 0, /* No replies are included. */
\r
207 0, /* No authorities. */
\r
208 0 /* No additional authorities. */
\r
211 /* Copy in the const part of the header. */
\r
212 memcpy( ( void * ) pucUDPPayloadBuffer, ( void * ) &xDefaultPartDNSHeader, sizeof( xDefaultPartDNSHeader ) );
\r
214 /* Write in a unique identifier. */
\r
215 pxDNSMessageHeader = ( xDNSMessage_t * ) pucUDPPayloadBuffer;
\r
216 pxDNSMessageHeader->usIdentifier = usIdentifier;
\r
218 /* Create the resource record at the end of the header. First
\r
219 find the end of the header. */
\r
220 pucStart = pucUDPPayloadBuffer + sizeof( xDefaultPartDNSHeader );
\r
222 /* Leave a gap for the first length bytes. */
\r
223 pucByte = pucStart + 1;
\r
225 /* Copy in the host name. */
\r
226 strcpy( ( char * ) pucByte, pcHostName );
\r
228 /* Mark the end of the string. */
\r
229 pucByte += strlen( pcHostName );
\r
232 /* Walk the string to replace the '.' characters with byte counts.
\r
233 pucStart holds the address of the byte count. Walking the string
\r
234 starts after the byte count position. */
\r
235 pucByte = pucStart;
\r
241 while( ( *pucByte != 0x00 ) && ( *pucByte != '.' ) )
\r
246 /* Fill in the byte count, then move the pucStart pointer up to
\r
247 the found byte position. */
\r
248 *pucStart = ( uint8_t ) ( ( uint32_t ) pucByte - ( uint32_t ) pucStart );
\r
251 pucStart = pucByte;
\r
253 } while( *pucByte != 0x00 );
\r
255 /* Finish off the record. */
\r
257 memcpy( ( void * ) pucByte, &usARecordType, sizeof( uint16_t ) );
\r
258 pucByte += sizeof( uint16_t );
\r
259 memcpy( ( void * ) pucByte, &usClass, sizeof( uint16_t ) );
\r
260 pucByte += sizeof( uint16_t );
\r
262 /* Return the total size of the generated message, which is the space from
\r
263 the last written byte to the beginning of the buffer. */
\r
264 return ( ( uint32_t ) pucByte - ( uint32_t ) pucUDPPayloadBuffer );
\r
266 /*-----------------------------------------------------------*/
\r
268 static uint8_t *prvSkipNameField( uint8_t *pucByte )
\r
270 /* Determine if the name is the fully coded name, or an offset to the name
\r
271 elsewhere in the message. */
\r
272 if( ( *pucByte & dnsNAME_IS_OFFSET ) == dnsNAME_IS_OFFSET )
\r
274 /* Jump over the two byte offset. */
\r
275 pucByte += sizeof( uint16_t );
\r
280 /* pucByte points to the full name. Walk over the string. */
\r
281 while( *pucByte != 0x00 )
\r
283 /* The number of bytes to jump for each name section is stored in the byte
\r
284 before the name section. */
\r
285 pucByte += ( *pucByte + 1 );
\r
293 /*-----------------------------------------------------------*/
\r
295 static uint32_t prvParseDNSReply( uint8_t *pucUDPPayloadBuffer, uint16_t usIdentifier )
\r
297 xDNSMessage_t *pxDNSMessageHeader;
\r
298 uint32_t ulIPAddress = 0UL;
\r
300 uint16_t x, usDataLength;
\r
301 const uint16_t usARecordType = dnsTYPE;
\r
303 pxDNSMessageHeader = ( xDNSMessage_t * ) pucUDPPayloadBuffer;
\r
305 if( pxDNSMessageHeader->usIdentifier == usIdentifier )
\r
307 if( ( pxDNSMessageHeader->usFlags & dnsRX_FLAGS_MASK ) == dnsEXPECTED_RX_FLAGS )
\r
309 /* Start at the first byte after the header. */
\r
310 pucByte = pucUDPPayloadBuffer + sizeof( xDNSMessage_t );
\r
312 /* Skip any question records. */
\r
313 pxDNSMessageHeader->usQuestions = FreeRTOS_ntohs( pxDNSMessageHeader->usQuestions );
\r
314 for( x = 0; x < pxDNSMessageHeader->usQuestions; x++ )
\r
316 /* Skip the variable length name field. */
\r
317 pucByte = prvSkipNameField( pucByte );
\r
319 /* Skip the type and class fields. */
\r
320 pucByte += sizeof( uint32_t );
\r
323 /* Search through the answers records. */
\r
324 pxDNSMessageHeader->usAnswers = FreeRTOS_ntohs( pxDNSMessageHeader->usAnswers );
\r
325 for( x = 0; x < pxDNSMessageHeader->usAnswers; x++ )
\r
327 pucByte = prvSkipNameField( pucByte );
\r
329 /* Is the type field that of an A record? */
\r
330 if( memcmp( ( void * ) pucByte, ( void * ) &usARecordType, sizeof( uint16_t ) ) == 0 )
\r
332 /* This is the required record. Skip the type, class, and
\r
333 time to live fields, plus the first byte of the data
\r
335 pucByte += ( sizeof( uint32_t ) + sizeof( uint32_t ) + sizeof( uint8_t ) );
\r
337 /* Sanity check the data length. */
\r
338 if( *pucByte == sizeof( uint32_t ) )
\r
340 /* Skip the second byte of the length. */
\r
343 /* Copy the IP address out of the record. */
\r
344 memcpy( ( void * ) &ulIPAddress, ( void * ) pucByte, sizeof( uint32_t ) );
\r
351 /* Skip the type, class and time to live fields. */
\r
352 pucByte += ( sizeof( uint32_t ) + sizeof( uint32_t ) );
\r
354 /* Determine the length of the data in the field. */
\r
355 memcpy( ( void * ) &usDataLength, ( void * ) pucByte, sizeof( uint16_t ) );
\r
356 usDataLength = FreeRTOS_ntohs( usDataLength );
\r
358 /* Jump over the data lenth bytes, and the data itself. */
\r
359 pucByte += usDataLength + sizeof( uint16_t );
\r
365 return ulIPAddress;
\r
367 /*-----------------------------------------------------------*/
\r
369 static xSocket_t prvCreateDNSSocket( void )
\r
371 static xSocket_t xSocket = NULL;
\r
372 struct freertos_sockaddr xAddress;
\r
373 BaseType_t xReturn;
\r
374 TickType_t xTimeoutTime = 200 / portTICK_RATE_MS;
\r
376 /* This must be the first time this function has been called. Create
\r
378 xSocket = FreeRTOS_socket( FREERTOS_AF_INET, FREERTOS_SOCK_DGRAM, FREERTOS_IPPROTO_UDP );
\r
380 /* Auto bind the port. */
\r
381 xAddress.sin_port = 0;
\r
382 xReturn = FreeRTOS_bind( xSocket, &xAddress, sizeof( xAddress ) );
\r
384 /* Check the bind was successful, and clean up if not. */
\r
387 FreeRTOS_closesocket( xSocket );
\r
392 /* Set the send and receive timeouts. */
\r
393 FreeRTOS_setsockopt( xSocket, 0, FREERTOS_SO_RCVTIMEO, ( void * ) &xTimeoutTime, sizeof( TickType_t ) );
\r
394 FreeRTOS_setsockopt( xSocket, 0, FREERTOS_SO_SNDTIMEO, ( void * ) &xTimeoutTime, sizeof( TickType_t ) );
\r
400 #endif /* ipconfigUSE_DNS != 0 */
\r