Mbed library for ENC28J60 Ethernet modules. Full support for TCP/IP and UDP Server, Client and HTTP server (webserver). DHCP and DNS is included.

Dependents:   mBuino_ENC28_MQTT Nucleo_Web_ENC28J60 Nucleo_Web_ENC28J60_ADC Serial_over_Ethernet ... more

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers DnsClient.cpp Source File

DnsClient.cpp

00001 // Arduino DNS client for Enc28J60-based Ethernet shield
00002 // (c) Copyright 2009-2010 MCQN Ltd.
00003 // Released under Apache License, version 2.0
00004 #include "UdpSocket.h"
00005 #include "utility/util.h"
00006 
00007 #include "DnsClient.h"
00008 #include <string.h>
00009 #include "mbed.h"
00010 #include "mbed_version.h"
00011 
00012 #define SOCKET_NONE 255
00013 
00014 // Various flags and header field values for a DNS message
00015 
00016 #define UDP_HEADER_SIZE             8
00017 #define DNS_HEADER_SIZE             12
00018 #define TTL_SIZE                    4
00019 #define QUERY_FLAG                  (0)
00020 #define RESPONSE_FLAG               (1 << 15)
00021 #define QUERY_RESPONSE_MASK         (1 << 15)
00022 #define OPCODE_STANDARD_QUERY       (0)
00023 #define OPCODE_INVERSE_QUERY        (1 << 11)
00024 #define OPCODE_STATUS_REQUEST       (2 << 11)
00025 #define OPCODE_MASK                 (15 << 11)
00026 #define AUTHORITATIVE_FLAG          (1 << 10)
00027 #define TRUNCATION_FLAG             (1 << 9)
00028 #define RECURSION_DESIRED_FLAG      (1 << 8)
00029 #define RECURSION_AVAILABLE_FLAG    (1 << 7)
00030 #define RESP_NO_ERROR               (0)
00031 #define RESP_FORMAT_ERROR           (1)
00032 #define RESP_SERVER_FAILURE         (2)
00033 #define RESP_NAME_ERROR             (3)
00034 #define RESP_NOT_IMPLEMENTED        (4)
00035 #define RESP_REFUSED                (5)
00036 #define RESP_MASK                   (15)
00037 #define TYPE_A                      (0x0001)
00038 #define CLASS_IN                    (0x0001)
00039 #define LABEL_COMPRESSION_MASK      (0xC0)
00040 
00041 // Port number that DNS servers listen on
00042 
00043 #define DNS_PORT    53
00044 
00045 // Possible return codes from ProcessResponse
00046 
00047 #define SUCCESS             1
00048 #define TIMED_OUT           - 1
00049 #define INVALID_SERVER      - 2
00050 #define TRUNCATED           - 3
00051 #define INVALID_RESPONSE    - 4
00052 
00053 /**
00054  * @brief
00055  * @note
00056  * @param
00057  * @retval
00058  */
00059 void DnsClient::begin(const IpAddress& aDNSServer)
00060 {
00061     iDNSServer = aDNSServer;
00062     iRequestId = 0;
00063 }
00064 
00065 /**
00066  * @brief
00067  * @note
00068  * @param
00069  * @retval
00070  */
00071 int DnsClient::inet_aton(const char* aIPAddrString, IpAddress& aResult)
00072 {
00073     // See if we've been given a valid IP address
00074     const char*     p = aIPAddrString;
00075     while (*p && ((*p == '.') || ((*p >= '0') && (*p <= '9')))) {
00076         p++;
00077     }
00078 
00079     if (*p == '\0') {
00080         // It's looking promising, we haven't found any invalid characters
00081         p = aIPAddrString;
00082 
00083         int segment = 0;
00084         int segmentValue = 0;
00085         while (*p && (segment < 4)) {
00086             if (*p == '.') {
00087                 // We've reached the end of a segment
00088                 if (segmentValue > 255) {
00089                     // You can't have IP address segments that don't fit in a byte
00090                     return 0;
00091                 }
00092                 else {
00093                     aResult[segment] = (uint8_t) segmentValue;
00094                     segment++;
00095                     segmentValue = 0;
00096                 }
00097             }
00098             else {
00099                 // Next digit
00100                 segmentValue = (segmentValue * 10) + (*p - '0');
00101             }
00102 
00103             p++;
00104         }
00105 
00106         // We've reached the end of address, but there'll still be the last
00107         // segment to deal with
00108         if ((segmentValue > 255) || (segment > 3)) {
00109             // You can't have IP address segments that don't fit in a byte,
00110             // or more than four segments
00111             return 0;
00112         }
00113         else {
00114             aResult[segment] = (uint8_t) segmentValue;
00115             return 1;
00116         }
00117     }
00118     else {
00119         return 0;
00120     }
00121 }
00122 
00123 /**
00124  * @brief
00125  * @note
00126  * @param
00127  * @retval
00128  */
00129 int DnsClient::getHostByName(const char* aHostname, IpAddress& aResult)
00130 {
00131     int ret = 0;
00132     Timer   timer;
00133 
00134     timer.start();
00135 
00136     // See if it's a numeric IP address
00137     if (inet_aton(aHostname, aResult)) {
00138         // It is, our work here is done
00139         return 1;
00140     }
00141 
00142     // Check we've got a valid DNS server to use
00143     if (iDNSServer == INADDR_NONE) {
00144         return INVALID_SERVER;
00145     }
00146 
00147     // Find a socket to use
00148     if (iUdp.begin(1024 + ((timer.read_ms() / 1000) & 0xF)) == 1) {
00149         // Try up to three times
00150         int retries = 0;
00151         //        while ((retries < 3) && (ret <= 0))
00152         {
00153             // Send DNS request
00154             ret = iUdp.beginPacket(iDNSServer, DNS_PORT);
00155             if (ret != 0) {
00156                 // Now output the request data
00157                 ret = buildRequest(aHostname);
00158                 if (ret != 0) {
00159                     // And finally send the request
00160                     ret = iUdp.endPacket();
00161                     if (ret != 0) {
00162                         // Now wait for a response
00163                         int wait_retries = 0;
00164                         ret = TIMED_OUT;
00165                         while ((wait_retries < 3) && (ret == TIMED_OUT)) {
00166                             ret = processResponse(5000, aResult);
00167                             wait_retries++;
00168                         }
00169                     }
00170                 }
00171             }
00172 
00173             retries++;
00174         }
00175 
00176         // We're done with the socket now
00177         iUdp.stop();
00178     }
00179 
00180     return ret;
00181 }
00182 
00183 /**
00184  * @brief
00185  * @note
00186  * @param
00187  * @retval
00188  */
00189 uint16_t DnsClient::buildRequest(const char* aName)
00190 {
00191     // Build header
00192     //                                    1  1  1  1  1  1
00193     //      0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
00194     //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
00195     //    |                      ID                       |
00196     //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
00197     //    |QR|   Opcode  |AA|TC|RD|RA|   Z    |   RCODE   |
00198     //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
00199     //    |                    QDCOUNT                    |
00200     //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
00201     //    |                    ANCOUNT                    |
00202     //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
00203     //    |                    NSCOUNT                    |
00204     //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
00205     //    |                    ARCOUNT                    |
00206     //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
00207     // As we only support one request at a time at present, we can simplify
00208     // some of this header
00209     srand(time(NULL) + 2);
00210     iRequestId = rand() % 0xFFFF + 1;   // generate a random ID
00211     uint16_t    twoByteBuffer;
00212 
00213     // FIXME We should also check that there's enough space available to write to, rather
00214     // FIXME than assume there's enough space (as the code does at present)
00215     iUdp.write((uint8_t*) &iRequestId, sizeof(iRequestId));
00216 
00217     twoByteBuffer = htons(QUERY_FLAG | OPCODE_STANDARD_QUERY | RECURSION_DESIRED_FLAG);
00218     iUdp.write((uint8_t*) &twoByteBuffer, sizeof(twoByteBuffer));
00219 
00220     twoByteBuffer = htons(1);           // One question record
00221     iUdp.write((uint8_t*) &twoByteBuffer, sizeof(twoByteBuffer));
00222 
00223     twoByteBuffer = 0;                  // Zero answer records
00224     iUdp.write((uint8_t*) &twoByteBuffer, sizeof(twoByteBuffer));
00225 
00226     iUdp.write((uint8_t*) &twoByteBuffer, sizeof(twoByteBuffer));
00227 
00228     // and zero additional records
00229     iUdp.write((uint8_t*) &twoByteBuffer, sizeof(twoByteBuffer));
00230 
00231     // Build question
00232     const char*     start = aName;
00233     const char*     end = start;
00234     uint8_t         len;
00235     // Run through the name being requested
00236     while (*end) {
00237         // Find out how long this section of the name is
00238         end = start;
00239         while (*end && (*end != '.')) {
00240             end++;
00241         }
00242 
00243         if (end - start > 0) {
00244             // Write out the size of this section
00245             len = end - start;
00246             iUdp.write(&len, sizeof(len));
00247 
00248             // And then write out the section
00249             iUdp.write((uint8_t*)start, end - start);
00250         }
00251 
00252         start = end + 1;
00253     }
00254 
00255     // We've got to the end of the question name, so
00256     // terminate it with a zero-length section
00257     len = 0;
00258     iUdp.write(&len, sizeof(len));
00259 
00260     // Finally the type and class of question
00261     twoByteBuffer = htons(TYPE_A);
00262     iUdp.write((uint8_t*) &twoByteBuffer, sizeof(twoByteBuffer));
00263 
00264     twoByteBuffer = htons(CLASS_IN);    // Internet class of question
00265     iUdp.write((uint8_t*) &twoByteBuffer, sizeof(twoByteBuffer));
00266 
00267     // Success!  Everything buffered okay
00268     return 1;
00269 }
00270 
00271 /**
00272  * @brief
00273  * @note
00274  * @param
00275  * @retval
00276  */
00277 int16_t DnsClient::processResponse(time_t aTimeout, IpAddress& aAddress)
00278 {
00279     time_t  startTime = time(NULL);
00280     Timer   timer;
00281 
00282     timer.start();
00283 
00284     // Wait for a response packet
00285     while (iUdp.parsePacket() <= 0) {
00286         if ((time_t)timer.read_ms() > aTimeout)
00287             return TIMED_OUT;
00288 #if MBED_MAJOR_VERSION == 2
00289         wait_ms(50);
00290 #else
00291         thread_sleep_for(50);
00292 #endif
00293     }
00294 
00295     // We've had a reply!
00296     // Read the UDP header
00297     uint8_t header[DNS_HEADER_SIZE];    // Enough space to reuse for the DNS header
00298 
00299     // Check that it's a response from the right server and the right port
00300     if ((iDNSServer != iUdp.remoteIP()) || (iUdp.remotePort() != DNS_PORT)) {
00301         // It's not from who we expected
00302         return INVALID_SERVER;
00303     }
00304 
00305     // Read through the rest of the response
00306     if (iUdp.available() < DNS_HEADER_SIZE) {
00307         return TRUNCATED;
00308     }
00309 
00310     iUdp.read(header, DNS_HEADER_SIZE);
00311 
00312     uint16_t    header_flags = htons(*((uint16_t*) &header[2]));
00313     // Check that it's a response to this request
00314     if
00315     (
00316         (iRequestId != (*((uint16_t*) &header[0]))) ||
00317         ((header_flags & QUERY_RESPONSE_MASK) != (uint16_t) RESPONSE_FLAG)
00318     ) {
00319         // Mark the entire packet as read
00320         iUdp.flush();
00321         return INVALID_RESPONSE;
00322     }
00323 
00324     // Check for any errors in the response (or in our request)
00325     // although we don't do anything to get round these
00326     if ((header_flags & TRUNCATION_FLAG) || (header_flags & RESP_MASK)) {
00327         // Mark the entire packet as read
00328         iUdp.flush();
00329         return -5;                      //INVALID_RESPONSE;
00330     }
00331 
00332     // And make sure we've got (at least) one answer
00333     uint16_t    answerCount = htons(*((uint16_t*) &header[6]));
00334     if (answerCount == 0) {
00335         // Mark the entire packet as read
00336         iUdp.flush();
00337         return -6;                      //INVALID_RESPONSE;
00338     }
00339 
00340     // Skip over any questions
00341     for (uint16_t i = 0; i < htons(*((uint16_t*) &header[4])); i++) {
00342         // Skip over the name
00343         uint8_t len;
00344         do {
00345             iUdp.read(&len, sizeof(len));
00346             if (len > 0) {
00347                 // Don't need to actually read the data out for the string, just
00348                 // advance ptr to beyond it
00349                 while (len--) {
00350                     iUdp.read();        // we don't care about the returned byte
00351                 }
00352             }
00353         } while (len != 0);
00354 
00355         // Now jump over the type and class
00356         for (int i = 0; i < 4; i++) {
00357             iUdp.read();                // we don't care about the returned byte
00358         }
00359     }
00360 
00361     // Now we're up to the bit we're interested in, the answer
00362     // There might be more than one answer (although we'll just use the first
00363     // type A answer) and some authority and additional resource records but
00364     // we're going to ignore all of them.
00365     for (uint16_t i = 0; i < answerCount; i++) {
00366         // Skip the name
00367         uint8_t len;
00368         do {
00369             iUdp.read(&len, sizeof(len));
00370             if ((len & LABEL_COMPRESSION_MASK) == 0) {
00371                 // It's just a normal label
00372                 if (len > 0) {
00373                     // And it's got a length
00374                     // Don't need to actually read the data out for the string,
00375                     // just advance ptr to beyond it
00376                     while (len--) {
00377                         iUdp.read();    // we don't care about the returned byte
00378                     }
00379                 }
00380             }
00381             else {
00382                 // This is a pointer to a somewhere else in the message for the
00383                 // rest of the name.  We don't care about the name, and RFC1035
00384                 // says that a name is either a sequence of labels ended with a
00385                 // 0 length octet or a pointer or a sequence of labels ending in
00386                 // a pointer.  Either way, when we get here we're at the end of
00387                 // the name
00388                 // Skip over the pointer
00389                 iUdp.read();            // we don't care about the returned byte
00390                 // And set len so that we drop out of the name loop
00391                 len = 0;
00392             }
00393         } while (len != 0);
00394 
00395         // Check the type and class
00396         uint16_t    answerType;
00397         uint16_t    answerClass;
00398         iUdp.read((uint8_t*) &answerType, sizeof(answerType));
00399         iUdp.read((uint8_t*) &answerClass, sizeof(answerClass));
00400 
00401         // Ignore the Time-To-Live as we don't do any caching
00402         for (int i = 0; i < TTL_SIZE; i++) {
00403             iUdp.read();                // we don't care about the returned byte
00404         }
00405 
00406         // And read out the length of this answer
00407         // Don't need header_flags anymore, so we can reuse it here
00408         iUdp.read((uint8_t*) &header_flags, sizeof(header_flags));
00409 
00410         if ((htons(answerType) == TYPE_A) && (htons(answerClass) == CLASS_IN)) {
00411             if (htons(header_flags) != 4) {
00412                 // It's a weird size
00413                 // Mark the entire packet as read
00414                 iUdp.flush();
00415                 return -9;              //INVALID_RESPONSE;
00416             }
00417 
00418             iUdp.read(aAddress.rawAddress(), 4);
00419             return SUCCESS;
00420         }
00421         else {
00422             // This isn't an answer type we're after, move onto the next one
00423             for (uint16_t i = 0; i < htons(header_flags); i++) {
00424                 iUdp.read();            // we don't care about the returned byte
00425             }
00426         }
00427     }
00428 
00429     // Mark the entire packet as read
00430     iUdp.flush();
00431 
00432     // If we get here then we haven't found an answer
00433     return -10;                         //INVALID_RESPONSE;
00434 }