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