You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

608 lines
15 KiB

/**
@file dns.c
@brief Realize Simple Domain Name System Protocol \n
Detail DNS protocol refer to RFC 1034
*/
#include <string.h>
#include "types.h"
#include "delay.h"
#include "socket.h"
#include "myprintf.h"
#include "sockutil.h"
#include "util.h"
//#include "config.h"
#include "w5100.h"
#include "dns.h"
#define DEBUG_DNS
static u_short dns_id = 0x1122; /**< DNS query id */
static u_char* dns_buf;
static u_char* get_domain_name;
static u_long get_domain_ip; /**< Resolved ip address */
static QUERYDATA query_data; /**< Query type */
//extern NETCONF NetConf;
u_long dns_server_ip = 0; /**< instead of "NETCONF" */
static int dns_makequery(u_char op,char * qname); /* make a DNS query */
static int dns_parse_reponse(void); /* analyze a response from DNS sever */
static u_char * dns_parse_question(u_char * cp); /* analyze a question part in resources recode */
static u_char * dns_answer(u_char *cp); /* analyze a answer part in resources recode */
static int parse_name(char* cp,char* qname, u_int qname_maxlen); /* analyze a qname in each part. */
/**
@brief Transfer query message for user entered domain name to desiginated DNS Server
@return 1 - DNS resolve Success, 0 - DNS resolve Failue
*/
u_char dns_query(
SOCKET s, /**< socket handle */
u_long dnsip, /**< DNS server ip address(32bit network ordering address) */
u_char * domain_name, /**< if querydata value is BYNAME then use parameter for resolving domain ip \n
BYIP then use return value(resolved domain name from DNS) */
u_long* domain_ip, /**< if querydata value is BYNAME then use return value(resolved domain ip from DNS) \n
BYIP then use parameter for resolving domain name */
QUERYDATA querydata, /**< BYNAME : use domain_name for resolving domain_ip \n
BYIP : use domain_ip for resolving domain_name */
u_int elapse /**< wait for resopnse from DNS server (unit : 10ms) */
)
{
int len;
u_int port;
u_char reponse_received = 0;
u_char* qname = 0;
dns_buf = (u_char*) TX_BUF;
get_domain_name = dns_buf + MAX_DNSMSG_SIZE;
query_data = querydata;
if(querydata==BYNAME) qname = domain_name;
else
{
qname = get_domain_name + MAX_QNAME_LEN;
////---- MODIFY_2006_06_05 :
/*2. Host address to host name translation
This function will often follow the form of previous
functions. Given a 32 bit IP address, the caller wants a
character string. The octets of the IP address are reversed,
used as name components, and suffixed with "IN-ADDR.ARPA". A
type PTR query is used to get the RR with the primary name of
the host. For example, a request for the host name
corresponding to IP address 1.2.3.4 looks for PTR RRs for
domain name "4.3.2.1.IN-ADDR.ARPA". */
// Check little or big endian
PRINTLN1("domain_ip=%08lx",domain_ip);
if(*domain_ip != ntohl(*domain_ip)) // if domain_ip is little-endian
strcpy(qname,inet_ntoa(*domain_ip));
else strcpy(qname,inet_ntoa(swapl(*domain_ip))); // if domain_ip is big-endian then make the reverse ip address string.
////---- MODIFY_END
strcat(qname,".in-addr.arpa");
}
if(socket(s, Sn_MR_UDP, 0, 0) == 0) return 0;
#ifdef DEBUG_DNS
DPRINTLN2("Querying server %s by %s", inet_ntoa(ntohl(dnsip)), qname);
#endif
len = dns_makequery(OP_QUERY, qname); // create DNS query
if(!len)
{
#ifdef DEBUG_DNS
DPRINTLN("Fail to make query");
#endif
return 0;
}
#ifdef DEBUG_DNS
/*
{
int i;
DPRINT1("%d length dns query is made.",len);
for(i = 0; i < len; i++)
{
if(!(i % 0x10)) DPRINTLN("");
DPRINT1("%02X ",dns_buf[i]);
}
DPRINTLN("\r\n");
}
*/
#endif
sendto(s, dns_buf, len, (u_char*)&dnsip, IPPORT_DOMAIN); // DNS query send to DNS server
#ifdef DEBUG_DNS
DPRINTLN1("sent dns query to DNS server : [%s]",inet_ntoa(ntohl(dnsip)));
#endif
while (elapse-- > 0)
{
wait_10ms(1); // wait until Domain name resolution
if ((len = getSn_RX_RSR(s)) > 0)
{
if (len > MAX_DNSMSG_SIZE) len = MAX_DNSMSG_SIZE;
len = recvfrom(s, dns_buf, len, (char*)&dnsip, &port);
reponse_received = 1;
break;
}
}
close(s);
if(!reponse_received)
{
#ifdef DEBUG_DNS
DPRINTLN("No response from DNS server");
#endif
return 0;
}
#ifdef DEBUG_DNS
/*
{
int i;
DPRINT2("%d received resonse from DNS Server[%s]\r\n",len,inet_ntoa(ntohl(dnsip)));
for(i = 0; i < len ; i++)
{
if(!(i%0x10)) DPRINTLN("");
DPRINT1("%02X ",dns_buf[i]);
}
DPRINTLN("\r\n");
}
*/
#endif
if(!dns_parse_reponse()) // Convert to local format
{
#ifdef DEBUG_DNS
DPRINTLN("Fail to parse reponse from DNS server");
#endif
return 0;
}
if(query_data == BYNAME) *domain_ip = get_domain_ip;
else strcpy(domain_name, get_domain_name);
return 1;
}
/**
@brief Create query message for transferring to DNS server
@return size of query message
*/
static int dns_makequery(
u_char op, /**< query<72><79> opcode (input) */
char * qname /**< size of query message */
)
{
u_char* query = dns_buf;
u_char* domain_tok;
u_int domain_len;
u_int qtype = (query_data) ? TYPE_PTR : TYPE_A; // Domain name pointer or Host Address
u_int qclass = CLASS_IN; // Internet
/* Make Qurey Header */
memset(dns_buf,0,MAX_DNSMSG_SIZE);
*((u_short*)query) = htons(dns_id);
#ifdef DEBUG_DNS
DPRINT2("query : id = %02X%02X, ",query[0],query[1]);
#endif
query += 2;
*query = MAKE_FLAG0(QR_QUERY,op,0,0,1); // Recursion desired
#ifdef DEBUG_DNS
DPRINT1("opcode = %02X, ",*query);
#endif
query++;
*query = MAKE_FLAG1(0,0,0);
#ifdef DEBUG_DNS
DPRINTLN1("rcode = %02X",*query);
#endif
query++;
*((u_short*)query) = htons(1);
query = dns_buf + DHDR_SIZE;
/* Make Question Section */
while(1) // fill in QNAME filed with domain_name
{
domain_tok = strchr(qname,'.');
if(domain_tok) domain_len = ((u_int)domain_tok - (u_int)qname) & 0xFF;
else domain_len = strlen(qname);
if(domain_len > 63)
{
#ifdef DEBUG_DNS
DPRINTLN("Invalid label length because labels are restricted to 63 octets or less.");
#endif
return 0; // since the label must begin with two zero bits because labels are restricted to 63 octets or less.
}
*query++ = domain_len;
memcpy(query,qname,domain_len);
qname += domain_len+1;
query += domain_len;
if(!domain_tok) break;
}
*query++ = '\0'; // terminate QNAME field with 'NULL'
// fill in QTYPE field, for host address
*query++ = qtype >> 8 & 0xFF;
*query++ = qtype & 0xFF;
// fill in QCLASS field, for internet
*query++ = qclass >> 8 & 0xFF;
*query++ = qclass & 0xFF;
return (int)(query - dns_buf); // return the size of generated query
}
/**
@brief Analyze received DNS message packet and store it into structure
@return success - 1, fail - 0
*/
static int dns_parse_reponse(void)
{
u_int i;
DHDR dhdr;
char* cur_ptr = dns_buf;
dhdr.id = ntohs(*((u_short*)cur_ptr));
if(dhdr.id != dns_id)
{
#ifdef DEBUG_DNS
DPRINTLN2("Responsed ID != Query ID : %d ! = %d",dhdr.id, dns_id);
#endif
return 0;
}
dns_id++;
cur_ptr += 2;
dhdr.flag0 = *cur_ptr++;
dhdr.flag1 = *cur_ptr++;
if(!(dhdr.flag0 & 0x80)|| !(dhdr.flag1 & 0x80) )
{
#ifdef DEBUG_DNS
DPRINTLN2("No reponse message, flag0 = 0x%02X, flag1 = 0x%02X",dhdr.flag0,dhdr.flag1);
#endif
return 0;
}
if(dhdr.flag1 & 0x0F)
{
#ifdef DEBUG_DNS
DPRINT("Error of reponse : ");
switch(dhdr.flag1 & 0x0F)
{
case RC_FORMAT_ERROR:
DPRINTLN("Format Error");
break;
case RC_SERVER_FAIL:
DPRINTLN("Server failure");
break;
case RC_NAME_ERROR:
DPRINTLN("Name Error");
break;
case RC_NOT_IMPL:
DPRINTLN("Not Implemented");
break;
case RC_REFUSED:
DPRINTLN("Refused");
break;
}
#endif
return 0;
}
dhdr.qdcount = ntohs(*((u_short*)cur_ptr));
cur_ptr += 2;
dhdr.ancount = ntohs(*((u_short*)cur_ptr));
cur_ptr += 2;
dhdr.nscount = ntohs(*((u_short*)cur_ptr));
cur_ptr += 2;
dhdr.arcount = ntohs(*((u_short*)cur_ptr));
cur_ptr += 2;
#ifdef DEBUG_DNS
DPRINTLN2("Response : question count = %d, answer count = %d",dhdr.qdcount,dhdr.ancount);
DPRINTLN2("Response : authority count = %d, additiional count = %d",dhdr.nscount,dhdr.arcount);
#endif
/* Now parse the variable length sections */
for(i = 0; i < dhdr.qdcount; i++)
{
cur_ptr = dns_parse_question(cur_ptr); // Question section
if(!cur_ptr)
{
#ifdef DEBUG_DNS
DPRINTLN1("Fail to parse question section%d",i);
#endif
return 0;
}
}
/* parse resource records */
for(i=0; i < dhdr.ancount; i++)
{
cur_ptr = dns_answer(cur_ptr); // Answer section
if(!cur_ptr)
{
#ifdef DEBUG_DNS
DPRINTLN1("Fail to parse answer section%d",i);
#endif
return 0;
}
}
for(i = 0; i < dhdr.nscount; i++) // Name server (authority) section
{
// if you need to authority section, insert user parse fuction into here.
}
for(i = 0; i < dhdr.arcount; i++) // Additional section
{
// if you need to additional section , insert user parse fuction into here.
}
return 1;
}
/**
@brief Parse question section in the DNS query
@return success - 1, fail - 0
*/
static u_char * dns_parse_question(
u_char * cp /**< curruent pointer to be parsed */
)
{
int len;
char name[MAX_QNAME_LEN];
len = parse_name(cp, name, sizeof(name));
if(!len)
{
#ifdef DEBUG_DNS
DPRINTLN("Fail to parse (Q)NAME field");
#endif
return 0;
}
cp += len;
cp += 2; // skip type
cp += 2; // skip class
#ifdef DEBUG_DNS
DPRINTLN1("In question section, (Q)NAME field value : %s",name);
#endif
return cp;
}
/**
@brief Parse answer section in the DNS query. Store resolved IP address into destination address
@return end address of answer section, fail - 0
*/
static u_char * dns_answer(
u_char *cp
)
{
int len, type;
char qname[MAX_QNAME_LEN];
u_long tip;
len = parse_name(cp, qname, sizeof(qname));
if(!len) return 0;
cp += len;
type = *cp++;
type = (type << 8) + (u_int)*cp++; // type
cp += 2; // skip class
cp += 4; // skip ttl
cp += 2; // skip len
switch(type)
{
case TYPE_A:
tip = 0;
*((u_char*)&tip) = *cp++; // Network odering
*(((u_char*)&tip) + 1) = *cp++;
*(((u_char*)&tip) + 2) = *cp++;
*(((u_char*)&tip) + 3) = *cp++;
#ifdef DEBUG_DNS
DPRINTLN1("RRs : TYPE_A = %s", inet_ntoa(ntohl(tip)));
#endif
if(query_data == BYNAME) get_domain_ip = tip;
break;
case TYPE_CNAME:
case TYPE_MB:
case TYPE_MG:
case TYPE_MR:
case TYPE_NS:
case TYPE_PTR:
len = parse_name(cp, qname, sizeof(qname)); // These types all consist of a single domain name
if(!len) return 0; // convert it to ascii format
cp += len;
if(query_data == BYIP && type == TYPE_PTR)
{
strcpy(get_domain_name,qname);
#ifdef DEBUG_DNS
DPRINTLN1("RRs : TYPE_PTR = %s",qname);
#endif
}
break;
case TYPE_HINFO:
len = *cp++;
cp += len;
len = *cp++;
cp += len;
break;
case TYPE_MX:
cp += 2;
len = parse_name(cp, qname, sizeof(qname)); // Get domain name of exchanger
if(!len)
{
#ifdef DEBUG_DNS
DPRINTLN("TYPE_MX : Fail to get domain name of exechanger");
#endif
return 0;
}
cp += len;
break;
case TYPE_SOA:
len = parse_name(cp, qname, sizeof(qname)); // Get domain name of name server
if(!len)
{
#ifdef DEBUG_DNS
DPRINTLN("TYPE_SOA : Fail to get domain name of name server");
#endif
return 0;
}
cp += len;
len = parse_name(cp, qname, sizeof(qname)); // Get domain name of responsible person
if(!len)
{
#ifdef DEBUG_DNS
DPRINTLN("TYPE_SOA : Fail to get domain name of responsible person");
#endif
return 0;
}
cp += len;
cp += 4;
cp += 4;
cp += 4;
cp += 4;
cp += 4;
break;
case TYPE_TXT:
break; // Just stash
default:
break; // Ignore
}
return cp;
}
/**
@brief Parse answer section in the DNS query. Store resolved IP address into destination address
@return end address of answer section, fail - 0
*/
static int parse_name(
char* cp, /**< Convert a compressed domain name to the human-readable form */
char* qname, /**< store human-readable form(input,output); */
u_int qname_maxlen /**< qname_max_len - max length of qname(input) */
)
{
u_int slen; // Length of current segment
int clen = 0; // Total length of compressed name
int indirect = 0; // Set if indirection encountered
int nseg = 0; // Total number of label in name
for(;;)
{
slen = *cp++; // Length of this segment
if (!indirect) clen++;
if ((slen & 0xc0) == 0xc0) // Is used in compression scheme?
{
cp = &dns_buf[((slen & 0x3f)<<8) + *cp]; // Follow indirection
if(!indirect) clen++;
indirect = 1;
slen = *cp++;
}
if (slen == 0) // zero length == all done
break;
if (!indirect) clen += slen;
if((qname_maxlen -= slen+1) < 0)
{
#ifdef DEBUG_DNS
DPRINTLN("Not enough memory");
#endif
return 0;
}
while (slen-- != 0) *qname++ = (char)*cp++;
*qname++ = '.';
nseg++;
}
if(nseg == 0) *qname++ = '.'; // Root name; represent as single dot
else --qname;
*qname = '\0';
#ifdef DEBUG_DNS
DPRINTLN1("Result of parsing (Q)NAME field : %s",qname);
#endif
return clen; // Length of compressed message // Length of compressed message
}
/**
@brief gethostbyaddr function retrieves the host domain name corresponding to a network address
@return success - 1, fail - 0
*/
int gethostbyaddr(
u_long ipaddr, /**< 32bit network ordering ip address */
char* domain /**< poniter to domain name string resolved from dns server */
)
{
SOCKET s;
//get_netconf(&NetConf);
if(dns_server_ip == 0 || dns_server_ip == 0xFFFFFFFF)
{
DPRINTLN("DNS server ip address is not configured.");
return 0;
}
if((s=getSocket(SOCK_CLOSED,0)) == MAX_SOCK_NUM)
{
DPRINTLN("All socket is alreay used. Not found free socket.");
return 0;
}
if(!dns_query(s,dns_server_ip,domain,&ipaddr,BYIP,1000))
{
DPRINTLN1("Fail to communicate with DNS server[%s]",inet_ntoa(ntohl(dns_server_ip)));
return 0;
}
return 1;
}
/**
@brief gethostbyname function retrieves host ip address corresponding to a host name
@return success - host ip address(32bit network odering address), fail - 0
*/
u_long gethostbyname(
char* hostname /**< host domain name */
)
{
SOCKET s;
u_long hostip=0;
if(dns_server_ip == 0 || dns_server_ip == 0xFFFFFFFF)
{
PRINTLN("DNS server ip address is not configured.");
return 0;
}
PRINTLN1("DNS SERVER : %s",inet_ntoa(ntohl(dns_server_ip)));
if((s=getSocket(SOCK_CLOSED,0)) == MAX_SOCK_NUM)
{
PRINTLN("All socket is alreay used. Not found free socket.");
return 0;
}
if(!dns_query(s,dns_server_ip,hostname,&hostip,BYNAME,1000))
{
PRINTLN1("Fail to communicate with DNS server[%s]",inet_ntoa(ntohl(dns_server_ip)));
hostip = 0;
}
return hostip;
}