DNS协议是互联网最重要的基础协议,也是用的最多的UDP协议之一,现在也开始支持基于TCP的DNS和基于ssl的加密的DNS协议。本文虫虫以一个简单实例带领大家一起学习DNS协议,实现一个简单的DNS查询客户端。DNS协议非常简单,详细可以参考国际互联网工程任务组IETF的rfc1035国际标准。
DNS查询协议由查询头和请求部分组成。
查询头
DNS查询头结构如上所示,实例中,我们需要设置的唯一字段是消息ID(16位值),它将被复制回服务器响应中。RD(设置为1),告诉DNS服务器在需要时递归查询;QDCOUNT(设置为1),表示我们只有一个域可供查找。
请求部分
请求部分,需要设置的字段有:要查询的域名通过QNAME中设置。不必直接放置域名,可以通过”标签”将其拆分为几个部分。实例中,我们的标签将是”www”,然后是”ijz”,然后是”me”。 QNAME要求在每个标签之前加上前缀长度,并且最后以0结尾。所以我们的QNAME将是:
3,w,w,w,3,i,j,z,2,m,e,0
注意,QNAME必须以0结尾。
QTYPE和QCLASS都将设置为1。
服务器响应
在查询后,服务器响应中,解析的IP地址将位于RDATA的最后4个字节中。为了简单处理,我们只需要获取该部分内容即可。
下面是C整个代码源码:
#include <stdio.h>
#include <stdint.h>
#include <arpa/inet.h>
#include <unistd.h>
int main(void)
{
int i;
uint8_t rsp[512];
uint8_t qry[] = {
0xde,0xad, //ID
0x01,0x00, //RD set
0x00,0x01, //QDCOUNT = 1
0x00,0x00, //ANCOUNT = 0
0x00,0x00, //NSCOUNT = 0
0x00,0x00, //ARCOUNT = 0
3,
w,w,w,
3,
i,j,z,
2,
m,e,
0,
0x00,0x01, //QTYPE
0x00,0x01, //QCLASS
};
struct sockaddr_in sv;
socklen_t len;
int s = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
sv.sin_family = AF_INET;
sv.sin_port = htons(53);
sv.sin_addr.s_addr = inet_addr(“114.114.114.114”);
sendto(s,qry,sizeof(qry),0,(struct sockaddr*)&sv,sizeof(sv));
i = recvfrom(s,rsp,512,0,(struct sockaddr*)&sv,&len);
printf(“请求的域名IP地址为: %d.%d.%d.%d\n”,rsp[i-4],rsp[i-3],rsp[i-2],rsp[i-1]);
close(s);
return 0;
}
编译及查询: