]> git.sur5r.net Git - freertos/blob - FreeRTOS-Plus/Source/FreeRTOS-Plus-UDP/FreeRTOS_DNS.c
f8da6b7d55ae0c60fac88ba6fe010ca635444b62
[freertos] / FreeRTOS-Plus / Source / FreeRTOS-Plus-UDP / FreeRTOS_DNS.c
1 /*\r
2  * FreeRTOS+UDP V1.0.3 (C) 2014 Real Time Engineers ltd.\r
3  * All rights reserved\r
4  *\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
7  *\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
14  *\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
21  *\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
28  *\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
35  *\r
36  * 1 tab == 4 spaces!\r
37  *\r
38  * http://www.FreeRTOS.org\r
39  * http://www.FreeRTOS.org/udp\r
40  *\r
41  */\r
42 \r
43 /* Standard includes. */\r
44 #include <stdint.h>\r
45 \r
46 /* FreeRTOS includes. */\r
47 #include "FreeRTOS.h"\r
48 #include "task.h"\r
49 #include "queue.h"\r
50 #include "timers.h"\r
51 \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
59 \r
60 /* Exclude the entire file if DNS is not enabled. */\r
61 #if ipconfigUSE_DNS != 0\r
62 \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
71 #else\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
84 \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
88 \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
92 \r
93 /*\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
96  */\r
97 static xSocket_t prvCreateDNSSocket( void );\r
98 \r
99 /*\r
100  * Create the DNS message in the zero copy buffer passed in the first parameter.\r
101  */\r
102 static size_t prvCreateDNSMessage( uint8_t *pucUDPPayloadBuffer, const char *pcHostName, uint16_t usIdentifier );\r
103 \r
104 /*\r
105  * Simple routine that jumps over the NAME field of a resource record.\r
106  */\r
107 static uint8_t *prvSkipNameField( uint8_t *pucByte );\r
108 \r
109 /*\r
110  * Process a response packet from a DNS server.\r
111  */\r
112 static uint32_t prvParseDNSReply( uint8_t *pucUDPPayloadBuffer, uint16_t usIdentifier );\r
113 \r
114 /*-----------------------------------------------------------*/\r
115 \r
116 #include "pack_struct_start.h"\r
117 struct xDNSMessage\r
118 {\r
119         uint16_t usIdentifier;\r
120         uint16_t usFlags;\r
121         uint16_t usQuestions;\r
122         uint16_t usAnswers;\r
123         uint16_t usAuthorityRRs;\r
124         uint16_t usAdditionalRRs;\r
125 }\r
126 #include "pack_struct_end.h"\r
127 typedef struct xDNSMessage xDNSMessage_t;\r
128 \r
129 /*-----------------------------------------------------------*/\r
130 \r
131 uint32_t FreeRTOS_gethostbyname( const char *pcHostName )\r
132 {\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
140 int32_t lBytes;\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
143 \r
144         if( xDNSSocket == NULL )\r
145         {\r
146                 xDNSSocket = prvCreateDNSSocket();\r
147         }\r
148 \r
149         if( xDNSSocket != NULL )\r
150         {\r
151                 /* Generate a unique identifier for this query. */\r
152                 usIdentifier++;\r
153 \r
154                 for( xAttempt = 0; xAttempt < dnsMAX_REQUEST_ATTEMPTS; xAttempt++ )\r
155                 {\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
161                         {\r
162                                 /* Create the message in the obtained buffer. */\r
163                                 xPayloadLength = prvCreateDNSMessage( pucUDPPayloadBuffer, pcHostName, usIdentifier );\r
164                                 iptraceSENDING_DNS_REQUEST();\r
165 \r
166                                 /* Obtain the DNS server address. */\r
167                                 FreeRTOS_GetAddressConfiguration( NULL, NULL, NULL, &ulIPAddress );\r
168 \r
169                                 /* Send the DNS message. */\r
170                                 xAddress.sin_addr = ulIPAddress;\r
171                                 xAddress.sin_port = dnsDNS_PORT;\r
172                                 ulIPAddress = 0;\r
173 \r
174                                 if( FreeRTOS_sendto( xDNSSocket, pucUDPPayloadBuffer, xPayloadLength, FREERTOS_ZERO_COPY, &xAddress, sizeof( xAddress ) ) != 0 )\r
175                                 {\r
176                                         /* Wait for the reply. */\r
177                                         lBytes = FreeRTOS_recvfrom( xDNSSocket, &pucUDPPayloadBuffer, 0, FREERTOS_ZERO_COPY, &xAddress, &ulAddressLength );\r
178 \r
179                                         if( lBytes > 0 )\r
180                                         {\r
181                                                 /* The reply was received.  Process it. */\r
182                                                 ulIPAddress = prvParseDNSReply( pucUDPPayloadBuffer, usIdentifier );\r
183 \r
184                                                 /* Finished with the buffer.  The zero copy interface\r
185                                                 is being used, so the buffer must be freed by the\r
186                                                 task. */\r
187                                                 FreeRTOS_ReleaseUDPPayloadBuffer( ( void * ) pucUDPPayloadBuffer );\r
188 \r
189                                                 if( ulIPAddress != 0 )\r
190                                                 {\r
191                                                         /* All done. */\r
192                                                         break;\r
193                                                 }\r
194                                         }\r
195                                 }\r
196                                 else\r
197                                 {\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
201                                 }\r
202                         }\r
203                 }\r
204         }\r
205 \r
206         return ulIPAddress;\r
207 }\r
208 /*-----------------------------------------------------------*/\r
209 \r
210 static size_t prvCreateDNSMessage( uint8_t *pucUDPPayloadBuffer, const char *pcHostName, uint16_t usIdentifier )\r
211 {\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
216 {\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
223 };\r
224 \r
225         /* Copy in the const part of the header. */\r
226         memcpy( ( void * ) pucUDPPayloadBuffer, ( void * ) &xDefaultPartDNSHeader, sizeof( xDefaultPartDNSHeader ) );\r
227 \r
228         /* Write in a unique identifier. */\r
229         pxDNSMessageHeader = ( xDNSMessage_t * ) pucUDPPayloadBuffer;\r
230         pxDNSMessageHeader->usIdentifier = usIdentifier;\r
231 \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
235 \r
236         /* Leave a gap for the first length bytes. */\r
237         pucByte = pucStart + 1;\r
238 \r
239         /* Copy in the host name. */\r
240         strcpy( ( char * ) pucByte, pcHostName );\r
241 \r
242         /* Mark the end of the string. */\r
243         pucByte += strlen( pcHostName );\r
244         *pucByte = 0x00;\r
245 \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
250 \r
251         do\r
252         {\r
253                 pucByte++;\r
254 \r
255                 while( ( *pucByte != 0x00 ) && ( *pucByte != '.' ) )\r
256                 {\r
257                         pucByte++;\r
258                 }\r
259 \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
263                 ( *pucStart )--;\r
264 \r
265                 pucStart = pucByte;\r
266 \r
267         } while( *pucByte != 0x00 );\r
268 \r
269         /* Finish off the record. */\r
270         pucByte++;\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
275 \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
279 }\r
280 /*-----------------------------------------------------------*/\r
281 \r
282 static uint8_t *prvSkipNameField( uint8_t *pucByte )\r
283 {\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
287         {\r
288                 /* Jump over the two byte offset. */\r
289                 pucByte += sizeof( uint16_t );\r
290 \r
291         }\r
292         else\r
293         {\r
294                 /* pucByte points to the full name.  Walk over the string. */\r
295                 while( *pucByte != 0x00 )\r
296                 {\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
300                 }\r
301 \r
302                 pucByte++;\r
303         }\r
304 \r
305         return pucByte;\r
306 }\r
307 /*-----------------------------------------------------------*/\r
308 \r
309 static uint32_t prvParseDNSReply( uint8_t *pucUDPPayloadBuffer, uint16_t usIdentifier )\r
310 {\r
311 xDNSMessage_t *pxDNSMessageHeader;\r
312 uint32_t ulIPAddress = 0UL;\r
313 uint8_t *pucByte;\r
314 uint16_t x, usDataLength;\r
315 const uint16_t usARecordType = dnsTYPE;\r
316 \r
317         pxDNSMessageHeader = ( xDNSMessage_t * ) pucUDPPayloadBuffer;\r
318 \r
319         if( pxDNSMessageHeader->usIdentifier == usIdentifier )\r
320         {\r
321                 if( ( pxDNSMessageHeader->usFlags & dnsRX_FLAGS_MASK ) == dnsEXPECTED_RX_FLAGS )\r
322                 {\r
323                         /* Start at the first byte after the header. */\r
324                         pucByte = pucUDPPayloadBuffer + sizeof( xDNSMessage_t );\r
325 \r
326                         /* Skip any question records. */\r
327                         pxDNSMessageHeader->usQuestions = FreeRTOS_ntohs( pxDNSMessageHeader->usQuestions );\r
328                         for( x = 0; x < pxDNSMessageHeader->usQuestions; x++ )\r
329                         {\r
330                                 /* Skip the variable length name field. */\r
331                                 pucByte = prvSkipNameField( pucByte );\r
332 \r
333                                 /* Skip the type and class fields. */\r
334                                 pucByte += sizeof( uint32_t );\r
335                         }\r
336 \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
340                         {\r
341                                 pucByte = prvSkipNameField( pucByte );\r
342 \r
343                                 /* Is the type field that of an A record? */\r
344                                 if( memcmp( ( void * ) pucByte, ( void * ) &usARecordType, sizeof( uint16_t ) ) == 0 )\r
345                                 {\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
348                                         length. */\r
349                                         pucByte += ( sizeof( uint32_t ) + sizeof( uint32_t ) + sizeof( uint8_t ) );\r
350 \r
351                                         /* Sanity check the data length. */\r
352                                         if( *pucByte == sizeof( uint32_t ) )\r
353                                         {\r
354                                                 /* Skip the second byte of the length. */\r
355                                                 pucByte++;\r
356 \r
357                                                 /* Copy the IP address out of the record. */\r
358                                                 memcpy( ( void * ) &ulIPAddress, ( void * ) pucByte, sizeof( uint32_t ) );\r
359                                         }\r
360 \r
361                                         break;\r
362                                 }\r
363                                 else\r
364                                 {\r
365                                         /* Skip the type, class and time to live fields. */\r
366                                         pucByte += ( sizeof( uint32_t ) + sizeof( uint32_t ) );\r
367 \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
371 \r
372                                         /* Jump over the data lenth bytes, and the data itself. */\r
373                                         pucByte += usDataLength + sizeof( uint16_t );\r
374                                 }\r
375                         }\r
376                 }\r
377         }\r
378 \r
379         return ulIPAddress;\r
380 }\r
381 /*-----------------------------------------------------------*/\r
382 \r
383 static xSocket_t prvCreateDNSSocket( void )\r
384 {\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
389 \r
390         /* This must be the first time this function has been called.  Create\r
391         the socket. */\r
392         xSocket = FreeRTOS_socket( FREERTOS_AF_INET, FREERTOS_SOCK_DGRAM, FREERTOS_IPPROTO_UDP );\r
393 \r
394         /* Auto bind the port. */\r
395         xAddress.sin_port = 0;\r
396         xReturn = FreeRTOS_bind( xSocket, &xAddress, sizeof( xAddress ) );\r
397 \r
398         /* Check the bind was successful, and clean up if not. */\r
399         if( xReturn != 0 )\r
400         {\r
401                 FreeRTOS_closesocket( xSocket );\r
402                 xSocket = NULL;\r
403         }\r
404         else\r
405         {\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
409         }\r
410 \r
411         return xSocket;\r
412 }\r
413 \r
414 #endif /* ipconfigUSE_DNS != 0 */\r
415 \r
416 \r