Network Services
Fork of W5500Interface_K22F by
Diff: DNSClient/DNSClient.cpp
- Revision:
- 15:14382459c8b7
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/DNSClient/DNSClient.cpp Thu Jun 15 20:29:03 2017 +0000 @@ -0,0 +1,245 @@ +// DNSClient.cpp 2013/8/27 +#include "mbed.h" +#include "mbed_debug.h" +#include "DNSClient.h" +#include "UDPSocket.h" +#include "dnsname.h" +#include "wiznet.h" + +#if DBG_DNS +#define DBG2(...) do{debug("[DNS]%p %d %s ", this,__LINE__,__PRETTY_FUNCTION__); debug(__VA_ARGS__); } while(0); +#else +#define DBG2(...) while(0); +#endif + +static SocketAddress theDnsServer; + +DNSClient::DNSClient() : m_state(MYNETDNS_START), m_udp(NULL) { + m_ns = NULL; + + //default to google + theDnsServer.set_ip_address("8.8.8.8"); // DNS + theDnsServer.set_port(53); // DNS +} + +DNSClient::DNSClient(NetworkStack *ns, const char* hostname) : m_state(MYNETDNS_START), m_udp(NULL) { + m_hostname = hostname; + m_ns = ns; + + //default to google + theDnsServer.set_ip_address("8.8.8.8"); // DNS + theDnsServer.set_port(53); // DNS +} + +DNSClient::DNSClient(NetworkStack *ns, SocketAddress* pHost) : m_state(MYNETDNS_START), m_udp(NULL) { + m_ns = ns; + + //default to google + theDnsServer.set_ip_address("8.8.8.8"); // DNS + theDnsServer.set_port(53); // DNS +} + +DNSClient::~DNSClient() { + if (m_udp) { + delete m_udp; + } +} + +int DNSClient::setup(NetworkStack *ns) { + m_ns = ns; + return 0; +} + +bool DNSClient::set_server(const char* serverip, int port) { + theDnsServer.set_ip_address(serverip); + theDnsServer.set_port(port); + return true; +} + +void DNSClient::callback() +{ + uint8_t buf[512]; + SocketAddress host; + int len = m_udp->recvfrom(&host, (char*)buf, sizeof(buf)); + if (len < 0) { + return; + } + if (memcmp(buf+0, m_id, 2) != 0) { //verify + return; + } + + int rcode = response(buf, len); + if (rcode == 0) { + m_state = MYNETDNS_OK; + } else { + m_state = MYNETDNS_NOTFOUND; + } +} + +int DNSClient::response(uint8_t buf[], int size) { + int rcode = buf[3] & 0x0f; + if (rcode != 0) { + return rcode; + } + int qdcount = buf[4]<<8|buf[5]; + int ancount = buf[6]<<8|buf[7]; + int pos = 12; + while(qdcount-- > 0) { + dnsname qname(buf); + pos = qname.decode(pos); // qname + pos += 4; // qtype qclass + } + while(ancount-- > 0) { + dnsname name(buf); + pos = name.decode(pos); // name + int type = buf[pos]<<8|buf[pos+1]; + pos += 8; // type class TTL + int rdlength = buf[pos]<<8|buf[pos+1]; pos += 2; + int rdata_pos = pos; + pos += rdlength; + if (type == 1) { // A record + m_ip = (buf[rdata_pos]<<24) | (buf[rdata_pos+1]<<16) | (buf[rdata_pos+2]<<8) | buf[rdata_pos+3]; + sprintf(m_ipaddr, "%d.%d.%d.%d", buf[rdata_pos],buf[rdata_pos+1],buf[rdata_pos+2],buf[rdata_pos+3]); + } +#if DBG_DNS + printf("%s", name.str.c_str()); + if (type == 1) { + printf(" A %d.%d.%d.%d\n", + buf[rdata_pos],buf[rdata_pos+1],buf[rdata_pos+2],buf[rdata_pos+3]); + } else if (type == 5) { + dnsname rdname(buf); + rdname.decode(rdata_pos); + printf(" CNAME %s\n", rdname.str.c_str()); + } else { + printf(" TYPE:%d", type); + printfBytes(" RDATA:", &buf[rdata_pos], rdlength); + } +#endif + } + return rcode; +} + +int DNSClient::query(uint8_t buf[], int size, const char* hostname) { + const uint8_t header[] = { + 0x00,0x00,0x01,0x00, // id=0x0000 QR=0 rd=1 opcode=0 rcode=0 + 0x00,0x01,0x00,0x00, // qdcount=1 ancount=0 + 0x00,0x00,0x00,0x00};// nscount=0 arcount=0 + const uint8_t tail[] = {0x00,0x01,0x00,0x01}; // qtype=A qclass=IN + memcpy(buf, header, sizeof(header)); + int t = rand(); + m_id[0] = t>>8; + m_id[1] = t; + memcpy(buf, m_id, 2); + dnsname qname(buf); + int pos = qname.encode(sizeof(header), (char*)hostname); + memcpy(buf+pos, tail, sizeof(tail)); + pos += sizeof(tail); + return pos; +} + +void DNSClient::resolve(const char* hostname) { + if (m_udp == NULL) { + m_udp = new UDPSocket; + m_udp->open(m_ns); + } + m_udp->set_blocking(false); + m_udp->bind(rand()&0x7fff); + uint8_t buf[256]; + int size = query(buf, sizeof(buf), hostname); +#if DBG_DNS + printf("hostname:[%s]\n", hostname); + printHex(buf, size); +#endif + m_udp->sendto(theDnsServer, (char*)buf, size); + m_interval.reset(); + m_interval.start(); +} + +void DNSClient::poll() { +#if DBG_DNS + printf("%p m_state: %d, m_udp: %p\n", this, m_state, m_udp); + wait_ms(400); +#endif + switch(m_state) { + case MYNETDNS_START: + m_retry = 0; + resolve(m_hostname); + m_state = MYNETDNS_PROCESSING; + break; + case MYNETDNS_PROCESSING: + break; + case MYNETDNS_NOTFOUND: + break; + case MYNETDNS_ERROR: + break; + case MYNETDNS_OK: + DBG2("m_retry=%d, m_interval=%d\n", m_retry, m_interval.read_ms()); + break; + } + if (m_interval.read_ms() > 1000) { + m_interval.stop(); + DBG2("timeout m_retry=%d\n", m_retry); + if (++m_retry >= 2) { + m_state = MYNETDNS_ERROR; + } else { + resolve(m_hostname); + m_state = MYNETDNS_PROCESSING; + } + } +} + +//tests for a valid IP address, returns 0 if invalid IP address format, returns ip address if valid +static uint32_t +isValidIP(const char* ip) { + int i1, i2, i3, i4; + int len = strlen(ip); + + if (len < 7) return 0; + + //count the number of dots, there must be 3 + int dotcount = 0; + for (int i = 0; i < len; i++) { + if (ip[i] == '.' ) dotcount++; + } + + //there must be exactly 3 dots + if (dotcount != 3) return 0; + + sscanf((char*)ip, "%d.%d.%d.%d", &i1, &i2, &i3, &i4); + + if (strlen(ip) <= 16 && (i1 >= 0 && i1 <= 255) && (i2 >= 0 && i2 <= 255) && + (i3 >= 0 && i3 <= 255) && (i4 >= 0 && i4 <= 255)) { + return ((i1<<24) | (i2<<16) | (i3<<8) | i4); + } + return 0; +} + +bool DNSClient::lookup(const char* hostname) { + m_hostname = hostname; + + uint32_t ip = isValidIP(hostname); + + //check if hostname is an IP address + if (ip > 0) { + //if it is already an IP address just return immediately + m_ip = ip; + strcpy(m_ipaddr, hostname); + m_state = MYNETDNS_OK; + return true; + } + + m_state = MYNETDNS_START; + while(1) { + poll(); + callback(); + if (m_state != MYNETDNS_PROCESSING) { + break; + } + } + + if (m_udp) { + delete m_udp; + m_udp = NULL; + } + return m_state == MYNETDNS_OK; +}