]> git.sur5r.net Git - freertos/blob - FreeRTOS-Plus/Source/FreeRTOS-Plus-UDP/FreeRTOS_DNS.c
Update license information text files for the CLI, TCP and UDP products to be correct...
[freertos] / FreeRTOS-Plus / Source / FreeRTOS-Plus-UDP / FreeRTOS_DNS.c
1 /*\r
2  * FreeRTOS+UDP V1.0.4\r
3  * Copyright (C) 2017 Amazon.com, Inc. or its affiliates.  All Rights Reserved.\r
4  *\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
11  *\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
15  *\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
22  *\r
23  * http://www.FreeRTOS.org\r
24  * http://aws.amazon.com/freertos\r
25  *\r
26  * 1 tab == 4 spaces!\r
27  */\r
28 \r
29 /* Standard includes. */\r
30 #include <stdint.h>\r
31 \r
32 /* FreeRTOS includes. */\r
33 #include "FreeRTOS.h"\r
34 #include "task.h"\r
35 #include "queue.h"\r
36 #include "timers.h"\r
37 \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
45 \r
46 /* Exclude the entire file if DNS is not enabled. */\r
47 #if ipconfigUSE_DNS != 0\r
48 \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
57 #else\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
70 \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
74 \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
78 \r
79 /*\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
82  */\r
83 static xSocket_t prvCreateDNSSocket( void );\r
84 \r
85 /*\r
86  * Create the DNS message in the zero copy buffer passed in the first parameter.\r
87  */\r
88 static size_t prvCreateDNSMessage( uint8_t *pucUDPPayloadBuffer, const char *pcHostName, uint16_t usIdentifier );\r
89 \r
90 /*\r
91  * Simple routine that jumps over the NAME field of a resource record.\r
92  */\r
93 static uint8_t *prvSkipNameField( uint8_t *pucByte );\r
94 \r
95 /*\r
96  * Process a response packet from a DNS server.\r
97  */\r
98 static uint32_t prvParseDNSReply( uint8_t *pucUDPPayloadBuffer, uint16_t usIdentifier );\r
99 \r
100 /*-----------------------------------------------------------*/\r
101 \r
102 #include "pack_struct_start.h"\r
103 struct xDNSMessage\r
104 {\r
105         uint16_t usIdentifier;\r
106         uint16_t usFlags;\r
107         uint16_t usQuestions;\r
108         uint16_t usAnswers;\r
109         uint16_t usAuthorityRRs;\r
110         uint16_t usAdditionalRRs;\r
111 }\r
112 #include "pack_struct_end.h"\r
113 typedef struct xDNSMessage xDNSMessage_t;\r
114 \r
115 /*-----------------------------------------------------------*/\r
116 \r
117 uint32_t FreeRTOS_gethostbyname( const char *pcHostName )\r
118 {\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
126 int32_t lBytes;\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
129 \r
130         if( xDNSSocket == NULL )\r
131         {\r
132                 xDNSSocket = prvCreateDNSSocket();\r
133         }\r
134 \r
135         if( xDNSSocket != NULL )\r
136         {\r
137                 /* Generate a unique identifier for this query. */\r
138                 usIdentifier++;\r
139 \r
140                 for( xAttempt = 0; xAttempt < dnsMAX_REQUEST_ATTEMPTS; xAttempt++ )\r
141                 {\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
147                         {\r
148                                 /* Create the message in the obtained buffer. */\r
149                                 xPayloadLength = prvCreateDNSMessage( pucUDPPayloadBuffer, pcHostName, usIdentifier );\r
150                                 iptraceSENDING_DNS_REQUEST();\r
151 \r
152                                 /* Obtain the DNS server address. */\r
153                                 FreeRTOS_GetAddressConfiguration( NULL, NULL, NULL, &ulIPAddress );\r
154 \r
155                                 /* Send the DNS message. */\r
156                                 xAddress.sin_addr = ulIPAddress;\r
157                                 xAddress.sin_port = dnsDNS_PORT;\r
158                                 ulIPAddress = 0;\r
159 \r
160                                 if( FreeRTOS_sendto( xDNSSocket, pucUDPPayloadBuffer, xPayloadLength, FREERTOS_ZERO_COPY, &xAddress, sizeof( xAddress ) ) != 0 )\r
161                                 {\r
162                                         /* Wait for the reply. */\r
163                                         lBytes = FreeRTOS_recvfrom( xDNSSocket, &pucUDPPayloadBuffer, 0, FREERTOS_ZERO_COPY, &xAddress, &ulAddressLength );\r
164 \r
165                                         if( lBytes > 0 )\r
166                                         {\r
167                                                 /* The reply was received.  Process it. */\r
168                                                 ulIPAddress = prvParseDNSReply( pucUDPPayloadBuffer, usIdentifier );\r
169 \r
170                                                 /* Finished with the buffer.  The zero copy interface\r
171                                                 is being used, so the buffer must be freed by the\r
172                                                 task. */\r
173                                                 FreeRTOS_ReleaseUDPPayloadBuffer( ( void * ) pucUDPPayloadBuffer );\r
174 \r
175                                                 if( ulIPAddress != 0 )\r
176                                                 {\r
177                                                         /* All done. */\r
178                                                         break;\r
179                                                 }\r
180                                         }\r
181                                 }\r
182                                 else\r
183                                 {\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
187                                 }\r
188                         }\r
189                 }\r
190         }\r
191 \r
192         return ulIPAddress;\r
193 }\r
194 /*-----------------------------------------------------------*/\r
195 \r
196 static size_t prvCreateDNSMessage( uint8_t *pucUDPPayloadBuffer, const char *pcHostName, uint16_t usIdentifier )\r
197 {\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
202 {\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
209 };\r
210 \r
211         /* Copy in the const part of the header. */\r
212         memcpy( ( void * ) pucUDPPayloadBuffer, ( void * ) &xDefaultPartDNSHeader, sizeof( xDefaultPartDNSHeader ) );\r
213 \r
214         /* Write in a unique identifier. */\r
215         pxDNSMessageHeader = ( xDNSMessage_t * ) pucUDPPayloadBuffer;\r
216         pxDNSMessageHeader->usIdentifier = usIdentifier;\r
217 \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
221 \r
222         /* Leave a gap for the first length bytes. */\r
223         pucByte = pucStart + 1;\r
224 \r
225         /* Copy in the host name. */\r
226         strcpy( ( char * ) pucByte, pcHostName );\r
227 \r
228         /* Mark the end of the string. */\r
229         pucByte += strlen( pcHostName );\r
230         *pucByte = 0x00;\r
231 \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
236 \r
237         do\r
238         {\r
239                 pucByte++;\r
240 \r
241                 while( ( *pucByte != 0x00 ) && ( *pucByte != '.' ) )\r
242                 {\r
243                         pucByte++;\r
244                 }\r
245 \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
249                 ( *pucStart )--;\r
250 \r
251                 pucStart = pucByte;\r
252 \r
253         } while( *pucByte != 0x00 );\r
254 \r
255         /* Finish off the record. */\r
256         pucByte++;\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
261 \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
265 }\r
266 /*-----------------------------------------------------------*/\r
267 \r
268 static uint8_t *prvSkipNameField( uint8_t *pucByte )\r
269 {\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
273         {\r
274                 /* Jump over the two byte offset. */\r
275                 pucByte += sizeof( uint16_t );\r
276 \r
277         }\r
278         else\r
279         {\r
280                 /* pucByte points to the full name.  Walk over the string. */\r
281                 while( *pucByte != 0x00 )\r
282                 {\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
286                 }\r
287 \r
288                 pucByte++;\r
289         }\r
290 \r
291         return pucByte;\r
292 }\r
293 /*-----------------------------------------------------------*/\r
294 \r
295 static uint32_t prvParseDNSReply( uint8_t *pucUDPPayloadBuffer, uint16_t usIdentifier )\r
296 {\r
297 xDNSMessage_t *pxDNSMessageHeader;\r
298 uint32_t ulIPAddress = 0UL;\r
299 uint8_t *pucByte;\r
300 uint16_t x, usDataLength;\r
301 const uint16_t usARecordType = dnsTYPE;\r
302 \r
303         pxDNSMessageHeader = ( xDNSMessage_t * ) pucUDPPayloadBuffer;\r
304 \r
305         if( pxDNSMessageHeader->usIdentifier == usIdentifier )\r
306         {\r
307                 if( ( pxDNSMessageHeader->usFlags & dnsRX_FLAGS_MASK ) == dnsEXPECTED_RX_FLAGS )\r
308                 {\r
309                         /* Start at the first byte after the header. */\r
310                         pucByte = pucUDPPayloadBuffer + sizeof( xDNSMessage_t );\r
311 \r
312                         /* Skip any question records. */\r
313                         pxDNSMessageHeader->usQuestions = FreeRTOS_ntohs( pxDNSMessageHeader->usQuestions );\r
314                         for( x = 0; x < pxDNSMessageHeader->usQuestions; x++ )\r
315                         {\r
316                                 /* Skip the variable length name field. */\r
317                                 pucByte = prvSkipNameField( pucByte );\r
318 \r
319                                 /* Skip the type and class fields. */\r
320                                 pucByte += sizeof( uint32_t );\r
321                         }\r
322 \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
326                         {\r
327                                 pucByte = prvSkipNameField( pucByte );\r
328 \r
329                                 /* Is the type field that of an A record? */\r
330                                 if( memcmp( ( void * ) pucByte, ( void * ) &usARecordType, sizeof( uint16_t ) ) == 0 )\r
331                                 {\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
334                                         length. */\r
335                                         pucByte += ( sizeof( uint32_t ) + sizeof( uint32_t ) + sizeof( uint8_t ) );\r
336 \r
337                                         /* Sanity check the data length. */\r
338                                         if( *pucByte == sizeof( uint32_t ) )\r
339                                         {\r
340                                                 /* Skip the second byte of the length. */\r
341                                                 pucByte++;\r
342 \r
343                                                 /* Copy the IP address out of the record. */\r
344                                                 memcpy( ( void * ) &ulIPAddress, ( void * ) pucByte, sizeof( uint32_t ) );\r
345                                         }\r
346 \r
347                                         break;\r
348                                 }\r
349                                 else\r
350                                 {\r
351                                         /* Skip the type, class and time to live fields. */\r
352                                         pucByte += ( sizeof( uint32_t ) + sizeof( uint32_t ) );\r
353 \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
357 \r
358                                         /* Jump over the data lenth bytes, and the data itself. */\r
359                                         pucByte += usDataLength + sizeof( uint16_t );\r
360                                 }\r
361                         }\r
362                 }\r
363         }\r
364 \r
365         return ulIPAddress;\r
366 }\r
367 /*-----------------------------------------------------------*/\r
368 \r
369 static xSocket_t prvCreateDNSSocket( void )\r
370 {\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
375 \r
376         /* This must be the first time this function has been called.  Create\r
377         the socket. */\r
378         xSocket = FreeRTOS_socket( FREERTOS_AF_INET, FREERTOS_SOCK_DGRAM, FREERTOS_IPPROTO_UDP );\r
379 \r
380         /* Auto bind the port. */\r
381         xAddress.sin_port = 0;\r
382         xReturn = FreeRTOS_bind( xSocket, &xAddress, sizeof( xAddress ) );\r
383 \r
384         /* Check the bind was successful, and clean up if not. */\r
385         if( xReturn != 0 )\r
386         {\r
387                 FreeRTOS_closesocket( xSocket );\r
388                 xSocket = NULL;\r
389         }\r
390         else\r
391         {\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
395         }\r
396 \r
397         return xSocket;\r
398 }\r
399 \r
400 #endif /* ipconfigUSE_DNS != 0 */\r
401 \r
402 \r