]> git.sur5r.net Git - freertos/blob - FreeRTOS-Plus/Source/FreeRTOS-Plus-UDP/FreeRTOS_DNS.c
Add FreeRTOS-Plus directory with new directory structure so it matches the FreeRTOS...
[freertos] / FreeRTOS-Plus / Source / FreeRTOS-Plus-UDP / FreeRTOS_DNS.c
1 /*\r
2  * FreeRTOS+UDP V1.0.0 (C) 2013 Real Time Engineers ltd.\r
3  *\r
4  * FreeRTOS+UDP is an add-on component to FreeRTOS.  It is not, in itself, part\r
5  * of the FreeRTOS kernel.  FreeRTOS+UDP is licensed separately from FreeRTOS,\r
6  * and uses a different license to FreeRTOS.  FreeRTOS+UDP uses a dual license\r
7  * model, information on which is provided below:\r
8  *\r
9  * - Open source licensing -\r
10  * FreeRTOS+UDP is a free download and may be used, modified and distributed\r
11  * without charge provided the user adheres to version two of the GNU General\r
12  * Public license (GPL) and does not remove the copyright notice or this text.\r
13  * The GPL V2 text is available on the gnu.org web site, and on the following\r
14  * URL: http://www.FreeRTOS.org/gpl-2.0.txt\r
15  *\r
16  * - Commercial licensing -\r
17  * Businesses and individuals who wish to incorporate FreeRTOS+UDP into\r
18  * proprietary software for redistribution in any form must first obtain a\r
19  * (very) low cost commercial license - and in-so-doing support the maintenance,\r
20  * support and further development of the FreeRTOS+UDP product.  Commercial\r
21  * licenses can be obtained from http://shop.freertos.org and do not require any\r
22  * source files to be changed.\r
23  *\r
24  * FreeRTOS+UDP is distributed in the hope that it will be useful.  You cannot\r
25  * use FreeRTOS+UDP unless you agree that you use the software 'as is'.\r
26  * FreeRTOS+UDP is provided WITHOUT ANY WARRANTY; without even the implied\r
27  * warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A PARTICULAR\r
28  * PURPOSE. Real Time Engineers Ltd. disclaims all conditions and terms, be they\r
29  * implied, expressed, or statutory.\r
30  *\r
31  * 1 tab == 4 spaces!\r
32  *\r
33  * http://www.FreeRTOS.org\r
34  * http://www.FreeRTOS.org/udp\r
35  *\r
36  */\r
37 \r
38 /* Standard includes. */\r
39 #include <stdint.h>\r
40 \r
41 /* FreeRTOS includes. */\r
42 #include "FreeRTOS.h"\r
43 #include "task.h"\r
44 #include "queue.h"\r
45 #include "timers.h"\r
46 \r
47 /* FreeRTOS+UDP includes. */\r
48 #include "FreeRTOS_UDP_IP.h"\r
49 #include "FreeRTOS_IP_Private.h"\r
50 #include "FreeRTOS_DNS.h"\r
51 #include "FreeRTOS_Sockets.h"\r
52 #include "NetworkInterface.h"\r
53 #include "IPTraceMacroDefaults.h"\r
54 \r
55 /* Exclude the entire file if DNS is not enabled. */\r
56 #if ipconfigUSE_DNS != 0\r
57 \r
58 #if( ipconfigBYTE_ORDER == FREERTOS_LITTLE_ENDIAN )\r
59         #define dnsOUTGOING_FLAGS                               0x0001 /* Standard query. */\r
60         #define dnsTYPE                                                 0x0100 /* A record (host address. */\r
61         #define dnsCLASS                                                0x0100 /* IN */\r
62         #define dnsDNS_PORT                                             0x3500\r
63         #define dnsONE_QUESTION                                 0x0100\r
64         #define dnsRX_FLAGS_MASK                                0x0f80 /* The bits of interest in the flags field of incoming DNS messages. */\r
65         #define dnsEXPECTED_RX_FLAGS                    0x0080 /* Should be a response, without any errors. */\r
66 #else\r
67         #define dnsDNS_PORT                                             0x35\r
68         #define dnsONE_QUESTION                                 0x01\r
69         #define dnsFLAG_QUERY_RESPONSE_BIT              0x8000\r
70         #define dnsFLAG_OPERATION_CODE_BITS             0x7800\r
71         #define dnsFLAG_TRUNCATION_BIT                  0x0200\r
72         #define dnsFLAG_RESPONSE_CODE_BITS              0x000f\r
73         #define dnsOUTGOING_FLAGS                               0x0100 /* Standard query. */\r
74         #define dnsTYPE                                                 0x0001 /* A record (host address. */\r
75         #define dnsCLASS                                                0x0001 /* IN */\r
76         #define dnsRX_FLAGS_MASK                                0x800f /* The bits of interest in the flags field of incoming DNS messages. */\r
77         #define dnsEXPECTED_RX_FLAGS                    0x8000 /* Should be a response, without any errors. */\r
78 #endif /* ipconfigBYTE_ORDER */\r
79 \r
80 /* The maximum number of times a DNS request should be sent out if a response\r
81 is not received, before giving up. */\r
82 #define dnsMAX_REQUEST_ATTEMPTS         5\r
83 \r
84 /* If the top two bits in the first character of a name field are set then the\r
85 name field is an offset to the string, rather than the string itself. */\r
86 #define dnsNAME_IS_OFFSET                       ( ( uint8_t ) 0xc0 )\r
87 \r
88 /*\r
89  * Create a socket and bind it to the standard DNS port number.  Return the\r
90  * the created socket - or NULL if the socket could not be created or bound.\r
91  */\r
92 static xSocket_t prvCreateDNSSocket( void );\r
93 \r
94 /*\r
95  * Create the DNS message in the zero copy buffer passed in the first parameter.\r
96  */\r
97 static size_t prvCreateDNSMessage( uint8_t *pucUDPPayloadBuffer, const uint8_t *pcHostName, uint16_t usIdentifier );\r
98 \r
99 /*\r
100  * Simple routine that jumps over the NAME field of a resource record.\r
101  */\r
102 static uint8_t *prvSkipNameField( uint8_t *pucByte );\r
103 \r
104 /*\r
105  * Process a response packet from a DNS server.\r
106  */\r
107 static uint32_t prvParseDNSReply( uint8_t *pucUDPPayloadBuffer, uint16_t usIdentifier );\r
108 \r
109 /*-----------------------------------------------------------*/\r
110 \r
111 #include "pack_struct_start.h"\r
112 struct xDNSMessage\r
113 {\r
114         uint16_t usIdentifier;\r
115         uint16_t usFlags;\r
116         uint16_t usQuestions;\r
117         uint16_t usAnswers;\r
118         uint16_t usAuthorityRRs;\r
119         uint16_t usAdditionalRRs;\r
120 }\r
121 #include "pack_struct_end.h"\r
122 typedef struct xDNSMessage xDNSMessage_t;\r
123 \r
124 /*-----------------------------------------------------------*/\r
125 \r
126 uint32_t FreeRTOS_gethostbyname( const uint8_t *pcHostName )\r
127 {\r
128 static uint16_t usIdentifier = 0;\r
129 struct freertos_sockaddr xAddress;\r
130 static xSocket_t xDNSSocket = NULL;\r
131 uint32_t ulIPAddress = 0UL;\r
132 uint8_t *pucUDPPayloadBuffer;\r
133 static uint32_t ulAddressLength;\r
134 portBASE_TYPE xAttempt;\r
135 int32_t lBytes;\r
136 size_t xPayloadLength;\r
137 const size_t xExpectedPayloadLength = sizeof( xDNSMessage_t ) + strlen( ( const char * const ) 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
138 \r
139         if( xDNSSocket == NULL )\r
140         {\r
141                 xDNSSocket = prvCreateDNSSocket();\r
142         }\r
143 \r
144         if( xDNSSocket != NULL )\r
145         {\r
146                 /* Generate a unique identifier for this query. */\r
147                 usIdentifier++;\r
148 \r
149                 for( xAttempt = 0; xAttempt < dnsMAX_REQUEST_ATTEMPTS; xAttempt++ )\r
150                 {\r
151                         /* Get a buffer.  This uses a maximum delay, but the delay will be\r
152                         capped to ipconfigMAX_SEND_BLOCK_TIME_TICKS so the return value\r
153                         still needs to be tested. */\r
154                         pucUDPPayloadBuffer = ( uint8_t * ) FreeRTOS_GetUDPPayloadBuffer( xExpectedPayloadLength, portMAX_DELAY );\r
155                         if( pucUDPPayloadBuffer != NULL )\r
156                         {\r
157                                 /* Create the message in the obtained buffer. */\r
158                                 xPayloadLength = prvCreateDNSMessage( pucUDPPayloadBuffer, pcHostName, usIdentifier );\r
159                                 iptraceSENDING_DNS_REQUEST();\r
160 \r
161                                 /* Obtain the DNS server address. */\r
162                                 FreeRTOS_GetAddressConfiguration( NULL, NULL, NULL, &ulIPAddress );\r
163 \r
164                                 /* Send the DNS message. */\r
165                                 xAddress.sin_addr = ulIPAddress;\r
166                                 xAddress.sin_port = dnsDNS_PORT;\r
167                                 ulIPAddress = 0;\r
168 \r
169                                 if( FreeRTOS_sendto( xDNSSocket, pucUDPPayloadBuffer, xPayloadLength, FREERTOS_ZERO_COPY, &xAddress, sizeof( xAddress ) ) != 0 )\r
170                                 {\r
171                                         /* Wait for the reply. */\r
172                                         lBytes = FreeRTOS_recvfrom( xDNSSocket, &pucUDPPayloadBuffer, 0, FREERTOS_ZERO_COPY, &xAddress, &ulAddressLength );\r
173 \r
174                                         if( lBytes > 0 )\r
175                                         {\r
176                                                 /* The reply was received.  Process it. */\r
177                                                 ulIPAddress = prvParseDNSReply( pucUDPPayloadBuffer, usIdentifier );\r
178 \r
179                                                 /* Finished with the buffer.  The zero copy interface\r
180                                                 is being used, so the buffer must be freed by the\r
181                                                 task. */\r
182                                                 FreeRTOS_ReleaseUDPPayloadBuffer( ( void * ) pucUDPPayloadBuffer );\r
183 \r
184                                                 if( ulIPAddress != 0 )\r
185                                                 {\r
186                                                         /* All done. */\r
187                                                         break;\r
188                                                 }\r
189                                         }\r
190                                 }\r
191                                 else\r
192                                 {\r
193                                         /* The message was not sent so the stack will not be\r
194                                         releasing the zero copy - it must be released here. */\r
195                                         FreeRTOS_ReleaseUDPPayloadBuffer( ( void * ) pucUDPPayloadBuffer );\r
196                                 }\r
197                         }\r
198                 }\r
199         }\r
200 \r
201         return ulIPAddress;\r
202 }\r
203 /*-----------------------------------------------------------*/\r
204 \r
205 static size_t prvCreateDNSMessage( uint8_t *pucUDPPayloadBuffer, const uint8_t *pcHostName, uint16_t usIdentifier )\r
206 {\r
207 xDNSMessage_t *pxDNSMessageHeader;\r
208 uint8_t *pucStart, *pucByte;\r
209 const uint16_t usARecordType = dnsTYPE, usClass = dnsCLASS;\r
210 static const xDNSMessage_t xDefaultPartDNSHeader =\r
211 {\r
212         0,                                      /* The identifier will be overwritten. */\r
213         dnsOUTGOING_FLAGS,      /* Flags set for standard query. */\r
214         dnsONE_QUESTION,        /* One question is being asked. */\r
215         0,                                      /* No replies are included. */\r
216         0,                                      /* No authorities. */\r
217         0                                       /* No additional authorities. */\r
218 };\r
219 \r
220         /* Copy in the const part of the header. */\r
221         memcpy( ( void * ) pucUDPPayloadBuffer, ( void * ) &xDefaultPartDNSHeader, sizeof( xDefaultPartDNSHeader ) );\r
222 \r
223         /* Write in a unique identifier. */\r
224         pxDNSMessageHeader = ( xDNSMessage_t * ) pucUDPPayloadBuffer;\r
225         pxDNSMessageHeader->usIdentifier = usIdentifier;\r
226 \r
227         /* Create the resource record at the end of the header.  First\r
228         find the end of the header. */\r
229         pucStart = pucUDPPayloadBuffer + sizeof( xDefaultPartDNSHeader );\r
230 \r
231         /* Leave a gap for the first length bytes. */\r
232         pucByte = pucStart + 1;\r
233 \r
234         /* Copy in the host name. */\r
235         strcpy( ( char * ) pucByte, ( const char * ) pcHostName );\r
236 \r
237         /* Mark the end of the string. */\r
238         pucByte += strlen( ( const char * ) pcHostName );\r
239         *pucByte = 0x00;\r
240 \r
241         /* Walk the string to replace the '.' characters with byte counts.\r
242         pucStart holds the address of the byte count.  Walking the string\r
243         starts after the byte count position. */\r
244         pucByte = pucStart;\r
245 \r
246         do\r
247         {\r
248                 pucByte++;\r
249 \r
250                 while( ( *pucByte != 0x00 ) && ( *pucByte != '.' ) )\r
251                 {\r
252                         pucByte++;\r
253                 }\r
254 \r
255                 /* Fill in the byte count, then move the pucStart pointer up to\r
256                 the found byte position. */\r
257                 *pucStart = ( uint8_t ) ( ( uint32_t ) pucByte - ( uint32_t ) pucStart );\r
258                 ( *pucStart )--;\r
259 \r
260                 pucStart = pucByte;\r
261 \r
262         } while( *pucByte != 0x00 );\r
263 \r
264         /* Finish off the record. */\r
265         pucByte++;\r
266         memcpy( ( void * ) pucByte, &usARecordType, sizeof( uint16_t ) );\r
267         pucByte += sizeof( uint16_t );\r
268         memcpy( ( void * ) pucByte, &usClass, sizeof( uint16_t ) );\r
269         pucByte += sizeof( uint16_t );\r
270 \r
271         /* Return the total size of the generated message, which is the space from\r
272         the last written byte to the beginning of the buffer. */\r
273         return ( ( uint32_t ) pucByte - ( uint32_t ) pucUDPPayloadBuffer );\r
274 }\r
275 /*-----------------------------------------------------------*/\r
276 \r
277 static uint8_t *prvSkipNameField( uint8_t *pucByte )\r
278 {\r
279         /* Determine if the name is the fully coded name, or an offset to the name\r
280         elsewhere in the message. */\r
281         if( ( *pucByte & dnsNAME_IS_OFFSET ) == dnsNAME_IS_OFFSET )\r
282         {\r
283                 /* Jump over the two byte offset. */\r
284                 pucByte += sizeof( uint16_t );\r
285 \r
286         }\r
287         else\r
288         {\r
289                 /* pucByte points to the full name.  Walk over the string. */\r
290                 while( *pucByte != 0x00 )\r
291                 {\r
292                         /* The number of bytes to jump for each name section is stored in the byte\r
293                         before the name section. */\r
294                         pucByte += ( *pucByte + 1 );\r
295                 }\r
296 \r
297                 pucByte++;\r
298         }\r
299 \r
300         return pucByte;\r
301 }\r
302 /*-----------------------------------------------------------*/\r
303 \r
304 static uint32_t prvParseDNSReply( uint8_t *pucUDPPayloadBuffer, uint16_t usIdentifier )\r
305 {\r
306 xDNSMessage_t *pxDNSMessageHeader;\r
307 uint32_t ulIPAddress = 0UL;\r
308 uint8_t *pucByte;\r
309 uint16_t x, usDataLength;\r
310 const uint16_t usARecordType = dnsTYPE;\r
311 \r
312         pxDNSMessageHeader = ( xDNSMessage_t * ) pucUDPPayloadBuffer;\r
313 \r
314         if( pxDNSMessageHeader->usIdentifier == usIdentifier )\r
315         {\r
316                 if( ( pxDNSMessageHeader->usFlags & dnsRX_FLAGS_MASK ) == dnsEXPECTED_RX_FLAGS )\r
317                 {\r
318                         /* Start at the first byte after the header. */\r
319                         pucByte = pucUDPPayloadBuffer + sizeof( xDNSMessage_t );\r
320 \r
321                         /* Skip any question records. */\r
322                         pxDNSMessageHeader->usQuestions = FreeRTOS_ntohs( pxDNSMessageHeader->usQuestions );\r
323                         for( x = 0; x < pxDNSMessageHeader->usQuestions; x++ )\r
324                         {\r
325                                 /* Skip the variable length name field. */\r
326                                 pucByte = prvSkipNameField( pucByte );\r
327 \r
328                                 /* Skip the type and class fields. */\r
329                                 pucByte += sizeof( uint32_t );\r
330                         }\r
331 \r
332                         /* Search through the answers records. */\r
333                         pxDNSMessageHeader->usAnswers = FreeRTOS_ntohs( pxDNSMessageHeader->usAnswers );\r
334                         for( x = 0; x < pxDNSMessageHeader->usAnswers; x++ )\r
335                         {\r
336                                 pucByte = prvSkipNameField( pucByte );\r
337 \r
338                                 /* Is the type field that of an A record? */\r
339                                 if( memcmp( ( void * ) pucByte, ( void * ) &usARecordType, sizeof( uint16_t ) ) == 0 )\r
340                                 {\r
341                                         /* This is the required record.  Skip the type, class, and\r
342                                         time to live fields, plus the first byte of the data\r
343                                         length. */\r
344                                         pucByte += ( sizeof( uint32_t ) + sizeof( uint32_t ) + sizeof( uint8_t ) );\r
345 \r
346                                         /* Sanity check the data length. */\r
347                                         if( *pucByte == sizeof( uint32_t ) )\r
348                                         {\r
349                                                 /* Skip the second byte of the length. */\r
350                                                 pucByte++;\r
351 \r
352                                                 /* Copy the IP address out of the record. */\r
353                                                 memcpy( ( void * ) &ulIPAddress, ( void * ) pucByte, sizeof( uint32_t ) );\r
354                                         }\r
355 \r
356                                         break;\r
357                                 }\r
358                                 else\r
359                                 {\r
360                                         /* Skip the type, class and time to live fields. */\r
361                                         pucByte += ( sizeof( uint32_t ) + sizeof( uint32_t ) );\r
362 \r
363                                         /* Determine the length of the data in the field. */\r
364                                         memcpy( ( void * ) &usDataLength, ( void * ) pucByte, sizeof( uint16_t ) );\r
365                                         usDataLength = FreeRTOS_ntohs( usDataLength );\r
366 \r
367                                         /* Jump over the data lenth bytes, and the data itself. */\r
368                                         pucByte += usDataLength + sizeof( uint16_t );\r
369                                 }\r
370                         }\r
371                 }\r
372         }\r
373 \r
374         return ulIPAddress;\r
375 }\r
376 /*-----------------------------------------------------------*/\r
377 \r
378 static xSocket_t prvCreateDNSSocket( void )\r
379 {\r
380 static xSocket_t xSocket = NULL;\r
381 struct freertos_sockaddr xAddress;\r
382 portBASE_TYPE xReturn;\r
383 portTickType xTimeoutTime = 200 / portTICK_RATE_MS;\r
384 \r
385         /* This must be the first time this function has been called.  Create\r
386         the socket. */\r
387         xSocket = FreeRTOS_socket( FREERTOS_AF_INET, FREERTOS_SOCK_DGRAM, FREERTOS_IPPROTO_UDP );\r
388 \r
389         /* Auto bind the port. */\r
390         xAddress.sin_port = 0;\r
391         xReturn = FreeRTOS_bind( xSocket, &xAddress, sizeof( xAddress ) );\r
392 \r
393         /* Check the bind was successful, and clean up if not. */\r
394         if( xReturn != 0 )\r
395         {\r
396                 FreeRTOS_closesocket( xSocket );\r
397                 xSocket = NULL;\r
398         }\r
399         else\r
400         {\r
401                 /* Set the send and receive timeouts. */\r
402                 FreeRTOS_setsockopt( xSocket, 0, FREERTOS_SO_RCVTIMEO, ( void * ) &xTimeoutTime, sizeof( portTickType ) );\r
403                 FreeRTOS_setsockopt( xSocket, 0, FREERTOS_SO_SNDTIMEO, ( void * ) &xTimeoutTime, sizeof( portTickType ) );\r
404         }\r
405 \r
406         return xSocket;\r
407 }\r
408 \r
409 #endif /* ipconfigUSE_DNS != 0 */\r
410 \r
411 \r