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