nslookup 这个命令大家一定非常熟悉,我们常常使用这个命令查看指定域名的IP地址。那这个命令是如何实现的呢?能否自己亲自动手实现一个属于自己的nslookup呢?
准备工具
Wireshark
基本思路
向指定DNS服务器(地址:144.144.144.144,端口:53)发送指定格式的请求报文,然后接收服务器发送来的响应报文,然后从接收到的响应报文中解析出指定IP地址。
请求报文
首先我们必须知道我们要发送的请求报文中要包含哪些数据,这里我们打开浏览器输入www.baidu.com然后使用Wire shark抓取到的请求报文长这个样子:
我们点击Domain Name System(query),我们会看到有下面这几项:
-
Transaction ID: 会话标识
我们通过点击它,会发现下面的请求报文中有对应的两个方块亮起,也就是说这一项会占用两个字节,下面的其他选项的操作都是如此,我就不一一放图了。
占用字节数:2
推荐值: 随机正整数。
推荐使用类型:unsigned short。
-
Flags: 标志
占用字节数目:2
推荐值:0x0100
推荐使用类型: short/unsigned short
这个推荐值有什么说法吗?有的,但是这里我们就不再过多深究了假如想要知道这个值是怎么来的可以自己百度一下或者参考这篇文章link,我们现在只需要明白,如果是想要向DNS服务器发送请求,填这个值就对了。 -
Questions :问题数目
占用字节数目:2
推荐使用类型: short/unsigned short
推荐值:1
这个就很好理解吧,我们每次只向服务器请求一个域名的IP地址,所以只有一个问题。 -
Answer RRs: 回答资源数目
占用字节数目:2
推荐使用类型: short/unsigned short
推荐值:0
我们作为请求的一方,自然是不知道我们请求的域名对应有几个IP地址,所以我们这里填0。 -
Authority RRs: 授权资源记录数目
占用字节数目:2
推荐使用类型: short/unsigned short
推荐值:0 -
Additional RRs: 附加资源记录数目
占用字节数目:2
推荐使用类型: short/unsigned short
推荐值:0
我作为请求方,这个是我可以知道的吗?所以是0。 -
Queries:查询问题区域
这个区域下面有三个项目是我们必须要填的:- Name:想要查询的域名
在请求报文中我们必须将我们想要查询的域名经过格式化服务器才能正确识别,比如我要查询的域名为:www.baidu.com那我必须将域名格式化为:3www5baidu3com\0 才行。 查询的域名不同,此项在请求报文中占用的字节数目也有所不同。比如我们上面我们经过格式化后的域名:3w5baidu3com\0在请求报文中就占用了12个字节。 - Type: 查询类型
占用字节数目:2
推荐使用类型: short/unsigned short
推荐值:1
值为1,是向服务器表示我们此次请求的目的是获得指定域名的IP地址。当然还有很多的其他选项,可以参考这篇文章link。 - Class:查询类
占用字节数目:2
推荐使用类型: short/unsigned short
推荐值:1
一般情况下这个值都为1,它表示我们查询的是互联网地址。
- Name:想要查询的域名
请求报文的代码实现
在实现代码的时候我们需要注意的是 我们需要利用htons()函数将上面每个选项在内存中的值从主机字节序进行网络字节序的转化,也就是从小端存储转化为大端存储。什么是小端存储,什么是大端存储参考这篇文章 link。然后依照上面各个选项的次序,依次将各个选项的值(注意,这里要求的是网络字节序)放到一个字符串数组中,然后利用sendto()函数发送给我们的DNS服务器。
// QUERY : without queries
struct QUERY {
unsigned short transaction_id;
short flags;
short count_question;
short answer_rrs;
short authority_rrs;
short additional_rrs;
};
//为部分请求参数赋值
int creat_query(struct QUERY* query, char* hostname) {
srand(time(0));
query->transaction_id = rand();
query->flags = htons(0x0100);
query->count_question = htons(1);
query->answer_rrs = htons(0);
query->authority_rrs = htons(0);
query->additional_rrs = htons(0);
return 0;
}
//将查询域名进行格式化 example:3wwww5baidu3com
char* format_hostname(char* hostname) {
if (hostname == NULL) return NULL;
// hostnaem after conversion
char* name = (char*)malloc(sizeof(strlen(hostname) + 2));
if (name == NULL)
return NULL;
//
char* temp_name = name;
char* duplicaton_hostname = strdup(hostname);
char delimition[2] = ".";
char* segment = strtok(duplicaton_hostname, delimition);
while (segment