Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1503725
  • 博文数量: 3500
  • 博客积分: 6000
  • 博客等级: 准将
  • 技术积分: 43870
  • 用 户 组: 普通用户
  • 注册时间: 2008-05-03 20:31
文章分类

全部博文(3500)

文章存档

2008年(3500)

我的朋友

分类:

2008-05-04 20:31:24

一起学习
一、MX记录的应用 Email是迄今为止互联网上最成功的应用了,试想一个触网者如果没有属于自己的Email邮箱,那将难以称作“网民”。互联网网上同Email相关的应用也增多。我们编写网络应用时,经常需要涉及将Email应用嵌入到自己的应用程序中。这种应用往往是将第三方的Email相关组件拿来使用,完成Email的撰写、发送、收取、解码。在发送Email的过程中,NT平台一般采用NT系统自带的CDO组件来完成,而这类组件往往依靠在本机的服务或一个特定的SMTP服务器来实现,不能够确保信件是否到达,而且由于存在中继延迟,还有丢失信件的可能。所以很多高手采用直接向目的邮箱的服务器投递Email的办法来实现Email发送,而实现此功能关键在于三个要点: 1、邮件的MIME编码 2、SMTP会话控制 3、MX记录的获取。 通过我的了解,互联网上关于1、2两点的文章很多,相应的参考代码很多,而对于如何定位目标邮箱接收服务器-获得邮箱域名的MX记录却很少详细讨论,搜索相关的组件得到一个ASP环境的服务器组件(收费),没有详细的源码可以参考。基于此,我通过学习Java语言的机会编写了一个类似的组件,帮助大家了解这方面的细节。 二、DNS记录分类以及原理 我们知道域名系统是互联网的基石,我们能够通过亲切的类似yahoo.com这样的名字来访问自己感兴趣的网站完全是域名系统的功劳。当您在浏览器敲下时,您的浏览器是不知道这个域名指向的网页该往哪儿去取,便会询问设定好的域名解析服务器(DNS),域名解析服务器会寻找自己的记录库是否存在请求的域名记录,如果存在会直接返回,不存在就向上一级域名解析服务器请求(转发请求),直到最后找到此域名的解析记录返回给您的浏览器,告诉您的浏览器该到哪一个IP地址上去获得相关的内容。这样一个将互联网域名转换为对应的IP的过程叫做解析。 域名记录有多种,常见的有A(地址)记录、别名(CNAME)记录、MX(邮件交换)记录。A记录就是一个域名对应一个IP的记录,刚才提到的对应某个IP地址即是A记录的查询过程。别名记录主要用于用一个已经存在A记录的域名替代需要解析的域名,譬如查询时会得到 的别名值,而进一步解析会得到IP地址64.58.76.177。邮件交换记录是用于邮件投递时采用的。譬如您需要发送一封到 abc@yahoo.com的Email,那么一般情况下,您的ISP的SMTP服务器(专业术语成为邮件代理,即代替用户直接投递邮件)会接受您发出的信件,然后SMTP服务器查询yahoo.com的MX记录,看应当将邮件发送到哪一个服务器上去,如果查询到yahoo.com的MX记录,那么您的ISP的SMTP服务器会建立到MX记录指定的服务器的TCP连接,并传出您的Email到目的服务器上去,完成Email的投递过程。 如果能够获得某个域名的MX记录,即获得了向目的邮箱直接投递的途径,而要获得这个MX记录,您必须能够正确的向域名服务器DNS提出您的请求,同时也要能够理解DNS返回的信息,读出自己需要的内容。这里面的细节相对复杂,我这里简单介绍一下: 客户向DNS的53端口发出的UDP报文,然后服务器查询(中间可能经过对此转发或者称作递归查询)后发回客户机需要的记录,也是UDP报文。此类报文的一般格式为: | 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字节) 资源数据(不固定)。域名的格式同查询域名格式相同。类型、类的解释同查询问题部分。资源数据根据记录类型不同而不同。 如果我们按照上面的格式同时结合自己的需求,向有域名解析功能的服务器发出UDP报问候就可以收到DNS服务器发回的应答,这样我们就可以获得想的记录。所以,主要难点在保文的构造和应答报文的分析。 三、编写MX记录组件 启动VJ。要求建立一个DLL工程,然后修改主类名为自己想要的名字,该工程建立完毕。如果一切无误的话,就会得到一个DLL,具有域名MX记录查询功能。具体代码见后面的源代码分析。关键就是结合自己的查询问题构造查询报文和解释得到的应答报文,同服务器的网络连接方式是UDP方式。 为了调试方便,我添加了一个Main函数入口,这样可以在DOS窗口调用jvew来察看域名MX记录的结果,同时也方便了调试。 四、应用举例及扩充 有了这样一个查询组件,可以直接获得域名的MX记录,可以帮助我们获得投递Email的方法。同时,我们也知道了该域名邮件接收服务器的位置,通过试探不同得用户名,我们还可以获得该域名的邮局存在那些邮箱用户名。你可能奇怪自己的邮箱如何被他人获得,很可能被一些Email搜索器通过查询MX记录,得到邮件服务器的地址,然后通过特别的用户名产生算法得到不同的用户名,一一试探从而得到邮箱。在你的应用程序中加入此功能,可以直接将邮件传递到目标务器,不依赖于服务器上的组件。甚至您可以编写邮件转发服务器,服务于您的用户(一般的发件服务器不对外开放的)。 五、源码及分析 import java.net.*; import java.io.*; import java.util.StringTokenizer; //import java.util.*; /** * This class is designed to be packaged with a COM DLL output format. * The class has no standard entry points, other than the constructor. * Public methods will be exposed as methods on the default COM interface. * @com.register ( clsid=20AE856F-E2F8-488E-B41E-753E6BEBD375, typelib=36611559-AFAB-479E-8572-9A0B1F06CCDA ) */ public class MXDNS { private String theDnsServer; //当前DNS服务器的地址 private DatagramPacket outPk; //发送数据包 private DatagramPacket inPk; //接收数据包 private DatagramSocket UDPSocket ; //UDP陶接字 private InetAddress DNSServerIP; //域名解析服务器的IP private int position,id,length;//分析的DNS记录时需要的变量 private String DMname; //查询的域名 private MXRec mxrecs; //DNS应答的记录 private static int DNS_PORT=53; //DNS服务的端口 private byte[] pkdata=new byte[512] ; //得到512字节的数据包 public MXDNS() //构造函数 { id=(new java.util.Date()).getSeconds()* 60 * (new java.util.Random()).nextInt(); //获得唯一ID } //以下为暴露的属性和方法 public void setDnsServer(String dnsserver) { theDnsServer=dnsserver; //设定当前的DNS服务器的名字或者IP } public String getMXRecords(String dm) //获得所有DNSMX记录数组 { return(getMXRecords(dm,theDnsServer)); } public String getMXRecords(String dm,String DNSServer) { try { DNSServerIP=InetAddress.getByName(DNSServer) ; //获得DNS服务器的InetAddress outPk=new DatagramPacket(pkdata,pkdata.length,DNSServerIP,DNS_PORT ); //外发的数据报 UDPSocket=new DatagramSocket(DNS_PORT); //数据包端口 makeDNSQuery(id,dm); //产生查询报文 UDPSocket.send(outPk); //发送数据报文 inPk=new DatagramPacket(pkdata,pkdata.length ); UDPSocket.receive(inPk); //接收返回的应答报文 pkdata=inPk.getData();//获得字接 length=pkdata.length; } catch(UnknownHostException ue) { } catch(SocketException se) { } catch(IOException ioe) { } return(getResponse()); //分析返回的数据报文,得到记录结果 } public void makeDNSQuery(int id,String dm) {//在PKdate byte数组中产生查询数据 for(int i=0;i<512;i ) { pkdata[i]=0; } pkdata[0]=(byte)(id>>8); pkdata[1]=(byte)(id & 0xff); //查询的标识2字节 pkdata[2]=(byte)1; //Qrbit位为1,表示是查询报文 pkdata[3]=(byte)0; pkdata[4]=(byte)0; pkdata[5]=(byte)1; //1个问题 pkdata[6]=(byte)0; //资源记录数、授权资源记录、额外资源记录均为0个,因为是查询报文 pkdata[7]=(byte)0; pkdata[8]=(byte)0; pkdata[9]=(byte)0; pkdata[10]=(byte)0; pkdata[11]=(byte)0; StringTokenizer st=new StringTokenizer(dm,".");//.分隔域名 String label; position=12; //从第12字节开始生成查询问题 while(st.hasMoreTokens ()) { label=st.nextToken(); pkdata[position ]=(byte)(label.length() & 0xFF);//转换为字节 byte[] b=label.getBytes(); for(int j=0;jlength) //超过长度 { return(-1);} DMname =new String(pkdata,position,len); //获得域名标识符各个部分 position =len; } if (position>length) {return(-1);} len=pkdata[position ] & 0xff;//最后是0结尾么 if(len!=0) { DMname =".";//加上.构成完整域名 } }while(len!=0); return(position); } public static void main(String args[]) throws Exception { MXDNS mx=new MXDNS(); String s=mx.getMXRecords("sina.com","202.96.128.68"); System.out.println(s); int k=System.in.read() ; } } 下载本文示例代码


MX记录获取组件的编写MX记录获取组件的编写MX记录获取组件的编写MX记录获取组件的编写MX记录获取组件的编写MX记录获取组件的编写MX记录获取组件的编写MX记录获取组件的编写MX记录获取组件的编写MX记录获取组件的编写MX记录获取组件的编写MX记录获取组件的编写
阅读(97) | 评论(0) | 转发(0) |
0

上一篇:JSP与JavaMail (二)

下一篇:javaMail

给主人留下些什么吧!~~