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