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