#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include "dns_protocol.h"
//DNS服务器端口,此处5002端口为连接server.c所用
//若请求公共DNS服务器,如114.114.114.114,则使用53端口
#define PORT 5002
char dns_servers[1][16];//存放DNS服务器的IP
int dns_server_count = 0;
void ngethostbyname(unsigned char*, int);
unsigned char* ReadName (unsigned char*,unsigned char*,int*);
void ChangetoDnsNameFormat(unsigned char*, unsigned char*);
int main(int argc, char *argv[]) {
unsigned char hostname[100];
unsigned char dns_servername[100];
printf("请输入DNS服务器的IP:");
scanf("%s", dns_servername);
strcpy(dns_servers[0], dns_servername);
printf("请输入要查询IP的主机名:");
scanf("%s", hostname);
//由域名获得IPv4地址,A是查询类型
ngethostbyname(hostname, T_A);
return 0;
}
/*
**实现DNS查询功能
*/
void ngethostbyname(unsigned char *host, int query_type) {
unsigned char buf[65536], *qname, *reader;
int i, j, stop, s;
struct sockaddr_in a;//地址
struct RES_RECORD answers[20], auth[20], addit[20];//回答区域、授权区域、附加区域中的资源数据字段
struct sockaddr_in dest;//地址
struct DNS_HEADER *dns = NULL;
struct QUESTION *qinfo = NULL;
printf("\n待解析域名:%s", host);
s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); //建立分配UDP套结字
dest.sin_family = AF_INET;//IPv4
dest.sin_port = htons(PORT);//DNS服务器端口号
dest.sin_addr.s_addr = inet_addr(dns_servers[0]);//DNS服务器IP
dns = (struct DNS_HEADER *) &buf;
/*设置DNS报文首部*/
dns->id = (unsigned short) htons(getpid());//id设为进程标识符
dns->qr = 0; //查询
dns->opcode = 0; //标准查询
dns->aa = 0; //不授权回答
dns->tc = 0; //不可截断
dns->rd = 1; //期望递归
dns->ra = 0; //不可用递归
dns->z = 0; //必须为0
dns->ad = 0;
dns->cd = 0;
dns->rcode = 0;//没有差错
dns->q_count = htons(1); //1个问题
dns->ans_count = 0;
dns->auth_count = 0;
dns->add_count = 0;
//qname指向查询问题区域的查询名字段
qname = (unsigned char*) &buf[sizeof(struct DNS_HEADER)];
ChangetoDnsNameFormat(qname, host);//修改域名格式
qinfo = (struct QUESTION*) &buf[sizeof(struct DNS_HEADER)
+ (strlen((const char*) qname) + 1)]; //qinfo指向问题查询区域的查询类型字段
qinfo->qtype = htons(query_type); //查询类型为A
qinfo->qclass = htons(1); //查询类为1
//向DNS服务器发送DNS请求报文
printf("\n\n发送报文中...");
if (sendto(s, (char*) buf,sizeof(struct DNS_HEADER) + (strlen((const char*) qname) + 1)+ sizeof(struct QUESTION), 0, (struct sockaddr*) &dest,sizeof(dest)) < 0)
{
perror("ERROR on sending!");
}
printf("发送成功!");
//从DNS服务器接受DNS响应报文
i = sizeof dest;
printf("\n接收报文中...");
if (recvfrom(s, (char*) buf, 65536, 0, (struct sockaddr*) &dest,(socklen_t*) &i) < 0) {
perror("ERROR on receive!");
}
printf("接收成功!");
dns = (struct DNS_HEADER*) buf;
//将reader指向接收报文的回答区域
reader = &buf[sizeof(struct DNS_HEADER) + (strlen((const char*) qname) + 1) + sizeof(struct QUESTION)];
printf("\n\n响应报文包含: ");
printf("\n %d个问题", ntohs(dns->q_count));
printf("\n %d个回答", ntohs(dns->ans_count));
printf("\n %d个授权服务", ntohs(dns->auth_count));
printf("\n %d个附加记录\n\n", ntohs(dns->add_count));
/*
**解析接收报文
*/
stop=0;
for(i=0;i<ntohs(dns->ans_count);i++)
{
answers[i].name=ReadName(reader,buf,&stop);
reader = reader + stop;
answers[i].resource = (struct R_DATA*)(reader);
reader = reader + sizeof(struct R_DATA);
if(ntohs(answers[i].resource->type) == T_A) //if its an ipv4 address
{
answers[i].rdata = (unsigned char*)malloc(ntohs(answers[i].resource->data_len));
for(j=0 ; j<ntohs(answers[i].resource->data_len) ; j++)
{
answers[i].rdata[j]=reader[j];
}
answers[i].rdata[ntohs(answers[i].resource->data_len)] = '\0';
reader = reader + ntohs(answers[i].resource->data_len);
}
else
{
answers[i].rdata = ReadName(reader,buf,&stop);
reader = reader + stop;
}
}
printf("\nAnswer Records : %d \n" , ntohs(dns->ans_count) );
for(i=0 ; i < ntohs(dns->ans_count) ; i++)
{
printf("Name : %s ",answers[i].name);
if( ntohs(answers[i].resource->type) == T_A) //IPv4 address
{
long *p;
p=(long*)answers[i].rdata;
a.sin_addr.s_addr=(*p); //working without ntohl
printf("has IPv4 address : %s",inet_ntoa(a.sin_addr));
}
if(ntohs(answers[i].resource->type) == T_CNAME)
{
//Canonical name for an alias
printf("has alias name : %s",answers[i].rdata);
}
printf("\n");
}
}
u_char* ReadName(unsigned char* reader,unsigned char* buffer,int* count)
{
unsigned char *name;
unsigned int p=0,jumped=0,offset;
int i , j;
*count = 1;
name = (unsigned char*)malloc(256);
name[0]='\0';
//read the names in 3www6google3com format
while(*reader!=0)
{
if(*reader>=192)
{
offset = (*reader)*256 + *(reader+1) - 49152; //49152 = 11000000 00000000 ;)
reader = buffer + offset - 1;
jumped = 1; //we have jumped to another location so counting wont go up!
}
else
{
name[p++]=*reader;
}
reader = reader+1;
if(jumped==0)
{
*count = *count + 1; //if we havent jumped to another location then we can count up
}
}
name[p]='\0'; //string complete
if(jumped==1)
{
*count = *count + 1; //number of steps we actually moved forward in the packet
}
//now convert 3www6google3com0 to www.google.com
for(i=0;i<(int)strlen((const char*)name);i++)
{
p=name[i];
for(j=0;j<(int)p;j++)
{
name[i]=name[i+1];
i=i+1;
}
name[i]='.';
}
name[i-1]='\0'; //remove the last dot
return name;
}
/*
**从www.baidu.com转换到3www5baidu3com
*/
void ChangetoDnsNameFormat(unsigned char* dns, unsigned char* host) {
int lock = 0, i;
strcat((char*) host, ".");
for (i = 0; i < strlen((char*) host); i++) {
if (host[i] == '.') {
*dns++ = i - lock;
for (; lock < i; lock++) {
*dns++ = host[lock];
}
lock++;
}
}
*dns++ = '\0';
}