Dns解析(下)
上篇讲述了Dns的查询包和发送,本文将分析Dns的返回包。
下面这段程序是从Dns服务器上得到dns的返回包:
ID_Packet=new DatagramPacket(new byte[Constant.DNSUDPLEN],
Constant.DNSUDPLEN);
ID_Socket.receive(ID_Packet);
这里的变量已在上篇中定义了,Constant.DNSUDPLEN为512。
接下来就只要将这数据解压缩就可以了。这里就涉及了RR的格式了(Resource Record Format)。
0 1 2 3 4 5 6 7 8 9 A B C D E F
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| |
/ /
/ NAME /
| |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| TYPE |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| CLASS |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| TTL |
| |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| RDLENGTH |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
/ RDATA /
/ /
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
这是在rfc文档中定义的RR格式。
NAME:就是在question中的QNAME;
TYPE:question中的QTYPE;
CLASS:question中的QCLASS;
RDLENGTH:RDATA的长度;
RDATA:返回的数据,这才是真正有用的数据,也是我们要解析的东西。
因为其数据是被压缩的,所以得想知道他的压缩格式:
0 1 2 3 4 5 6 7 8 9 A B C D E F
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| 1 1| OFFSET |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
他的压缩方式是将在数据中重复出现的字符放在一起,然后再字符出现的地方加上一个偏移位置,即如上图所示,16位的数据以11开头,后跟偏移量。偏移量是从信息的头部开始算得。下面是一个rfc文档中的例子:
0 1 2 3 4 5 6 7 8 9 A B C D E F
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
20 | 1 | F |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
22 | 3 | I |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
24 | S | I |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
26 | 4 | A |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
28 | R | P |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
30 | A | 0 |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
40 | 3 | F |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
42 | O | O |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
44 | 1 1| 20 |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
这个结果是:在40位置的域名是FOO.F.ISI.ARPA。
了解了他的压缩方式,解析就简单了。
上篇中在Header中我们已提到ANCOUNT这个字段,他表示的是回复中结果的数目,我们相见他解析出来:
public int getAnswerCount()
{
int INDEX=6;
byte[] AnCountArray=new byte[2];
System.arraycopy(message,INDEX,AnCountArray,0,2);
return DnsTool.BytesToInt(AnCountArray);//将byte[]变为int
}
得到了ANCOUNT,就可以解释结果了:
public Vector parseAnswer()
{
int theOffset=8;
int pos=thePosOfAnswer;(thePosOfAnswer是你发得dns包的长度)
int i=0,p;
int RDlength;
byte[] tmp;
String Name="";
Vector IV_ Answer=new Vector();
//get return name from message
while(i<getAnswerCount())
{
Name="";
//get type
pos+=2;
tmp=new byte[2];
System.arraycopy(message,pos,tmp,0,2);
if(DnsTool.BytesToInt(tmp)==Constant.TYPE_MX)//check the type
{
pos+=theOffset;
//get RDlength
tmp=new byte[2];
System.arraycopy(message,pos,tmp,0,2);
RDlength=DnsTool.BytesToInt(tmp);
pos+=4;
p=pos;
while((pos-p)<RDlength-2)
{
if((message[pos]&0xC0)==0xC0)
{
//this is a offset
Name+=getPrior((message[pos]&0x3F)
|(message[pos+1]&0xFF));
pos+=2;
}
else
{
//not offset
tmp=new byte[message[pos]];
System.arraycopy(message,pos+1,tmp,0,tmp.length);
pos+=message[pos]+1;
if(message[pos]!=0)
Name+=new String(tmp)+".";
else
Name+=new String(tmp);
}
}
}
IV_Answer.addElement(Name);
i++;
}
}
函数Stirng getPrior(int)是根据其偏移量等到所要的字符串,这是一个递归函数:
private String getPrior(int j)
{
byte[] tmp;
String Name="";
while(message[j]!=0)
{
if((message[j]&0xC0)==0xC0)
{
String mid=getPrior((message[j]&0x3F)|(message[j+1]&0xFF));
Name+=mid;
j+=mid.length()+1;
}
else
{
tmp=new byte[message[j]];
System.arraycopy(message,j+1,tmp,0,tmp.length);
j+=message[j]+1;
if(message[j]!=0)
Name+=new String(tmp)+".";
else
Name+=new String(tmp);
}
}
return Name;
}
我们只介绍了mail地址的dns解析,其他几类都大同小异,如需要可参考rfc1035。
文章不免有错,请各位多指点craks@263.net