Library to resolve text URLs to IP addresses (IPv4)
Dependents: NetworkSocketAPI NetworkSocketAPI Nucleo-AWS-IoT-mbed
Diff: DnsQuery.cpp
- Revision:
- 0:fff4b9055396
- Child:
- 1:5d978992a518
diff -r 000000000000 -r fff4b9055396 DnsQuery.cpp --- /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