DNS协议解析
项目需要,所以要多DNS协议的报文进行解析。读了《TCP/IP详解 卷1》的关于DNS部分的内容后,开始写代码做解析。
参考资料:
http://blog.163.com/libo_5/blog/static/1569685201031433013508/
http://libo.deng.blog.163.com/blog/static/401574222007850244930/
代码:
void GetDNSHost(unsigned char * pDataChar, int begin, char *host)
{
unsigned char *p = pDataChar+begin;
int len = *p;
while(len) {
if ((*p & 0xC0) == 0xC0) // 如果是一个压缩指针
{
len = ntohs(*(unsigned short *) p);
len = len & 0x03FFF;
GetDNSHost(pDataChar, len, host);
break;
}
else {
len = *p;
strncpy(host, (char*)p+1, len);
strcpy(host+len, "."); // 将数字替换为点。 这样简单处理的后果是域名最后会有一个点。
p = p + len +1 ;
host = host + len + 1;
}
len = *p;
}
}
主代码,不是完整代码,只是项目代码一部分的简化版本
// dns
if( (sport == 53 || dport == 53) && (pTransPacket->nPacketType == IPPROTO_UDP) ) {
if (pTransPacket->nDataLen <= 12)
return 0;
// 分析报文头部
unsigned char *pDataChar = pTransPacket->pDataChar; // 报文数据
DNSPacket *dnsPacket = (DNSPacket * )(pDataChar);
unsigned short id = ntohs(dnsPacket->id);
unsigned short sign = ntohs(dnsPacket->sign);
unsigned short qdCount = ntohs(dnsPacket->qdCount); // 问题数
unsigned short anCount = ntohs(dnsPacket->anCount); // 回答数
unsigned short nsCount = ntohs(dnsPacket->nsCount); // 授权资源记录数
unsigned short arCount = ntohs(dnsPacket->arCount); // 额外资源记录数
int curLen = 12; // 当前分析位置, 头部分析完成,共12字节
if ((sign & 0x8000) == 0) // 查询报文
{
// 分析第一个问题,一般都只有一个问题
char hostname[256];
GetDNSHost(pDataChar, curLen, hostname);
// debug info
{
printf("DNS REQUEST:\n");
printf("id: %4X\nsign:%4X\n问题数:%u\n回答数:%u\n授权资源记录数:%u\n额外资源记录书:%u\n", id, sign, qdCount, anCount,
nsCount, anCount);
printf("询问host: %s\n\n", hostname);
}
}
else // 响应报文
{
/* // 调试使用,将所有响应报文打印出来
{
printf("响应报文内容:\n");
for(int i = 0;i<pTransPacket->nDataLen;i++)
{
if(i%4==0 && i!=0)
printf("\n");
printf("%02X ",pTransPacket->pDataChar[i]);
}
printf("\n");
}*/
// debug info
{
printf("DNS RESPONSE:\n");
printf("id: %4X\nsign:%4X\n问题数:%u\n回答数:%u\n授权资源记录数:%u\n额外资源记录书:%u\n", id, sign, qdCount, anCount,
nsCount, anCount);
}
// 响应报文的查询问题
{
// 查询问题字段格式: 查询名 + 查询类型(2字节)+ 查询类(2字节)
char hostname[256];
GetDNSHost(pDataChar, curLen, hostname);
printf("响应问题: %s\n", hostname);
curLen += strlen(hostname) + 1 + 4; // 响应报文的查新问题字段一般不会又压缩的,内容长度为 host长度+1 +查询类型(2字节)+查询类(2字节)
}
// 响应报文的回答格式
// 域名(不定字节) + 查询类型(2字节)+ 查询类(2字节)+ 生存时间(4字节)+ 资源数据长度(2字节)+ 资源数据(长度由前一个字段确定)
// 其中域名一般为压缩方式,指向上一个问题或者回答的域名,一般2个字节
{
for (int i = 0; i < anCount; i++) // 分析每一个回答
{
// 域名
char hostname[256];
GetDNSHost(pDataChar, curLen, hostname);
printf("回答%d: %s----", i, hostname);
uint16_t dns_type = ntohs(*(unsigned short *) (pDataChar + curLen + 2));
uint16_t dns_class = ntohs(*(unsigned short *) (pDataChar + curLen + 2 + 2));
uint16_t dns_len = ntohs(*(unsigned short *) (pDataChar + curLen + 2 + 2 + 2 + 4));
if (dns_type == 1) // 查询类型为A, 是一个4个字节的ip地址
{
struct in_addr addr;
addr.s_addr = *(unsigned int *) (pDataChar + curLen + 2 + 2 + 2 + 4 + 2);
char *ip = inet_ntoa(addr);
curLen += 2 + 2 + 2 + 4 + 2 + 4;
printf("A----ip:%s\n", ip);
}
else if (dns_type == 5) // 查询类型为 CNAME, 一个域名
{
char hostname[256];
GetDNSHost(pDataChar, curLen + 2 + 2 + 2 + 4 + 2, hostname);
printf("CNAME----host: %s\n", hostname);
curLen += 12 + dns_len;
}
}
printf("\n");
}
}
}