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