source: rtos_arduino/trunk/arduino_lib/libraries/Ethernet2/src/Dns.cpp@ 136

Last change on this file since 136 was 136, checked in by ertl-honda, 8 years ago

ライブラリとOS及びベーシックなサンプルの追加.

File size: 13.1 KB
RevLine 
[136]1// Arduino DNS client for WizNet5100-based Ethernet shield
2// (c) Copyright 2009-2010 MCQN Ltd.
3// Released under Apache License, version 2.0
4
5#include "utility/w5500.h"
6#include "EthernetUdp2.h"
7#include "utility/util.h"
8
9#include "Dns.h"
10#include <string.h>
11//#include <stdlib.h>
12#include "Arduino.h"
13
14
15#define SOCKET_NONE 255
16// Various flags and header field values for a DNS message
17#define UDP_HEADER_SIZE 8
18#define DNS_HEADER_SIZE 12
19#define TTL_SIZE 4
20#define QUERY_FLAG (0)
21#define RESPONSE_FLAG (1<<15)
22#define QUERY_RESPONSE_MASK (1<<15)
23#define OPCODE_STANDARD_QUERY (0)
24#define OPCODE_INVERSE_QUERY (1<<11)
25#define OPCODE_STATUS_REQUEST (2<<11)
26#define OPCODE_MASK (15<<11)
27#define AUTHORITATIVE_FLAG (1<<10)
28#define TRUNCATION_FLAG (1<<9)
29#define RECURSION_DESIRED_FLAG (1<<8)
30#define RECURSION_AVAILABLE_FLAG (1<<7)
31#define RESP_NO_ERROR (0)
32#define RESP_FORMAT_ERROR (1)
33#define RESP_SERVER_FAILURE (2)
34#define RESP_NAME_ERROR (3)
35#define RESP_NOT_IMPLEMENTED (4)
36#define RESP_REFUSED (5)
37#define RESP_MASK (15)
38#define TYPE_A (0x0001)
39#define CLASS_IN (0x0001)
40#define LABEL_COMPRESSION_MASK (0xC0)
41// Port number that DNS servers listen on
42#define DNS_PORT 53
43
44// Possible return codes from ProcessResponse
45#define SUCCESS 1
46#define TIMED_OUT -1
47#define INVALID_SERVER -2
48#define TRUNCATED -3
49#define INVALID_RESPONSE -4
50
51void DNSClient::begin(const IPAddress& aDNSServer)
52{
53 iDNSServer = aDNSServer;
54 iRequestId = 0;
55}
56
57
58int DNSClient::inet_aton(const char* aIPAddrString, IPAddress& aResult)
59{
60 // See if we've been given a valid IP address
61 const char* p =aIPAddrString;
62 while (*p &&
63 ( (*p == '.') || (*p >= '0') || (*p <= '9') ))
64 {
65 p++;
66 }
67
68 if (*p == '\0')
69 {
70 // It's looking promising, we haven't found any invalid characters
71 p = aIPAddrString;
72 int segment =0;
73 int segmentValue =0;
74 while (*p && (segment < 4))
75 {
76 if (*p == '.')
77 {
78 // We've reached the end of a segment
79 if (segmentValue > 255)
80 {
81 // You can't have IP address segments that don't fit in a byte
82 return 0;
83 }
84 else
85 {
86 aResult[segment] = (byte)segmentValue;
87 segment++;
88 segmentValue = 0;
89 }
90 }
91 else
92 {
93 // Next digit
94 segmentValue = (segmentValue*10)+(*p - '0');
95 }
96 p++;
97 }
98 // We've reached the end of address, but there'll still be the last
99 // segment to deal with
100 if ((segmentValue > 255) || (segment > 3))
101 {
102 // You can't have IP address segments that don't fit in a byte,
103 // or more than four segments
104 return 0;
105 }
106 else
107 {
108 aResult[segment] = (byte)segmentValue;
109 return 1;
110 }
111 }
112 else
113 {
114 return 0;
115 }
116}
117
118int DNSClient::getHostByName(const char* aHostname, IPAddress& aResult)
119{
120 int ret =0;
121
122 // See if it's a numeric IP address
123 if (inet_aton(aHostname, aResult))
124 {
125 // It is, our work here is done
126 return 1;
127 }
128
129 // Check we've got a valid DNS server to use
130 if (iDNSServer == INADDR_NONE)
131 {
132 return INVALID_SERVER;
133 }
134
135 // Find a socket to use
136 if (iUdp.begin(1024+(millis() & 0xF)) == 1)
137 {
138 // Try up to three times
139 int retries = 0;
140// while ((retries < 3) && (ret <= 0))
141 {
142 // Send DNS request
143 ret = iUdp.beginPacket(iDNSServer, DNS_PORT);
144 if (ret != 0)
145 {
146 // Now output the request data
147 ret = BuildRequest(aHostname);
148 if (ret != 0)
149 {
150 // And finally send the request
151 ret = iUdp.endPacket();
152 if (ret != 0)
153 {
154 // Now wait for a response
155 int wait_retries = 0;
156 ret = TIMED_OUT;
157 while ((wait_retries < 3) && (ret == TIMED_OUT))
158 {
159 ret = ProcessResponse(5000, aResult);
160 wait_retries++;
161 }
162 }
163 }
164 }
165 retries++;
166 }
167
168 // We're done with the socket now
169 iUdp.stop();
170 }
171
172 return ret;
173}
174
175uint16_t DNSClient::BuildRequest(const char* aName)
176{
177 // Build header
178 // 1 1 1 1 1 1
179 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
180 // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
181 // | ID |
182 // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
183 // |QR| Opcode |AA|TC|RD|RA| Z | RCODE |
184 // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
185 // | QDCOUNT |
186 // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
187 // | ANCOUNT |
188 // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
189 // | NSCOUNT |
190 // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
191 // | ARCOUNT |
192 // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
193 // As we only support one request at a time at present, we can simplify
194 // some of this header
195 iRequestId = millis(); // generate a random ID
196 uint16_t twoByteBuffer;
197
198 // FIXME We should also check that there's enough space available to write to, rather
199 // FIXME than assume there's enough space (as the code does at present)
200 iUdp.write((uint8_t*)&iRequestId, sizeof(iRequestId));
201
202 twoByteBuffer = htons(QUERY_FLAG | OPCODE_STANDARD_QUERY | RECURSION_DESIRED_FLAG);
203 iUdp.write((uint8_t*)&twoByteBuffer, sizeof(twoByteBuffer));
204
205 twoByteBuffer = htons(1); // One question record
206 iUdp.write((uint8_t*)&twoByteBuffer, sizeof(twoByteBuffer));
207
208 twoByteBuffer = 0; // Zero answer records
209 iUdp.write((uint8_t*)&twoByteBuffer, sizeof(twoByteBuffer));
210
211 iUdp.write((uint8_t*)&twoByteBuffer, sizeof(twoByteBuffer));
212 // and zero additional records
213 iUdp.write((uint8_t*)&twoByteBuffer, sizeof(twoByteBuffer));
214
215 // Build question
216 const char* start =aName;
217 const char* end =start;
218 uint8_t len;
219 // Run through the name being requested
220 while (*end)
221 {
222 // Find out how long this section of the name is
223 end = start;
224 while (*end && (*end != '.') )
225 {
226 end++;
227 }
228
229 if (end-start > 0)
230 {
231 // Write out the size of this section
232 len = end-start;
233 iUdp.write(&len, sizeof(len));
234 // And then write out the section
235 iUdp.write((uint8_t*)start, end-start);
236 }
237 start = end+1;
238 }
239
240 // We've got to the end of the question name, so
241 // terminate it with a zero-length section
242 len = 0;
243 iUdp.write(&len, sizeof(len));
244 // Finally the type and class of question
245 twoByteBuffer = htons(TYPE_A);
246 iUdp.write((uint8_t*)&twoByteBuffer, sizeof(twoByteBuffer));
247
248 twoByteBuffer = htons(CLASS_IN); // Internet class of question
249 iUdp.write((uint8_t*)&twoByteBuffer, sizeof(twoByteBuffer));
250 // Success! Everything buffered okay
251 return 1;
252}
253
254
255uint16_t DNSClient::ProcessResponse(uint16_t aTimeout, IPAddress& aAddress)
256{
257 uint32_t startTime = millis();
258
259 // Wait for a response packet
260 while(iUdp.parsePacket() <= 0)
261 {
262 if((millis() - startTime) > aTimeout)
263 return TIMED_OUT;
264 delay(50);
265 }
266
267 // We've had a reply!
268 // Read the UDP header
269 uint8_t header[DNS_HEADER_SIZE]; // Enough space to reuse for the DNS header
270 // Check that it's a response from the right server and the right port
271 if ( (iDNSServer != iUdp.remoteIP()) ||
272 (iUdp.remotePort() != DNS_PORT) )
273 {
274 // It's not from who we expected
275 return INVALID_SERVER;
276 }
277
278 // Read through the rest of the response
279 if (iUdp.available() < DNS_HEADER_SIZE)
280 {
281 return TRUNCATED;
282 }
283 iUdp.read(header, DNS_HEADER_SIZE);
284
285 uint16_t header_flags = htons(*((uint16_t*)&header[2]));
286 // Check that it's a response to this request
287 if ( ( iRequestId != (*((uint16_t*)&header[0])) ) ||
288 ((header_flags & QUERY_RESPONSE_MASK) != (uint16_t)RESPONSE_FLAG) )
289 {
290 // Mark the entire packet as read
291 iUdp.flush();
292 return INVALID_RESPONSE;
293 }
294 // Check for any errors in the response (or in our request)
295 // although we don't do anything to get round these
296 if ( (header_flags & TRUNCATION_FLAG) || (header_flags & RESP_MASK) )
297 {
298 // Mark the entire packet as read
299 iUdp.flush();
300 return -5; //INVALID_RESPONSE;
301 }
302
303 // And make sure we've got (at least) one answer
304 uint16_t answerCount = htons(*((uint16_t*)&header[6]));
305 if (answerCount == 0 )
306 {
307 // Mark the entire packet as read
308 iUdp.flush();
309 return -6; //INVALID_RESPONSE;
310 }
311
312 // Skip over any questions
313 for (uint16_t i =0; i < htons(*((uint16_t*)&header[4])); i++)
314 {
315 // Skip over the name
316 uint8_t len;
317 do
318 {
319 iUdp.read(&len, sizeof(len));
320 if (len > 0)
321 {
322 // Don't need to actually read the data out for the string, just
323 // advance ptr to beyond it
324 while(len--)
325 {
326 iUdp.read(); // we don't care about the returned byte
327 }
328 }
329 } while (len != 0);
330
331 // Now jump over the type and class
332 for (int i =0; i < 4; i++)
333 {
334 iUdp.read(); // we don't care about the returned byte
335 }
336 }
337
338 // Now we're up to the bit we're interested in, the answer
339 // There might be more than one answer (although we'll just use the first
340 // type A answer) and some authority and additional resource records but
341 // we're going to ignore all of them.
342
343 for (uint16_t i =0; i < answerCount; i++)
344 {
345 // Skip the name
346 uint8_t len;
347 do
348 {
349 iUdp.read(&len, sizeof(len));
350 if ((len & LABEL_COMPRESSION_MASK) == 0)
351 {
352 // It's just a normal label
353 if (len > 0)
354 {
355 // And it's got a length
356 // Don't need to actually read the data out for the string,
357 // just advance ptr to beyond it
358 while(len--)
359 {
360 iUdp.read(); // we don't care about the returned byte
361 }
362 }
363 }
364 else
365 {
366 // This is a pointer to a somewhere else in the message for the
367 // rest of the name. We don't care about the name, and RFC1035
368 // says that a name is either a sequence of labels ended with a
369 // 0 length octet or a pointer or a sequence of labels ending in
370 // a pointer. Either way, when we get here we're at the end of
371 // the name
372 // Skip over the pointer
373 iUdp.read(); // we don't care about the returned byte
374 // And set len so that we drop out of the name loop
375 len = 0;
376 }
377 } while (len != 0);
378
379 // Check the type and class
380 uint16_t answerType;
381 uint16_t answerClass;
382 iUdp.read((uint8_t*)&answerType, sizeof(answerType));
383 iUdp.read((uint8_t*)&answerClass, sizeof(answerClass));
384
385 // Ignore the Time-To-Live as we don't do any caching
386 for (int i =0; i < TTL_SIZE; i++)
387 {
388 iUdp.read(); // we don't care about the returned byte
389 }
390
391 // And read out the length of this answer
392 // Don't need header_flags anymore, so we can reuse it here
393 iUdp.read((uint8_t*)&header_flags, sizeof(header_flags));
394
395 if ( (htons(answerType) == TYPE_A) && (htons(answerClass) == CLASS_IN) )
396 {
397 if (htons(header_flags) != 4)
398 {
399 // It's a weird size
400 // Mark the entire packet as read
401 iUdp.flush();
402 return -9;//INVALID_RESPONSE;
403 }
404 iUdp.read(aAddress.raw_address(), 4);
405 return SUCCESS;
406 }
407 else
408 {
409 // This isn't an answer type we're after, move onto the next one
410 for (uint16_t i =0; i < htons(header_flags); i++)
411 {
412 iUdp.read(); // we don't care about the returned byte
413 }
414 }
415 }
416
417 // Mark the entire packet as read
418 iUdp.flush();
419
420 // If we get here then we haven't found an answer
421 return -10;//INVALID_RESPONSE;
422}
423
Note: See TracBrowser for help on using the repository browser.