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
|