作者/喻勇
导读:
本文根据笔者在微软Technet Webcast上的讲稿整理而成。文章介绍了微软Exchange Server中的核心传输组件以及它们的工作原理,阐述了SMTP协议的内容和使用SMTP发送邮件时的详细过程;深入地讨论了邮件传输和路由的工作机制,分析了SMTP报文的组成和Exchange在传输邮件时的路由过程(包括AQ, Routing Engine等组件)。本文可以供Exchange管理员深入的了解邮件传输组件的内部工作方式。
STMP协议规范和命令字
SMTP协议是随着Internet的发展逐渐规范化和进一步完善的。SMTP协议最早在RFC821中被定义,它提供了Internet上两个节点之间传送信息的一种方式。SMTP协议非常的简单和直观,在RFC821中,STMP由一些通过ASCII码表示的命令字组成,通过这些命令字,两个节点之间(客户机和服务器)可以建立SMTP连接,并进行信息传递。SMTP的标准命令字和其作用如表一:
SMTP命令
作用
HELO
用于开始会话,通常在HELO后跟客户机的主机名(hostname)
MAIL
用于指出发起会话的发件人,通常在MAIL后面跟From参数来指定发件人
RCPT
用于指定该消息的接收人,通常在RCPT后面跟To参数来指定收件人
DATA
表示客户端开始向服务器端发送消息(邮件) 正文
RSET
放弃当前的数据传输
VRFY
用以确定指定的收件人在服务器端是合法的(在发送邮件正文之前确定邮箱是否存在),考虑到这个功能有一定的安全隐患(可能导致组织的邮件地址泄漏),此命令在包括Exchange在内的大部分企业邮件系统中不被支持,SMTP服务器对VRFY请求,总是会返回”非法地址”
TURN
交换客户端和服务器角色,允许客户端触发服务器端的邮件传送(此命令也很少被使用)
QUIT
关闭当前会话
表一:SMTP标准命令字和其作用
SMTP服务器监听TCP 25号端口,并对客户端发送的合法命令做出响应。我们可以通过Telnet的方式演示一下最简单的SMTP邮件传送过程。表二中列出了使用Windows Telnet客户端跟Exchange SMTP Server通信的过程:(粗体表示用户输入,斜体表示服务器回应)
Telnet过程
注释
c:\telnet win2003.msft.com 25
220 win2003.msft.com Microsoft ESMTP Mail Service, Version 6.0.3790.0 ready at Tue, 18 Oct 2005 19:21:55 -0700
客户端通过TCP 25号端口连接服务器,SMTP服务器返回连接成功信息,并返回服务器操作系统类型,版本和当前时间。
HELO
250 win2003.msft.com Hello [192.168.1.100]
客户端通过HELO命令跟服务器开始通信,服务器以250回应表示服务器做好了进行通信的准备。
MAIL FROM:tom@msft.com
250 2.1.0 tom@msft.com...Sender OK
客户端提供发信人地址。
RCPT TO:mike@msft.com
250 2.1.5 mike@msft.com
客户端提供收信人地址。
DATA
354 Start mail input; end with
客户端输入DATA命令,DATA之后的字符都将作为邮件的正文。正文以.终止。
Hello Mike,
Let’s have dinner together.
Tom
.
250 2.6.0 <###@win2003.msft.com> Queued mail for delivery
.表示连续的回车-换行-点号-回车-换行。正文传送完毕以后,服务器端会生成邮件并进行下一步的传送。
QUIT
221 2.0.0 win2003.msft.com Service closing transmission channel
使用QUIT命令结束当前会话。
表二:使用Telnet进行SMTP会话
通过Telnet进行SMTP会话在诊断一些邮件传输问题时非常有用,微软在其知识库文档中详细描述了使用Telnet进行SMTP会话的过程。(XFOR:Telnet 到端口 25 以测试 SMTP 通信,
http://support.microsoft.com/default.aspx?scid=kb;zh-cn;153119)
在Exchange Server中,SMTP是邮件系统传输模块的核心,SMTP负责了组织内部邮件服务器间邮件的传送和对外发送邮件的重任。图一表示了SMTP模块在Exchange整个系统结构中的位置。SMTP协议栈运行在IIS的进程中,在IIS中,还有AQE和Routing模块协助SMTP的工作。Exchange的数据库模块(Information Store)通过EXIPC作为桥梁,跟SMTP进行通信。
图一:SMTP模块在Exchange中的位置
用户可以通过多种方式向Exchange服务器提交需要发送的邮件,例如,用户可以使用Outlook的MAPI方式,把邮件提交到Information Store中,也可以使用Outlook Express的SMTP方式,向SMTP服务器直接传送需要发送的邮件。Exchange上的SMTP模块,在不同的情况下承担的角色是不一样的,我们可以通过如下的情景分析,来透彻的了解SMTP服务器的工作方式。
图二:SMTP服务器在不同情况下担任的角色
如图二,我们分6种情况来讨论SMTP服务器的不同角色。
连接1,2,3,4代表了Inbound连接(其中连接1中的MAPI客户端是把邮件直接提交到Information Store中,不属于SMTP连接),当处理inbound连接时,图中的邮件服务器起到的是SMTP Server的角色。请注意,Inbound连接有两种类型,一种是客户端提交的邮件(连接2),这种通常表示组织内的用户通过在Outlook Express软件中指定SMTP服务器IP地址,来向服务器提交需要发送的邮件;另一种类型是其他的SMTP服务器转发过来的邮件(连接3,4),这通常表示当前邮件服务器是网关,或者是信件中收件人的邮箱所在的服务器。
连接5,6代表了Outbound连接,当处理outbound连接时,邮件服务器起到的是SMTP Client的角色,不管是发到外部,还是内部邮件的转发,都是由SMTP Client发起会话,并且由远端的服务器来响应。
由此我们可以发现,一台SMTP主机,它在运行过程中,既会承担SMTP Server的角色来接收Inbound连接所提交的SMTP报文,同时也会作为SMTP Client来向远程的服务器发送SMTP请求(Outbound)。只有分清楚了这些不同角色的含义,才可能透彻的了解Exchange的邮件传输过程。
如果把一封邮件的发送过程比喻为一次长途旅程,那么SMTP在其中只是扮演了交通工具的作用。大家可能已经注意到,对于一个给定的收件人地址(例如,abc@microsoft.com),SMTP并没有定义到底与目标域中哪一个主机来建立连接来完成邮件的传输过程,也就是说,RFC 821中的SMTP并没有定义“寻址”的方法(下面提到的MX记录及邮件选路和域名系统的规范,在RFC 974中被详细定义)。
在Internet中,我们是通过DNS里面的MX记录来实现邮件的寻址的。当一个组织或者公司购买了一个Internet域名并且建立了邮件服务器以后,往往会把某一台或者几台服务器发布到外网,作为邮件网关,接收来自Internet的邮件。同时,需要在该域名DNS系统(这里指此域名在Internet上的DNS,而不是内网活动目录的DNS,千万不能混淆)的MX记录字段中,注册这些主机的名字,这样当给这个域中的邮箱发邮件时,发送方通过向Internet上DNS服务器查询该域名的MX记录,再确定有哪些服务器可以建立SMTP连接。我们可以使用NSLOOK这个命令,在查询特定域名下面注册的MX记录。如表三,我们查到了微软公司现有的MX记录。在输出的结果中,我们发现微软公司注册了三个主机名来作为接收外部邮件的服务器,并且当进一步解析DNS主机记录时,我们可以发现这三个主机名分别对应6个IP地址,通过这种做法,可以实现SMTP流量的负载均衡。(仔细看会发现这些IP地址属于两个不同的网段,应该是由不同的ISP提供的网络链路,这也在某种程度上实现了线路的互相备份和冗余。)
当我们架设邮件服务器系统时,只有正确的在ISP的DNS上设置了MX记录,才能够收到从外部发来的邮件。关于MX记录的更多信息,可以参考微软的知识库文档。(XFOR: DNS MX Records and CNAME
http://support.microsoft.com/default.aspx?scid=kb;en-us;153001)
H:\>nslookup
>set q=mx
>microsoft.com
DNS Server: ns-pxb.online.sh.cn
Address: 202.96.209.6
microsoft.com MX preference = 10, mail exchanger = maila.microsoft.com
microsoft.com MX preference = 10, mail exchanger = mailb.microsoft.com
microsoft.com MX preference = 10, mail exchanger = mailc.microsoft.com
maila.microsoft.com internet address = 131.107.3.125
maila.microsoft.com internet address = 131.107.3.124
mailb.microsoft.com internet address = 207.46.121.51
mailb.microsoft.com internet address = 131.107.3.123
mailc.microsoft.com internet address = 207.46.121.53
mailc.microsoft.com internet address = 207.46.121.52
-----------Query Result-----------
表三:微软公司的MX记录设置
SMTP标准命令字最初定义在RFC821标准中,随着Internet的发展,这些命令逐渐无法满足一些特殊的邮件发送要求,比如,发送方身份的认证,数据加密,报文的传输优化等等。在RFC 1869中,国际标准化组织定义了扩展SMTP协议(Extended SMTP,简称ESMTP),在原有的命令字基础之上,增加了如下表(表四)所示的的扩展命令:
ESMTP命令
作用
EHLO
作为标准HELO的替代者,客户端发送EHLO来跟服务器确定其对ESMTP的支持程度,服务器会返回一个它所支持的ESMTP命令字列表给客户端。
ATRN
TURN命令的增强,在启用TURN之前需要身份认证。
ETRN
TURN命令的增强,功能上与TURN类似,但是通过创建另外一个独立的会话完成TURN报文传送。
Pipelining
允许SMTP客户端在服务器响应之前以异步的方式连续发送若干的命令字。例如,可以在获得服务器确认之前,连续发送多个RCPT命令字,这样可以实现在慢速网络上的高效通信。
BDAT
此命令字替代DATA,允许客户端采用批量的方式传送消息报文,可以在一定程度上降低接收方的负载。
AUTH
允许客户端使用基本验证、Windows集成验证(NTLM和Kerberos)与服务器进行身份认证,进行身份认证也是避免SMTP服务器被relay的重要手段。
STARTTLS
用来表示客户端希望能够与服务器建立一个基于TLS的加密会话。
XEXCH50
用以传送Exchange服务器间专用的报头数据。这个命令会在下文讲解Exchange分类器的时候提到。
表四:ESMTP的扩展命令集
标准SMTP(RFC 821)和扩展SMTP(RFC 1869)都得到了当今主流邮件服务器的广泛支持,Exchange服务器完全兼容这两种类型的SMTP协议,并且还增加了一些用于Exchange服务器间通信的特殊SMTP命令字(非国际标准)。在一些特殊情况下(比如跟比较旧版本的邮件服务器进行SMTP通信),我们也可以关闭Exchange Server上的ESMTP,使其只提供最简单和标准的RFC 821 SMTP协议,具体请参考:How to turn off ESMTP verbs in Exchange Server,
http://support.microsoft.com/default.aspx?scid=kb;en-us;257569。
SMTP报文格式
现在我们来分析SMTP的报文格式。如图三,一个合法的SMTP报文由信封和正文这两部分所组成。在传递过程中,邮件每经过一台SMTP服务器,该服务器就会在信封这一部分上留下转发的痕迹(转发时间,服务器IP地址等等)。正文(既SMTP DATA命令后所做的输入)部分由信头和信体组成,信头中包含发件人和收件人地址以及其他的一些信息,信体为邮件的具体内容。
信封上有发件人和收件人地址,不同的是,信封上的发件人收件人地址由MAIL FROM和RCPT TO命令写入,是用来给邮件服务器进行信件传送的,而信头上的发件人收件人是用来显示在Outlook中的。这两组地址,是可以不相同的!我们跟传统的书信比较一下:当给女朋友写情书的时候,大家一般都在信封上写她的真实姓名,而在信纸的开头冠以“亲爱的。。。”之类的亲切称呼。情书在被邮局投递时,工作人员会在信封上敲邮戳,以反映投递的过程。
信封和信体上的地址可以不相同,这有什么好处呢?我们举一个简单的例子。当我们采用BCC的方式发一封邮件给A,B,C三个人时,BCC的特点就要求隐藏邮件的收件人。这时,Exchange就会把原来的邮件拆分成三封独立的邮件,然后分别生成SMTP报文,在发给每个人的信封中,写入他的地址,在正文的信头中,把收件人表示为空。这样,每个收件人都回收到BCC的邮件,但是都无法知道这封信还BCC了哪些其他人。
图三:SMTP的报文格式
我们可能已经注意到,在RFC 821和1869中,我们定义了SMTP中用于建立连接和传输报文的命令字。在DATA命令后,我们可以传送由ASCII字符组成的数据流(即前面所提到的正文部分)到远端的SMTP主机,可这远远不能满足日常email通信的要求。邮件发送者往往需要在信件中使用非ASCII字符(例如,中文和其他非罗马字母语言),还需要插入各种类型的附件。为了能通过email发送非ASCII数据,IETF定义了多用途Internet邮件扩充MIME(Multipurpose Internet Mail Extension)标准。MIME并非为了改动SMTP或者取代它,而是允许使用ASCII码对任意数据进行编码,然后使用SMTP的DATA命令进行传送。
我们可以通过如下的例子来深入了解MIME编码在邮件中的作用。如图四,如果邮件中包含由MIME编码的数据,一般会在信头中指定MIME版本和分隔符(例子中的boundary=98766789,通过此分隔符来分隔不同的MIME内容)。当信件正文中有图文混排,或者中英文交叉在一起的情况,就需要把不同的内容分别进行MIME编码。下图中所表示的邮件由一段文本和一张jpeg图片组成,所以就会生成两段独立的MIME编码。每段MIME编码起始位置,都会指明这段编码所代表的数据类型和使用的编码算法,以方便客户端软件进行解码。我们可以在Outlook软件中把邮件另存为以EML为扩展名的文件,然后用写字板打开,这样可以看到所有的邮件结构和MIME编码。
图四:MIME编码在邮件中的作用
至此我们简单的介绍了SMTP协议的传输命令和其编码方式,正如我们在开篇所提到的,SMTP协议是随着Internet的发展和用户的需求逐渐完善和优化的,其标准的形成经历了一定的过程,下面简单的列出了跟SMTP有关的几个RFC标准。
标准定义时间
RFC编号
作用
1982年8月
821
简单邮件传送协议和基本的SMTP命令字
1982年8月
822
邮件报文的确切格式,定义了信封加正文的结构
1986年1月
974
邮件选路和域名系统的关系
1986年2月
976
用于UNIX UUCP的邮件系统标准
1993年9月
1521
MIME标准,包括信头说明语法,内容类型和编码算法
1993年9月
1522
为非ASCII文本定义了MIME信头的扩充
1994年3月
1590
描述了新MIME内容和编码类型的登记过程
1995年11月
1869
扩展SMTP标准
Exchange传输引擎架构
在花了大量篇幅讨论过SMTP协议的细节之后,我们来看一看微软是如何设计Exchange的传输模块,使其既满足SMTP协议的规则,又能够在微软的特定系统架构环境下高效地工作。Exchange Server是一个非常复杂的大型软件,它涉及到了活动目录、高性能数据库、各种类型的网络协议(MAPI、RPC、HTTP、SMTP、NNTP等等)以及很多Windows底层的技术,做为传输模块核心的SMTP服务,需要在实现自身功能的基础上,跟上述模块紧密、高效、安全的运行在一起,这是微软的Exchange开发者所面临的巨大挑战。
在微软发布Exchange 2000之前,SMTP协议的实现作为IIS 5.0的一部分,已经随着Windows 2000一起发布了。Windows中的SMTP服务是严格按照RFC标准所开发的,实现了其中的绝大部分要求。但是对于Exchange Server来说,这个版本的SMTP还需要进一步的增强,以实现跟微软Exchange相关的邮件数据库和活动目录的接口。Exchange Server的SMTP是在Windows SMTP的基础之上进行了一定的扩展和增强。从图一中我们可以看出来,SMTP是运行在IIS的进程空间(inetinfo.exe)的,在安装Exchange Server时,安装程序接管Windows SMTP,并对其进行改造。首先,安装程序把SMTP的工作目录转移到“\Exchsrvr\mailroot\vsi 1”下面,并且重新设置这些目录的若干属性;然后,安装程序注册了一些COM组件(DLL文件),这些组件作为Exchange SMTP的扩展,将扩展和覆盖Windows SMTP原有的模块,并和IIS无缝的集成在一起进行邮件的传输工作。概括来说,Exchange对Windows SMTP一共做了如下五个方面的扩展:
1. 高级队列引擎(Advanced Queuing Engine,下文简称AQE)。作为传输模块的心脏,高级队列引擎控制着SMTP服务器上邮件的处理流程,所有提交到SMTP服务器的邮件,都无一例外的要经过AQE的处理。
2. 增强型邮件分类器(Enhanced Categorizer)。邮件分类器负责邮件的收件人地址解析。在Windows SMTP中,Windows在其自带的CAT.DLL中实现了邮件分类器的基本功能,但是这个功能在Windows下默认是关闭的。Exchange安装时,安装程序激活了邮件分类器,并且使用其自带的PHATCAT.DLL来接管CAT.DLL实现Exchange SMTP邮件分类器的功能。PHATCAT.DLL由Exchange开发小组编写,有更加复杂的功能,并且与活动目有很好的集成。
(Link State Protocol)。链路状态协议用于Exchange组织中的路由信息更新。Exchange服务器、路由组和路由组连接器的任何状态变化,都由链路状态协议负责在整个组织环境中进行广而告之。这些功能也是由Exchange SMTP来协助实现的,服务器使用特定的X-LINK2STATE命令字来传送路由表的变化。
4. 数据库驱动程序接口(Information Store Driver)。数据库驱动程序接口允许SMTP中的组件对Exchange数据库中的邮件进行直接的读取,这在进行邮件的本地投递和获取邮件属性时非常有用。
5. Exchange SMTP专用命令字(Extended Command Verbs)。为了满足自身的要求,微软在SMTP命令字的基础上,增加了X-LINK2STATE、Chunking和Pipelining等等特殊的关键字,以实现服务器间通信和特定的性能优化。
Exchange的SMTP之所以建立在Windows SMTP的基础之上,除了为了接管Windows SMTP原有的功能之外,另一个重要的原因是为了获得IIS所提供的安全、高效的网络通信平台。在IIS中,用户身份认证,底层网络通信这些模块已经完成并且技术成熟度也很高,因此不需要Exchange的开发者再重新花力气去另起炉灶。图五显示了SMTP在IIS和整个Windows平台中的位置。SMTP可以同时响应很多并发的连接,其中一个很重要的原因就是SMTP借助了IIS所提供的异步线程队列技术(Asynchronous Thread Queue),该技术允许并发连接可以被系统以平行、异步的方式高速的处理,在Windows平台上尽最大的可能性提高了系统的吞吐量。
图五:SMTP在IIS和整个Windows平台中的位置
前文所提到的Exchange SMTP扩展模块,是采用COM组件和Event Sink技术来完成的。当IIS中的线程运行SMTPSVC.DLL中的SMTP协议处理代码,或者运行PHATQ.DLL中的SMTP传输过程处理代码时,被传送的邮件会经历一个又一个的“事件”。SMTP使用微软服务器扩展对象(Server Extension Object,SEO.DLL)这个COM组件来触发这一系列的事件,并且随后使用COM激活技术去实例化跟这些事件所关联的相应代码(调用Event Sink)。SMTP常常用事件来表示一个SMTP命令的提交(如客户端发送一个EHLO命令给服务器),也用事件表示把一封邮件提交给传输子系统。一些诸如SMTP协议扩展、邮件分类器、路由引擎的模块,这些都是微软所开发的Event Sink,他们分别注册并跟SMTP中相应的事件相关联。Event和Event Sink是互相关联的,Event被触发后,事先注册好的Event Sink中的代码就会被调用。Event触发与调用Event Sink和编程技术中的回调函数是同样的道理。值得一提的是,Exchange的Event Sink接口技术是公开的,也就是说任何开发者都可以编写自己的事件响应代码,来扩展SMTP的功能。(常见的在邮件结尾自动添加公司保密声明、邮件自动归档、邮件查毒过滤、垃圾邮件判别等等功能,都是通过二次开发的Event Sink来实现的)。在进一步讨论之前,我们来看一看SMTP中事件的种类和他们相应的作用。如下图,SMTP触发的事件一般分为4个大类别。
图六:SMTP触发的事件类别
1. SMTP协议事件(SMTP protocol events)。
这些事件针对SMTP协议本身,其注册的事件响应代码(Event Sink)通过修改、禁用或增加一些特殊的命令字,来改变SMTP协议引擎对标准SMTP命令字的响应行为。举例来说,进行X-LSA链路状态协议(此协议用于更新服务器路由拓扑)处理的Event Sink会针对其自定义的X-LINK2STATE这一SMTP命令字做出响应,执行相应的路由更新操作。SMTP协议事件的Event Sink也能够更改标准SMTP和ESMTP命令字的回应和处理过成功,例如,当用户提交MAIL FROM命令时,预先定义的Event Sink可以检查FROM字段中的邮件地址是否在黑名单中,如果是,可以采取屏蔽操作。
2. SMTP存储事件(SMTP store events)。
这些存储事件允许其Event Sink(通常在数据库驱动中被实现)去获取在数据库或者文件系统中的邮件内容。传输子系统使用此Event Sink来完成邮件的本地投递工作。(邮件最终目的地就是当前SMTP服务器时,SMTP执行本地投递,把信件保存到数据库中)
3. SMTP传输事件(SMTP transport events)。
当邮件到达服务器、在传输系统中被处理、投递给收件人或向外转发时,都会触发一连串的SMTP传输事件。在Exchange 2003中,微软使用SMTP传输事件来调用邮件的分类器和路由组件(这两部分会在下一期的文章中进行非常细致的讨论)。
4. SMTP系统事件(SMTP system events)。
与此事件相关联的Event Sink负责进行日志记录等工作,该Event Sink往往以异步的方式被触发,执行Event Log和SMTP诊断日志的记录工作。
我们再从全局的角度看一下Exchange的传输模块。如图七,Exchange邮件传输流程大致可以从这张图上反映出来。
邮件可以从两个来源进入传输模块,通过SMTP协议引擎(图二中的2、3、4,当使用Outlook Express的SMTP/POP3方式收发邮件时,属于这种情况)或者通过数据库(Web Storage System)直接向传输模块提交(前文图二中的1,当使用Outlook MAPI方式,或者OWA发信时,属于这种情况)。需要注意的是,当信件由数据库引擎提交时,它是不经过SMTP协议模块处理的,而是直接放入到分类器的队列里面了。因此,一般的反垃圾邮件和反病毒程序,都不会去通过注册Event Sink来监听SMTP协议事件来工作,因为这样会漏掉通过数据库直接提交的邮件,这种类型的程序,往往注册监听AQ中分类器所产生的事件。请牢记,只有高级队列中的分类器是Exchange系统中传送邮件时唯一的一个必经之路,其它模块都有可能在特定的情况下被跳过。
邮件经过分类器和路由引擎进行处理,来分析收件人地址并进行下一跳(next hop)的投递路由判断和选择,然后就会交给SMTP向外投递。当下一跳的目的地恰好是本地服务器时,SMTP调用Store Driver中定义的Local Delivery把邮件投递到数据库中,当下一跳目的地是其他远程服务器时,根据目的地服务器的类型,邮件依次被放置到不同的远程队列中进行投递处理,此时,SMTP承担图二中5、6的角色,以SMTP Client的身份,向远程SMTP主机发起会话请求。邮件就是这样在一台台的服务器间被传递,直到被Local Delivery到目标数据库中(或者被退信)。
总结和下期介绍
本文介绍了SMTP协议的工作方式,基本命令字和MIME的基础知识,同时也阐明了Exchange Server SMTP模块的组成方式。作为连载,笔者会在下一篇文章中从邮件投递过程的动态角度,深入地讨论Exchange是如何在内部处理邮件传输的。下期要目如下:
1. 基于事件触发机制的邮件传输过程
2. Exchange Categorizer
3. 队列和MailMsg对象