mbed OS5

Fork of UIPEthernet by Zoltan Hudak

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers Dns.cpp Source File

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