DNS查询器的linux C实例程序
来源:互联网 发布:阿达帕林对黑头知乎 编辑:程序博客网 时间:2024/05/16 02:07
RFC 1034说明了DNS的概念和功能,RFC 1035详细说明了DNS的规范和实现。通过阅读RFC,我们知道明白了,应用程序对DNS的访问是通过解析器来(resolver)完成的,解析器并不像TCP/IP协议那样是OS的内核,而是通过网络访问DNS服务器来得到名字和地址的对应关系。OS的TCP/IP协议簇对DNS一点都知道。
工欲善其事必先利其器,先得进行些基础知识的复习:《bit与byte的区别》和 《bit与byte的联系》及《位运算》,一个int是4个byte(十六进制中01 02 03 04转化为十进制为16909060),一个char是1个byte(十六进制中97转化为字符为a)。例如在十六进制中0x80,用bit来表示就是1000 0000,此时如果我们对它实施位(>>5)运算,得到的结果就是0000 0100,十六进制值为0x04。
在Linux的内核代码中,经常可以看见形如#define do{ }while(0)的宏定义,是否感到疑惑呢?宏定义只是帮助我们进行替换而已,当定义多条语句时,会在if...else...语句中产生歧义,详细解释参考链接。(小插曲,我在测试中只#include <stdlib.h>,忘记了#include <stdio.h>,然后在后面使用了printf等,结果编译的时候产生警告:
warning: incompatible implicit declaration of built-in function ‘printf’
一般的DNS是基于UDP,报文格式如下图:
从首部开始,0~15位bit刚好是2个byte,由客户程序设置并由服务器返回,客户程序通过它来确定响应是否与查询匹配(例如,客户程序在这里输入的是十六进制的0xD8B4,那么服务器的该字段也会填入相同的值。这个标识又称为Transaction ID,在《DNS欺骗技术原理与安全防范技术》中有更详细的讨论)。
接下来的16~31位bit刚好也是2个byte,用作协议的标志位
- QR是1个bit位:0代表查询报文,1代表相应报文
- opcode是4个bit位字段:0代表标准查询,1代表反向查询,2代表服务器状态请求
- AA是1个bit位,是Authoritative Answer的缩写,指明名字服务器是授权于该域的
- TC是1个bit位,是Truncated的缩写,意为可截断的,指明在UDP中应答报文超过512字节时,只返回512字节
- RD是1个bit位,是Recursion Desired的缩写,意为期望递归,期望名字服务器必须处理这个查询,而不是给出一个迭代查询服务器的列表
- RA是1个bit位,是Recursion Available的缩写,意为可用递归,如果名字服务器支持递归查询,这会将此位设置为1
- zero是3个bit位,设置为0
- rcode是4个bit位,表示名字差错,0为无差错,3为有差错。当查询中指定的域不存在的时候,就返回3
再接着是4段16位bit:
- QuestionCount 查询问题记录数由客户端填写,服务器端按原值返回
- AnswerCount 资源记录数由服务器端填写,代表有多少适应这个问题记录的对应IP
- NameServerCount 授权资源记录数,一般为0
- AdditionalCount 额外资源记录数,一般为0
分析完报文头,现在该是报文体了。分为四大块:
- 查询问题
- 回答
- 授权
- 额外信息
接着就是查询类型,该类型就是针对查询问题的,在RFC中有详细的描述,一般使用如下表:
类型
值
描述
A
1
IP地址
NS
2
名字服务器
MD
3
邮件目的的(已过时,请用MX)
MF
4
邮件中转站(已过时,请用MX)
CNAME
5
规范名词
SOA
6
xxx
MB
7
邮箱记录名(实验性质)
MG
8
邮件组成员(实验性质)
MR
9
邮件更改后记录名(实验性质)
NULL
10
空RR(实验性质)
WKS
11
众所皆知的服务描述
PTR
12
指针记录
HINFO
13
主机信息
MINFO
14
邮箱或者邮件列表信息
MX
15
邮件交换记录
TXT
16
文本字符串
最常用的查询类型为A,表示期望获得查询名对应的IP地址。最后的查询类,通常是1,指互联网地址剩下的3个字段是:回答,授权和额外信息,均采用资源记录RR(Resource Record)格式,如下图:
=============================================================================
下例执行的环境在Debian4.0上,编译工具为gcc,DNS服务器地址为192.168.1.1(通常该服务的默认监听端口为53),文件名为DNSClient.c:
#include<stdio.h>
#include<stdlib.h>
#include<error.h>
#include<string.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<time.h>
staticvoid printmessage(unsignedchar *buf);
staticunsigned char *printnamestring(unsignedchar *p,unsigned char *buf);
#define GETWORD(__w,__p)do{__w=*(__p++)<<8;__w|=*(p++);}while(0)
#define GETLONG(__l,__p)do{__l=*(__p++)<<24;__l|=*(__p++)<<16;__l|=*(__p++)<<8;__l|=*(p++);}while(0)
int main(int argc,char* argv[])
{
if(argc != 2)
{
printf("usage: dnsclient <host_name>\n");
return -1;
}
time_t ident;
int fd;
int rc;
int serveraddrlent;
char *q;
unsigned char *p;
unsigned char *countp;
unsigned char reqBuf[512] = {0};
unsigned char rplBuf[512] = {0};
struct sockaddr_in serveraddr;
//udp
fd = socket(AF_INET, SOCK_DGRAM, 0);
if(fd == -1)
{
perror("error create udp socket");
return -1;
}
time(&ident);
//copy
p = reqBuf;
//Transaction ID
*(p++) = ident;
*(p++) = ident>>8;
//Header section
//flag word = 0x0100
*(p++) = 0x01;
*(p++) = 0x00;
//Questions = 0x0001
//just one query
*(p++) = 0x00;
*(p++) = 0x01;
//Answer RRs = 0x0000
//no answers in this message
*(p++) = 0x00;
*(p++) = 0x00;
//Authority RRs = 0x0000
*(p++) = 0x00;
*(p++) = 0x00;
//Additional RRs = 0x0000
*(p++) = 0x00;
*(p++) = 0x00;
//Query section
countp = p;
*(p++) = 0;
for(q=argv[1]; *q!=0; q++)
{
if(*q != '.')
{
(*countp)++;
*(p++) = *q;
}
else if(*countp != 0)
{
countp = p;
*(p++) = 0;
}
}
if(*countp != 0)
*(p++) = 0;
//Type=1(A):host address
*(p++)=0;
*(p++)=1;
//Class=1(IN):internet
*(p++)=0;
*(p++)=1;
printf("\nRequest:\n");
printmessage(reqBuf);
//fill
bzero(&serveraddr,sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(53);
serveraddr.sin_addr.s_addr = inet_addr("192.168.1.1");
//send to DNS Serv
if(sendto(fd,reqBuf,p-reqBuf,0,(void *)&serveraddr,sizeof(serveraddr)) < 0)
{
perror("error sending request");
return -1;
}
//recev the reply
bzero(&serveraddr,sizeof(serveraddr));
serveraddrlent =sizeof(serveraddr);
rc = recvfrom(fd,&rplBuf,sizeof(rplBuf),0,(void *)&serveraddr,&serveraddrlent);
if(rc < 0)
{
perror("error receiving request\n");
return -1;
}
//print out results
printf("\nReply:\n");
printmessage(rplBuf);
//exit
printf("Program Exit\n");
return 0;
}
staticvoid printmessage(unsignedchar *buf)
{
unsigned char *p;
unsigned int ident,flags,qdcount,ancount,nscount,arcount;
unsigned int i,j,type,class,ttl,rdlength;
p = buf;
GETWORD(ident,p);
printf("ident=%#x\n",ident);
GETWORD(flags,p);
printf("flags=%#x\n",flags);
//printf("qr=%u\n",(flags>>15)&1);
printf("qr=%u\n",flags>>15);
printf("opcode=%u\n",(flags>>11)&15);
printf("aa=%u\n",(flags>>10)&1);
printf("tc=%u\n",(flags>>9)&1);
printf("rd=%u\n",(flags>>8)&1);
printf("ra=%u\n",(flags>>7)&1);
printf("z=%u\n",(flags>>4)&7);
printf("rcode=%u\n",flags&15);
GETWORD(qdcount,p);
printf("qdcount=%u\n",qdcount);
GETWORD(ancount,p);
printf("ancount=%u\n",ancount);
GETWORD(nscount,p);
printf("nscount=%u\n",nscount);
GETWORD(arcount,p);
printf("arcount=%u\n",arcount);
for(i=0; i<qdcount; i++)
{
printf("qd[%u]:\n",i);
while(*p!=0)
{
p = printnamestring(p,buf);
if(*p != 0)
printf(".");
}
p++;
printf("\n");
GETWORD(type,p);
printf("type=%u\n",type);
GETWORD(class,p);
printf("class=%u\n",class);
}
for(i=0; i<ancount; i++)
{
printf("an[%u]:\n",i);
p = printnamestring(p,buf);
printf("\n");
GETWORD(type,p);
printf("type=%u\n",type);
GETWORD(class,p);
printf("class=%u\n",class);
GETLONG(ttl,p);
printf("ttl=%u\n",ttl);
GETWORD(rdlength,p);
printf("rdlength=%u\n",rdlength);
printf("rd=");
for(j=0; j<rdlength; j++)
{
printf("%2.2x(%u)",*p,*p);
p++;
}
printf("\n");
}
}
staticunsigned char *printnamestring(unsignedchar *p,unsigned char *buf)
{
unsigned int nchars,offset;
nchars = *(p++);
if((nchars & 0xc0) == 0xc0)
{
offset = (nchars & 0x3f) << 8;
offset |= *(p++);
nchars = buf[offset++];
printf("%*.*s",nchars,nchars,buf+offset);
}
else
{
printf("%*.*s",nchars,nchars,p);
p += nchars;
}
return (p);
}
编译命令为
lsj@debian007:~$ gcc -g -Wall -o DNSClient DNSClient.c
然后执行:
lsj@debian007:~$ ./DNSClient bigdogchina.cublog.cn
就可以看见结果啦,这里使用的是UDP的数据格式,我们知道UDP的头部有一个16bit的长度,那么能表示的最大长度为2的16次方65536,再减去包头20,所以UDP包最大长度为65536-20=65516,但是在实际应用中,最好不要超过1K
- DNS查询器的linux C实例程序
- DIY一个DNS查询器:程序实现
- linux/unix 下的DNS有效查询
- C#:DNS HostName查询
- linux命令行学习-dig(DNS查询器)
- DNS报文格式及DNS查询程序
- linux c写的一个航班查询的程序
- linux c程序调用lua代码的实例
- LINUX C语言 DNS
- DNS的查询方式
- DNS的查询过程
- Linux查询子网掩码网关DNS
- linux下C程序:运行单个实例
- linux下c程序访问mysql实例
- linux c实现dns域名解析
- DNS 查询的工作原理
- DNS查询的工作原理
- DNS 查询的工作原理
- UIImage 图片处理:截图,缩放,设定大小,存储
- log4j在spring应用中的配置
- ViewPager onPageChangeListener总结
- C#OOP中的集合
- 1288元,苍井空“空系列”内衣微博开卖
- DNS查询器的linux C实例程序
- glib与glibc的区别
- MySQL内核:InnoDB存储引擎 卷1
- 史上最牛高三老师:请保持光棍的节操
- 上百个Android开源项目分享
- PAT3-04. 一元多项式的乘法与加法运算
- 通过yum安装phpMyAdmin及配置过程
- 怎样申请微信支付接口
- Android架构分析之Android智能指针(一)