root / arduino-1.0 / libraries / Ethernet / Dns.cpp @ 58d82c77
History | View | Annotate | Download (13.1 KB)
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 "w5100.h" |
6 |
#include "EthernetUdp.h" |
7 |
#include "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 |
|
51 |
void DNSClient::begin(const IPAddress& aDNSServer) |
52 |
{ |
53 |
iDNSServer = aDNSServer; |
54 |
iRequestId = 0;
|
55 |
} |
56 |
|
57 |
|
58 |
int 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 |
|
118 |
int 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 |
|
175 |
uint16_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 |
|
255 |
uint16_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 |
|