Library to resolve text URLs to IP addresses (IPv4)

Dependents:   NetworkSocketAPI NetworkSocketAPI Nucleo-AWS-IoT-mbed

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers DnsQuery.cpp Source File

DnsQuery.cpp

00001 /* Copyright (c) 2013 Henry Leinen (henry[dot]leinen [at] online [dot] de)
00002  *
00003  * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
00004  * and associated documentation files (the "Software"), to deal in the Software without restriction,
00005  * including without limitation the rights to use, copy, modify, merge, publish, distribute,
00006  * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
00007  * furnished to do so, subject to the following conditions:
00008  *
00009  * The above copyright notice and this permission notice shall be included in all copies or
00010  * substantial portions of the Software.
00011  *
00012  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
00013  * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
00014  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
00015  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00016  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00017  */
00018 #include "DnsQuery.h"
00019 #include <stdio.h>
00020 #include <string.h>
00021 
00022 
00023 #define DNS_COUNT (sizeof DNS_IPS / sizeof DNS_IPS[0])
00024 const char *DNS_IPS[] = {
00025     "8.8.8.8",
00026     "209.244.0.3",
00027     "84.200.69.80",
00028     "8.26.56.26",
00029     "208.67.222.222"
00030 };
00031 
00032 static bool isIP(const char *host)
00033 {
00034     int i;
00035 
00036     for (i = 0; host[i]; i++) {
00037         if (!(host[i] >= '0' && host[i] <= '9') && host[i] != '.') {
00038             return false;
00039         }
00040     }
00041 
00042     // Ending with '.' garuntees host
00043     if (i > 0 && host[i-1] == '.') {
00044         return false;
00045     }
00046 
00047     return true;
00048 }
00049 
00050 
00051 static bool parseRR(uint8_t *resp, int &c, char *adr)
00052 {
00053     int n = 0;
00054     while((n=resp[c++]) != 0) {
00055         if ((n & 0xc0) != 0) {
00056             //  This is a link
00057             c++;
00058             break;
00059         } else {
00060             //  skip this segment, not interested in string domain names
00061             c+= n;
00062         }
00063     }
00064 
00065     int TYPE = (((int)resp[c])<<8) + resp[c+1];
00066     int CLASS = (((int)resp[c+2])<<8) + resp[c+3];
00067     int RDLENGTH = (((int)resp[c+8])<<8) + resp[c+9];
00068 
00069     c+= 10;
00070     if ((CLASS == 1) && (TYPE == 1)) {
00071         sprintf(adr,"%d.%d.%d.%d", resp[c], resp[c+1], resp[c+2], resp[c+3]);
00072         c+= RDLENGTH;
00073         return true;
00074     }
00075     c+= RDLENGTH;
00076 
00077     return false;
00078 }
00079 
00080 
00081 static bool resolve(unsigned char *resp, char *ipaddress)
00082 {
00083 
00084     int ID = (((int)resp[0]) <<8) + resp[1];
00085     int QR = resp[2] >>7;
00086     int Opcode = (resp[2]>>3) & 0x0F;
00087     int RCODE = (resp[3] & 0x0F);
00088     int ANCOUNT = (((int)resp[6])<<8)+ resp[7];
00089 
00090     if ((ID != 1) || (QR != 1) || (Opcode != 0) || (RCODE != 0)) {
00091         return false;
00092     }
00093 
00094     int c = 12;
00095     int d;
00096     //  Skip domain question
00097     while(  (d=resp[c++]) != 0) {
00098         c+=d;
00099     }
00100     c+= 4; //   skip QTYPE and QCLASS
00101 
00102     //  Here comes the resource record
00103     for (int ans = 0 ; ans < ANCOUNT; ans++) {
00104         if (parseRR(resp, c, ipaddress)) {
00105             return true;
00106         }
00107     }
00108 
00109     return false;
00110 }
00111 
00112 static int32_t query(UDPSocket *socket, const SocketAddress &addr, const char *hostname, char *ipaddress)
00113 {
00114     int len = 0;
00115     if (hostname == NULL) {
00116         return false;
00117     }
00118     len = strlen(hostname);
00119     if ((len > 128) || (len == 0)) {
00120         return false;
00121     }
00122 
00123     int packetlen = /* size of HEADER structure */ 12 + /* size of QUESTION Structure */5 + len + 1;
00124     uint8_t *packet = new uint8_t[packetlen]; /* this is the UDP packet to send to the DNS */
00125     if (packet == NULL) {
00126         return false;
00127     }
00128 
00129     //  Fill the header
00130     memset(packet, 0, packetlen);
00131     packet[1] = 1;      //  ID = 1
00132     packet[5] = 1;      // QDCOUNT = 1 (contains one question)
00133     packet[2] = 1;      // recursion requested
00134 
00135     int c = 13;         //  point to NAME element in question section or request
00136     int cnt = 12;       //  points to the counter of
00137     packet[cnt] = 0;
00138     for (int i = 0 ; i < len ; i++) {
00139         if (hostname[i] != '.') {
00140             //  Copy the character and increment the character counter
00141             packet[cnt]++;
00142             packet[c++] = hostname[i];
00143         } else {
00144             //  Finished with this part, so go to the next
00145             cnt = c++;
00146             packet[cnt] = 0;
00147         }
00148     }
00149 
00150     //  Terminate this domain name with a zero entry
00151     packet[c++] = 0;
00152 
00153     //  Set QTYPE
00154     packet[c++] = 0;
00155     packet[c++] = 1;
00156     //  Set QCLASS
00157     packet[c++] = 0;
00158     packet[c++] = 1;
00159 
00160 
00161     if (socket->sendto(addr, packet, packetlen) < 0) {
00162         delete packet;
00163         return false;
00164     }
00165     delete packet;
00166 
00167     packet = new uint8_t [1024];
00168 
00169     //  Receive the answer from DNS
00170     int response_length = 0;
00171     response_length = socket->recvfrom(NULL, packet, 1024);
00172 
00173     if (response_length > 0 ) {
00174         if (!resolve(packet, ipaddress)) {
00175             delete packet;
00176             return NSAPI_ERROR_DNS_FAILURE;
00177         }
00178         
00179         //  cleanup and return
00180         delete packet;
00181         return 0;
00182     }
00183     
00184     delete packet;
00185     return NSAPI_ERROR_DNS_FAILURE;
00186 }
00187 
00188 int32_t dnsQuery(NetworkStack *iface, const char *host, char *ip)
00189 {
00190     if (isIP(host)) {
00191         strcpy(ip, host);
00192         return 0;
00193     }
00194 
00195     UDPSocket sock(iface);
00196 
00197     for (unsigned i = 0; i < DNS_COUNT; i++) {
00198         return query(&sock, SocketAddress(DNS_IPS[0], 53), host, ip);
00199     }
00200 
00201     return NSAPI_ERROR_DNS_FAILURE;
00202 }
00203 
00204 int32_t dnsQuery(UDPSocket *socket, const char *host, char *ip)
00205 {
00206     if (isIP(host)) {
00207         strcpy(ip, host);
00208         return 0;
00209     }
00210 
00211     for (unsigned i = 0; i < DNS_COUNT; i++) {
00212         return query(socket, SocketAddress(DNS_IPS[0], 53), host, ip);
00213     }
00214 
00215     return NSAPI_ERROR_DNS_FAILURE;
00216 }