/**/ /* * DNSTools.java * * Created on 2003-9-24, 13:56:09 * * To change this template, choose Tools | Templates * and open the template in the editor. */ package test; /** */ /** * * @author axman */ import java.io. * ; import java.net. * ; import java.util. * ; public class DNSTools ... { public static final byte TYPE_A = 1 ; // A记录 public static final byte TYPE_CNAME = 5 ; // CNAME记录 public static final byte TYPE_MX = 15 ; // X记录 private static int PORT = 53 ; // DNS服务的端口 public static String[] getQueryRecords(String NameServer,String domainName, byte type) ... { byte [] buf = new byte [ 512 ]; makeQueryData(buf,domainName,type); // 封装DNS请求 // 以下获取DNS服务器的响应内容 DatagramSocket udpSocket = null ; try ... { InetAddress dnsIP = InetAddress.getByName(NameServer); DatagramPacket sendPack = new DatagramPacket(buf, buf.length, dnsIP, PORT); udpSocket = new DatagramSocket(PORT); udpSocket.send(sendPack); DatagramPacket receivePack = new DatagramPacket(buf, buf.length); udpSocket.receive(receivePack); buf = receivePack.getData(); } catch (Exception e) ... { return null ; } finally ... { try ... { if (udpSocket != null && ! udpSocket.isClosed()) udpSocket.close();} catch (Exception e) ... {} } // 以下对DNS响应内容做分析 int qCount = ( (buf[ 4 ] & 0xff ) << 8 ) | (buf[ 5 ] & 0xFF ); // 获得请求数 if (qCount < 0 ) return null ; int aCount = ( (buf[ 6 ] & 0xff ) << 8 ) | (buf[ 7 ] & 0xff ); // 获得响应数 if (aCount < 0 ) return null ; int position = 12 ; // 起始位置 for ( int i = 0 ; i < qCount; i ++ ) ... { StringBuffer dmBuffer = new StringBuffer(); position = analyzer(buf,dmBuffer,position); position += 4 ; // 增加长度字节部分 } ArrayList< String > al = new ArrayList < String > (); for ( int i = 0 ; i < aCount; i ++ ) ... { StringBuffer dmBuffer = new StringBuffer(); position = analyzer(buf,dmBuffer,position); position += 10 ; int pref = (buf[position ++ ] << 8 ) | (buf[position ++ ] & 0xff ); // 获得基数 dmBuffer = new StringBuffer(); dmBuffer.append(pref).append( " " ); position = analyzer(buf,dmBuffer,position); al.add(dmBuffer.toString()); } return al.toArray( new String[al.size()]); } private static int analyzer( byte [] receiveBytes, StringBuffer dmBuffer, int position) ... { int len = receiveBytes[position ++ ] & 0xff ; // 取得将要处理的部分的长度 if (len == 0 ) ... { return position; } int offset; // 偏移 do ... { if ((len & 0xc0 ) == 0xc0 ) ... { // 压缩格式 if (position >= receiveBytes.length) ... { // 超过包的大小 return - 1 ; } offset = ((len & 0x3f ) << 8 ) | (receiveBytes[position ++ ] & 0xff ); analyzer(receiveBytes, dmBuffer, offset); // 再一次递归调用获得压缩前的名称 return position; } else ... { // 非压缩格式 if ((position + len) > receiveBytes.length) ... { // 超过长度 return - 1 ; } dmBuffer.append(new String(receiveBytes, position, len)); position += len; } if (position > receiveBytes.length) ... { return - 1 ; } len = receiveBytes[position ++ ] & 0xff ; if (len != 0 ) ... { dmBuffer.append( " . " ); // 加上.构成完整域名 } }while (len != 0 ); return position; } private static void makeQueryData( byte [] sendBytes,String domainName, byte type) ... { int id = 137 * ( new java.util.Random()).nextInt( 65535 ); sendBytes[ 0 ] = ( byte ) (id >> 8 ); sendBytes[ 1 ] = ( byte ) (id & 0xff ); sendBytes[ 2 ] = ( byte ) 1 ; sendBytes[ 3 ] = ( byte ) 0 ; sendBytes[ 4 ] = ( byte ) 0 ; sendBytes[ 5 ] = ( byte ) 1 ; sendBytes[ 6 ] = ( byte ) 0 ; sendBytes[ 7 ] = ( byte ) 0 ; sendBytes[ 8 ] = ( byte ) 0 ; sendBytes[ 9 ] = ( byte ) 0 ; sendBytes[ 10 ] = ( byte ) 0 ; sendBytes[ 11 ] = ( byte ) 0 ; String[] cols = domainName.split( " //. " ); int position = 12 ; for ( int i = 0 ;i < cols.length;i ++ ) ... { sendBytes[position ++ ] = ( byte ) (cols[i].length() & 0xFF ); // 转换为字节 byte [] b = cols[i].getBytes(); for ( int j = 0 ; j < b.length; j ++ ) ... { sendBytes[position ++ ] = b[j]; } } sendBytes[position++ ] = ( byte ) 0 ; sendBytes[position ++ ] = ( byte ) 0 ; sendBytes[position ++ ] = type; sendBytes[position ++ ] = ( byte ) 0 ; sendBytes[position ++ ] = ( byte ) 1 ; } public static void main(String[] args) throws Exception ... { String[] s = DNSTools.getQueryRecords( " 202.106.0.20 " , " sina.com.cn " , DNSTools.TYPE_MX); for ( int i = 0 ;i < s.length;i ++ ) System.out.println(s[i]); } }
/** | 2字节的标识 | 2字节的标志 | 2字节的请求个数 | 2字节的资源记录数 | 2字节的授权资源记录个数 | 2字节额外的资源记录数 | 查询的域名(不固定长度) | 针对请求的应答资源记录(长度不固定) | 授权资源记录(长度不固定) | 额外记录信息(长度不固定)
标识字段用于指出报文的编号,一般由客户指定,DNS服务器返回信息时带上此标识,告诉客户端回答 的是哪一个请求。 标志字段的16比特划分为8个子字段,从左至右(高位到低位)分别为: QR 1 bit :0 查询报文 1 响应报文 Opcode 4 bit :通常为0,表示标准查询 ,1 反向查询,2 服务器状态查询 AA 1 bit :用于服务器返回报文,表示是否是授权回答 TC 1 bit :由于UDP自身长度限制,往往会截断512字节后的内容,该位表示是否可截断 RD 1 bit :该为用于在查询报文中设置,并由服务器响应报文中返回。该位告诉服务器必须处理此查询, 如果该位为0,且服务器返回的授权回答个数为0,那么服务器必须返回一个能够解答该查询的其他服务器的列表 RA 1 bit :如果服务器支持递归,那么服务器在响应报文中设定该位。 随后的3bit必须为0
rcode 4 bit :最后为返回码,0 无差错,3 名字差错,即在服务器上不存在要查询的域名的记录,一般用于从最终的授权名 字服务器返回。
查询请求部分由查询名字 查询类型 查询类组成。查询名字由多个标识符的序列组成,每一个标识首字节说明该标识符的长度, 最终由字节0表示名字结束。譬如cn.yahoo.com由2 c n 5 y a h o o 3 c o m 0组成。如果此域名后面还用到,一般在后面采用 压缩格式,那么首字节不是长度了,而是一个最高位为1的字节,一般是0xc0,因为不会出现长度超过64的标识符。压缩格式的 标志字节后是该域名的原标识的偏移值。查询类型为2字节,1 表示A记录查询 5 表示CNAME记录查询 15 表示MX记录查询。类表 示是否是Internet数据。
应答报文中的应答记录由域名(长度不固定) 类型(2字节) 类(2字节) 生存时间(4字节,秒数) 资源数据长度(2字节) 资源数据(不固定)。域名的格式同查询域名格式相同。类型、类的解释同查询请求部分。资源数据根据记录类型不同而不同。
*/