Library to resolve text URLs to IP addresses (IPv4)

Dependents:   NetworkSocketAPI NetworkSocketAPI Nucleo-AWS-IoT-mbed

Revision:
0:fff4b9055396
Child:
1:5d978992a518
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/DnsQuery.cpp	Tue Jul 14 20:48:12 2015 +0000
@@ -0,0 +1,249 @@
+/* Copyright (c) 2013 Henry Leinen (henry[dot]leinen [at] online [dot] de)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute,
+ * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "mbed.h"
+#include "DnsQuery.h"
+#include "UDPSocket.h"
+
+//Debug is disabled by default
+#if 0
+#define DBG(x, ...)  printf("[DNS : DBG]"x" \t[%s,%d]\r\n", ##__VA_ARGS__,__FILE__,__LINE__); 
+#define WARN(x, ...) printf("[DNS : WARN]"x" \t[%s,%d]\r\n", ##__VA_ARGS__,__FILE__,__LINE__); 
+#define ERR(x, ...)  printf("[DNS : ERR]"x" \t[%s,%d]\r\n", ##__VA_ARGS__,__FILE__,__LINE__); 
+#else
+#define DBG(x, ...) //wait_us(10);
+#define WARN(x, ...) //wait_us(10);
+#define ERR(x, ...)
+#endif
+
+#if 0
+#define INFO(x, ...) printf("[DNS : INFO]"x" \t[%s,%d]\r\n", ##__VA_ARGS__,__FILE__,__LINE__); 
+#else
+#define INFO(x, ...)
+#endif
+
+
+
+DnsQuery::DnsQuery(IPADDRESS_t *dnsip)
+{
+    _dnsip.sin_addr.o1 = dnsip->sin_addr.o1;
+    _dnsip.sin_addr.o2 = dnsip->sin_addr.o2;
+    _dnsip.sin_addr.o3 = dnsip->sin_addr.o3;
+    _dnsip.sin_addr.o4 = dnsip->sin_addr.o4;
+    
+    INFO("Setting   DNS = %d.%d.%d.%d !", dnsip->sin_addr.o1, dnsip->sin_addr.o2, dnsip->sin_addr.o3, dnsip->sin_addr.o4);
+    INFO("Accepting DNS = %s !",_dnsip.string_format());
+}
+
+bool DnsQuery::gethostbyname(char* hostname, IPADDRESS_t &ipaddress)
+{
+    IPADDRESS_t dnsIp_1 = {209,244,0,3};
+    IPADDRESS_t dnsIp_2 = {8,8,8,8};
+    IPADDRESS_t dnsIp_3 = {84,200,69,80};
+    IPADDRESS_t dnsIp_4 = {8,26,56,26};
+    IPADDRESS_t dnsIp_5 = {208,67,222,222};
+    IPADDRESS_t dnsIp;
+    for(int i = 0; i<5; i++){
+        switch(i) {
+            case 0 : dnsIp= dnsIp_1; break ;
+            case 1 : dnsIp= dnsIp_2;  break ;
+            case 2 : dnsIp= dnsIp_3;  break ;           
+            case 3 : dnsIp= dnsIp_4;  break ;      
+            case 4 : dnsIp= dnsIp_5;  break ;  
+        }
+        DnsQuery dns = DnsQuery(&dnsIp);
+        if(dns.getIP(hostname, ipaddress)){
+            return true;   
+        }
+           
+    }
+    return false;
+    
+}
+
+bool DnsQuery::getIP(char* hostname, IPADDRESS_t &ipaddress)
+{
+    INFO("%s", hostname);
+    int len = 0;
+    if (hostname == NULL)
+        return false;
+    len = strlen(hostname);
+    if ((len >128) || (len == 0))
+        return false;
+    memset (&ipaddress, 0, sizeof(ipaddress));
+    
+    int packetlen = /* size of HEADER structure */ 12 + /* size of QUESTION Structure */5 + len + 1;
+    char *packet = new char[packetlen]; /* this is the UDP packet to send to the DNS */
+    if (packet == NULL)
+        return false;
+    
+    //  Fill the header
+    memset(packet, 0, packetlen);
+    packet[1] = 1;      //  ID = 1
+    packet[5] = 1;      // QDCOUNT = 1 (contains one question)
+    packet[2] = 1;      // recursion requested
+
+    int c = 13;         //  point to NAME element in question section or request
+    int cnt = 12;       //  points to the counter of
+    packet[cnt] = 0;
+    for (int i = 0 ; i < len ; i++) {
+        if (hostname[i] != '.') {
+            //  Copy the character and increment the character counter
+            packet[cnt]++;
+            packet[c++] = hostname[i];
+        } else {
+            //  Finished with this part, so go to the next
+            cnt = c++;
+            packet[cnt] = 0;
+        }
+    }
+    
+    //  Terminate this domain name with a zero entry
+    packet[c++] = 0;
+    
+    //  Set QTYPE
+    packet[c++] = 0;
+    packet[c++] = 1;
+    //  Set QCLASS
+    packet[c++] = 0;
+    packet[c++] = 1;
+    
+    
+    //  Ready to send to DNS
+    UDPSocket sock = UDPSocket();
+    Endpoint e = Endpoint();
+    //INFO("Using IP: %s",_dnsip.string_format());
+    char ip[7];
+    sprintf(ip,"%s",_dnsip.string_format());
+    e.set_address(ip, 53);
+    delete ip;
+   
+    INFO("Sending packet"); 
+    if (sock.sendTo(e, packet, packetlen) < 0) {
+        delete packet;
+        return false;
+    }
+    
+    delete packet;
+    
+    packet = new char [1024];
+    if (packet == NULL) {
+        return false;
+    }
+    
+    //  Receive the answer from DNS
+    Timer t;
+    t.start();
+    int response_length = 0;
+    while (t.read_ms() < 10000) {
+        INFO("Recieving");
+        response_length = sock.receiveFrom(e, packet, 1024);
+        if (response_length > 0 ) {
+            int offset = packetlen;
+            for(int i = offset; i < 1024; i++) {
+                if(0x3A==packet[i]){
+                    offset = i+1;
+                    break;
+                }
+            }
+            char *offset_packet = packet + offset;
+            if (!resolve(offset_packet, ipaddress)) {
+                break;
+            }
+                        
+            //  cleanup and return
+            delete packet;
+            sock.close();
+            //INFO("Found this IP:%s",ipaddress.string_format());
+            return true;
+        } else {
+            ERR("SocketRecvFrom returned %d !", response_length);
+        }
+    }    
+    sock.close();
+    delete packet;
+    ERR("NO IP FOUND\n");
+    return false;
+}
+
+
+bool DnsQuery::resolve(char* resp, IPADDRESS_t &ipaddress)
+{
+    
+    int ID = (((int)resp[0]) <<8) + resp[1];
+    int QR = resp[2] >>7;
+    int Opcode = (resp[2]>>3) & 0x0F;
+    int RCODE = (resp[3] & 0x0F);
+    int ANCOUNT = (((int)resp[6])<<8)+ resp[7];
+    
+    INFO("Resolving response : ID = %d, QR = %d, Opcode = %d, RCODE = %d", ID, QR, Opcode, RCODE);
+    if ((ID != 1) || (QR != 1) || (Opcode != 0) || (RCODE != 0)) {
+        ERR("Received non matching response from DNS !");
+        return false;
+    }
+    
+    int c = 12;
+    int d;
+    //  Skip domain question
+    while(  (d=resp[c++]) != 0) {
+        c+=d;
+    }
+    c+= 4; //   skip QTYPE and QCLASS
+    
+    //  Here comes the resource record
+    for (int ans = 0 ; ans < ANCOUNT; ans++) {
+        if (parseRR(resp, c, ipaddress)) {
+            return true;
+        }
+    }
+    
+    return false;
+}
+
+bool DnsQuery::parseRR(char *resp, int& c, IPADDRESS_t& adr )
+{
+    int n = 0;
+    
+    while( (n=resp[c++]) != 0) {
+        if ((n & 0xc0) != 0) {
+            //  This is a link
+            c++;
+            break;
+        } else {
+            c+= n;  //  skip this segment, not interested in string domain names
+        }
+    }
+    
+    int TYPE = (((int)resp[c])<<8) + resp[c+1];
+    int CLASS = (((int)resp[c+2])<<8) + resp[c+3];
+    int RDLENGTH = (((int)resp[c+8])<<8) + resp[c+9];
+
+    INFO("Record of TYPE=%d and CLASS=%d detected !", TYPE, CLASS);
+    c+= 10;
+    if ((CLASS == 1) && (TYPE == 1)) {
+        adr.sin_addr.o1 = resp[c];
+        adr.sin_addr.o2 = resp[c+1];
+        adr.sin_addr.o3 = resp[c+2];
+        adr.sin_addr.o4 = resp[c+3];
+        c+= RDLENGTH;
+        return true;
+    } else {
+    }
+    c+= RDLENGTH;
+    
+    return false;
+}
\ No newline at end of file