Project

General

Profile

Statistics
| Branch: | Revision:

root / arduino-1.0 / libraries / Ethernet / Dns.cpp @ 58d82c77

History | View | Annotate | Download (13.1 KB)

1 58d82c77 Tom Mullins
// 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
}