mbed OS5

Fork of UIPEthernet by Zoltan Hudak

Revision:
0:5350a66d5279
Child:
2:049ce85163c5
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Dns.cpp	Mon Sep 15 11:12:30 2014 +0000
@@ -0,0 +1,454 @@
+// mbed DNS client for Enc28J60-based Ethernet shield
+// (c) Copyright 2009-2010 MCQN Ltd.
+// Released under Apache License, version 2.0
+#include "Udp.h"
+#include "util.h"
+
+#include "Dns.h"
+#include <string.h>
+//#include <stdlib.h>
+
+#include "mbed.h"
+#include "uip_clock.h"
+
+#define SOCKET_NONE 255
+// Various flags and header field values for a DNS message
+
+#define UDP_HEADER_SIZE             8
+#define DNS_HEADER_SIZE             12
+#define TTL_SIZE                    4
+#define QUERY_FLAG                  (0)
+#define RESPONSE_FLAG               (1 << 15)
+#define QUERY_RESPONSE_MASK         (1 << 15)
+#define OPCODE_STANDARD_QUERY       (0)
+#define OPCODE_INVERSE_QUERY        (1 << 11)
+#define OPCODE_STATUS_REQUEST       (2 << 11)
+#define OPCODE_MASK                 (15 << 11)
+#define AUTHORITATIVE_FLAG          (1 << 10)
+#define TRUNCATION_FLAG             (1 << 9)
+#define RECURSION_DESIRED_FLAG      (1 << 8)
+#define RECURSION_AVAILABLE_FLAG    (1 << 7)
+#define RESP_NO_ERROR               (0)
+#define RESP_FORMAT_ERROR           (1)
+#define RESP_SERVER_FAILURE         (2)
+#define RESP_NAME_ERROR             (3)
+#define RESP_NOT_IMPLEMENTED        (4)
+#define RESP_REFUSED                (5)
+#define RESP_MASK                   (15)
+#define TYPE_A                      (0x0001)
+#define CLASS_IN                    (0x0001)
+#define LABEL_COMPRESSION_MASK      (0xC0)
+// Port number that DNS servers listen on
+
+#define DNS_PORT    53
+
+// Possible return codes from ProcessResponse
+
+#define SUCCESS             1
+#define TIMED_OUT           - 1
+#define INVALID_SERVER      - 2
+#define TRUNCATED           - 3
+#define INVALID_RESPONSE    - 4
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+void DNSClient::begin(const IPAddress& aDNSServer) {
+    iDNSServer = aDNSServer;
+    iRequestId = 0;
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+int DNSClient::inet_aton(const char* aIPAddrString, IPAddress& aResult) {
+
+    // See if we've been given a valid IP address
+    const char*     p = aIPAddrString;
+    while(*p && ((*p == '.') || (*p >= '0') || (*p <= '9'))) {
+        p++;
+    }
+
+    if(*p == '\0') {
+
+        // It's looking promising, we haven't found any invalid characters
+        p = aIPAddrString;
+
+        int segment = 0;
+        int segmentValue = 0;
+        while(*p && (segment < 4)) {
+            if(*p == '.') {
+
+                // We've reached the end of a segment
+                if(segmentValue > 255) {
+
+                    // You can't have IP address segments that don't fit in a byte
+                    return 0;
+                }
+                else {
+                    aResult[segment] = (uint8_t) segmentValue;
+                    segment++;
+                    segmentValue = 0;
+                }
+            }
+            else {
+
+                // Next digit
+                segmentValue = (segmentValue * 10) + (*p - '0');
+            }
+
+            p++;
+        }
+
+        // We've reached the end of address, but there'll still be the last
+        // segment to deal with
+        if((segmentValue > 255) || (segment > 3)) {
+
+            // You can't have IP address segments that don't fit in a byte,
+            // or more than four segments
+            return 0;
+        }
+        else {
+            aResult[segment] = (uint8_t) segmentValue;
+            return 1;
+        }
+    }
+    else {
+        return 0;
+    }
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+int DNSClient::getHostByName(const char* aHostname, IPAddress& aResult) {
+    int ret = 0;
+
+    // See if it's a numeric IP address
+
+    if(inet_aton(aHostname, aResult)) {
+
+        // It is, our work here is done
+        return 1;
+    }
+
+    // Check we've got a valid DNS server to use
+    if(iDNSServer == INADDR_NONE) {
+        return INVALID_SERVER;
+    }
+
+    // Find a socket to use
+    if(iUdp.begin(1024 + (clock_time() & 0xF)) == 1) {
+
+        // Try up to three times
+        int retries = 0;
+        //        while ((retries < 3) && (ret <= 0))
+        {
+            // Send DNS request
+            ret = iUdp.beginPacket(iDNSServer, DNS_PORT);
+            if(ret != 0) {
+
+                // Now output the request data
+                ret = BuildRequest(aHostname);
+                if(ret != 0) {
+
+                    // And finally send the request
+                    ret = iUdp.endPacket();
+                    if(ret != 0) {
+
+                        // Now wait for a response
+                        int wait_retries = 0;
+                        ret = TIMED_OUT;
+                        while((wait_retries < 3) && (ret == TIMED_OUT)) {
+                            ret = ProcessResponse(5000, aResult);
+                            wait_retries++;
+                        }
+                    }
+                }
+            }
+
+            retries++;
+        }
+
+        // We're done with the socket now
+        iUdp.stop();
+    }
+
+    return ret;
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+uint16_t DNSClient::BuildRequest(const char* aName) {
+
+    // Build header
+
+    //                                    1  1  1  1  1  1
+    //      0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    //    |                      ID                       |
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    //    |QR|   Opcode  |AA|TC|RD|RA|   Z    |   RCODE   |
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    //    |                    QDCOUNT                    |
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    //    |                    ANCOUNT                    |
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    //    |                    NSCOUNT                    |
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    //    |                    ARCOUNT                    |
+    //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    // As we only support one request at a time at present, we can simplify
+    // some of this header
+    iRequestId = clock_time();          // generate a random ID
+    uint16_t    twoByteBuffer;
+
+    // FIXME We should also check that there's enough space available to write to, rather
+
+    // FIXME than assume there's enough space (as the code does at present)
+    iUdp.write((uint8_t*) &iRequestId, sizeof(iRequestId));
+
+    twoByteBuffer = htons(QUERY_FLAG | OPCODE_STANDARD_QUERY | RECURSION_DESIRED_FLAG);
+    iUdp.write((uint8_t*) &twoByteBuffer, sizeof(twoByteBuffer));
+
+    twoByteBuffer = htons(1);           // One question record
+    iUdp.write((uint8_t*) &twoByteBuffer, sizeof(twoByteBuffer));
+
+    twoByteBuffer = 0;                  // Zero answer records
+    iUdp.write((uint8_t*) &twoByteBuffer, sizeof(twoByteBuffer));
+
+    iUdp.write((uint8_t*) &twoByteBuffer, sizeof(twoByteBuffer));
+
+    // and zero additional records
+    iUdp.write((uint8_t*) &twoByteBuffer, sizeof(twoByteBuffer));
+
+    // Build question
+    const char*     start = aName;
+    const char*     end = start;
+    uint8_t         len;
+    // Run through the name being requested
+
+    while(*end) {
+
+        // Find out how long this section of the name is
+        end = start;
+        while(*end && (*end != '.')) {
+            end++;
+        }
+
+        if(end - start > 0) {
+
+            // Write out the size of this section
+            len = end - start;
+            iUdp.write(&len, sizeof(len));
+
+            // And then write out the section
+            iUdp.write((uint8_t*)start, end - start);
+        }
+
+        start = end + 1;
+    }
+
+    // We've got to the end of the question name, so
+    // terminate it with a zero-length section
+    len = 0;
+    iUdp.write(&len, sizeof(len));
+
+    // Finally the type and class of question
+    twoByteBuffer = htons(TYPE_A);
+    iUdp.write((uint8_t*) &twoByteBuffer, sizeof(twoByteBuffer));
+
+    twoByteBuffer = htons(CLASS_IN);    // Internet class of question
+    iUdp.write((uint8_t*) &twoByteBuffer, sizeof(twoByteBuffer));
+
+    // Success!  Everything buffered okay
+    return 1;
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+uint16_t DNSClient::ProcessResponse(uint16_t aTimeout, IPAddress& aAddress) {
+    uint32_t    startTime = clock_time();
+
+    // Wait for a response packet
+
+    while(iUdp.parsePacket() <= 0) {
+        if((clock_time() - startTime) > aTimeout)
+            return TIMED_OUT;
+        wait(0.050);
+    }
+
+    // We've had a reply!
+    // Read the UDP header
+    uint8_t header[DNS_HEADER_SIZE];    // Enough space to reuse for the DNS header
+
+    // Check that it's a response from the right server and the right port
+    if((iDNSServer != iUdp.remoteIP()) || (iUdp.remotePort() != DNS_PORT)) {
+
+        // It's not from who we expected
+        return INVALID_SERVER;
+    }
+
+    // Read through the rest of the response
+    if(iUdp.available() < DNS_HEADER_SIZE) {
+        return TRUNCATED;
+    }
+
+    iUdp.read(header, DNS_HEADER_SIZE);
+
+    uint16_t    header_flags = htons(*((uint16_t*) &header[2]));
+    // Check that it's a response to this request
+
+    if
+    (
+        (iRequestId != (*((uint16_t*) &header[0])))
+    ||  ((header_flags & QUERY_RESPONSE_MASK) != (uint16_t) RESPONSE_FLAG)
+    ) {
+
+        // Mark the entire packet as read
+        iUdp.flush();
+        return INVALID_RESPONSE;
+    }
+
+    // Check for any errors in the response (or in our request)
+    // although we don't do anything to get round these
+    if((header_flags & TRUNCATION_FLAG) || (header_flags & RESP_MASK)) {
+
+        // Mark the entire packet as read
+        iUdp.flush();
+        return -5;  //INVALID_RESPONSE;
+    }
+
+    // And make sure we've got (at least) one answer
+    uint16_t    answerCount = htons(*((uint16_t*) &header[6]));
+    if(answerCount == 0) {
+
+        // Mark the entire packet as read
+        iUdp.flush();
+        return -6;  //INVALID_RESPONSE;
+    }
+
+    // Skip over any questions
+    for(uint16_t i = 0; i < htons(*((uint16_t*) &header[4])); i++) {
+
+        // Skip over the name
+        uint8_t len;
+        do
+        {
+            iUdp.read(&len, sizeof(len));
+            if(len > 0) {
+
+                // Don't need to actually read the data out for the string, just
+                // advance ptr to beyond it
+                while(len--) {
+                    iUdp.read();        // we don't care about the returned byte
+                }
+            }
+        } while(len != 0);
+
+        // Now jump over the type and class
+        for(int i = 0; i < 4; i++) {
+            iUdp.read();                // we don't care about the returned byte
+        }
+    }
+
+    // Now we're up to the bit we're interested in, the answer
+    // There might be more than one answer (although we'll just use the first
+    // type A answer) and some authority and additional resource records but
+    // we're going to ignore all of them.
+    for(uint16_t i = 0; i < answerCount; i++) {
+
+        // Skip the name
+        uint8_t len;
+        do
+        {
+            iUdp.read(&len, sizeof(len));
+            if((len & LABEL_COMPRESSION_MASK) == 0) {
+
+                // It's just a normal label
+                if(len > 0) {
+
+                    // And it's got a length
+                    // Don't need to actually read the data out for the string,
+                    // just advance ptr to beyond it
+                    while(len--) {
+                        iUdp.read();    // we don't care about the returned byte
+                    }
+                }
+            }
+            else {
+
+                // This is a pointer to a somewhere else in the message for the
+                // rest of the name.  We don't care about the name, and RFC1035
+                // says that a name is either a sequence of labels ended with a
+                // 0 length octet or a pointer or a sequence of labels ending in
+                // a pointer.  Either way, when we get here we're at the end of
+                // the name
+                // Skip over the pointer
+                iUdp.read();            // we don't care about the returned byte
+
+                // And set len so that we drop out of the name loop
+                len = 0;
+            }
+        } while(len != 0);
+
+        // Check the type and class
+        uint16_t    answerType;
+        uint16_t    answerClass;
+        iUdp.read((uint8_t*) &answerType, sizeof(answerType));
+        iUdp.read((uint8_t*) &answerClass, sizeof(answerClass));
+
+        // Ignore the Time-To-Live as we don't do any caching
+        for(int i = 0; i < TTL_SIZE; i++) {
+            iUdp.read();        // we don't care about the returned byte
+        }
+
+        // And read out the length of this answer
+        // Don't need header_flags anymore, so we can reuse it here
+        iUdp.read((uint8_t*) &header_flags, sizeof(header_flags));
+
+        if((htons(answerType) == TYPE_A) && (htons(answerClass) == CLASS_IN)) {
+            if(htons(header_flags) != 4) {
+
+                // It's a weird size
+                // Mark the entire packet as read
+                iUdp.flush();
+                return -9;      //INVALID_RESPONSE;
+            }
+
+            iUdp.read(aAddress.raw_address(), 4);
+            return SUCCESS;
+        }
+        else {
+
+            // This isn't an answer type we're after, move onto the next one
+            for(uint16_t i = 0; i < htons(header_flags); i++) {
+                iUdp.read();    // we don't care about the returned byte
+            }
+        }
+    }
+
+    // Mark the entire packet as read
+    iUdp.flush();
+
+    // If we get here then we haven't found an answer
+    return -10; //INVALID_RESPONSE;
+}
+