PageRenderTime 40ms CodeModel.GetById 25ms app.highlight 12ms RepoModel.GetById 0ms app.codeStats 0ms

/libraries/Ethernet/Dns.cpp

https://github.com/doctormars/Arduino
C++ | 423 lines | 279 code | 43 blank | 101 comment | 64 complexity | 7709e3512c6b8ce02b3fc737bf56f0a5 MD5 | raw file
  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
 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(int 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 != 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 (int 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 (int 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 (int 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