📄 计费邮件服务器研究与实现.txt
字号:
计费邮件服务器研究与实现
石岩 蒋东兴 白冰 戚丽
清华大学计算机与信息管理中心(北京 100084)
jdx@cic.tsinghua.edu.cn
摘要:本文在阐述电子邮件原理、相关网络协议和软件的基础上,提出一种按照流量计费的电子邮件服务器解决方案,并给出该方案的实现模型。
关键字:电子邮件,计费,SMTP,POP3
1 前言
电子邮件是Internet服务的重要组成部分。随着Internet技术日新月异的发展,电子邮件以其方便、快速、廉价和可靠的特点越来越赢得人们的喜爱。现在,电子邮件已经成为学术界、商业界最为流行的一种通信方式,和国外进行学术交流更少不了电子邮件。
在国外,电子邮件服务一般是一次性收取服务费,或者是商业广告性质的免费服务。但是,在CERNET按照网络流量计费的大前提下,为公众提供电子邮件服务时,将不得不考虑流量计费的问题。
本文将从Internet电子邮件原理入手,在阐明相关网络协议和软件的基础上,提供一种按照流量计费的电子邮件服务器解决方案,并给出该方案的实现模型。
2 Internet邮件原理
2.1 Internet电子邮件简介
为了保证电子邮件系统的正常运行,TCP/IP定义了一组协议,SMTP(简单邮件传输协议)、POP3(邮局协议)和IMAP(Internet消息通道协议)是主要的几个协议。它们的关系如图1所示:
图1 Internet邮件传送示意图
图1显示了电子邮件从发送到接收的一个单向的典型过程。SMTP和POP3服务器是服务器软件,它们运行在邮件服务器上。SMTP服务器负责接收待发送的邮件,并发送至目标邮件服务器的SMTP服务器,由该SMTP服务器写入用户邮箱。实际上,由于SMTP服务器具有中转(Relay)功能,它并不区分邮件是来自用户机(如普通PC)还是其他SMTP服务器。如果用户想在普通客户机(没有SMTP服务器的普通主机)上接收邮件的话,他需要通过POP3协议或IMAP协议从邮件服务器上获取。不同的是,POP3服务器要求用户将邮件取回本地的普通客户机进行维护,而IMAP则可以在服务器上直接维护,例如建立不同的邮件夹等。到目前为止,POP3的使用比IMAP要广泛的多,下面我们主要介绍SMTP协议和POP3协议。
2.2 SMTP协议
SMTP(Simple Mail Transfer Protocol, RFC 821)是一个用7-bit基本ASCII字符传送简单信文的邮件协议。它是一个独立的用户级协议,它要求一个可靠的数据通道。在TCP/IP协议中,这个通道是8-bit的TCP数据流,因此SMTP的7-bit字节一律按照最高位为零的8-bit字节进行传输。如果要传送8-bit数据,需要用特殊的调制算法(例如MIME)将其转为7-bit数据,在接收端再用相反的算法将其复原。
SMTP的一个重要特点是“中转”(Relay)。一般地,用户可以选择任意一台SMTP服务器(如A)来发送邮件(只要能与该服务器建立传输层连接),若该服务器与目标SMTP服务器B可以建立直接连接,则邮件将被直接送至目标服务器B;若不能建立直接连接,该SMTP服务器将向其他所知的SMTP服务器询问路由,假如有一台SMTP服务器C可以与目标建立直接连接,或知道通向目标的路由,则邮件被转至服务器C,由服务器C向目标B转发。不管是从客户机到服务器的发送还是服务器间的中转,SMTP使用同一套指令来进行连接和数据的接收发送,从而使得整个过程清晰简捷。
Sendmail 是由美国加州大学开发的一个基于UNIX的共享SMTP服务器软件,它支持多种UNIX的CLONE平台,如Solaris、Linux等,并且兼容很多其他类型的电子邮件系统,如UUCP等。Sendmail模型如图2所示。
图2 Sendmail模型
图2中,客户方sender与SMTP服务器Sendmail建立连接,将邮件送交Sendmail服务器;Sendmail按照邮件的不同类型,送交不同的邮件发送程序(称之为mailer)进行发送,例如SMTP mailer,UUCP mailer等等。Sendmail提供一种Local mailer程序mail.local,对邮件目标地址进行认证,若合法则将邮件写入用户的邮箱,否则将邮件退回。
2.3 POP3协议
POP3(Post Office Protocol version 3, RFC 1939)定义了客户机从邮件服务器上获取邮件的一个简单的方法,它通过一组简单指令和应答实现与用户的交互操作。例如,用户通过user指令和pass指令实现身份认证,认证成功后可以通过retr指令收取邮件等。
加州大学针对SMTP服务器Sendmail,开发了一个共享的POP3服务器软件popper,该软件具有与Sendmail相同的支持多平台和多种类型邮件的优点,并且在设计上采用结构清晰的状态机模型,其模型如图3所示。
图3 popper状态机模型
如图3,系统初始状态为AUTH,身份认证通过后进入TRANSACT状态,系统邮箱被拷贝至一个临时文件。这一状态中,用户可以通过list命令列出邮件头的信息,通过retr指令将指定邮件取回本地机,通过dele命令将指定邮件标识为删除,等等。接到quit指令后,进入UPDATE状态,系统将没有被标识为删除的邮件反拷贝回系统邮箱,然后进入HAULT状态退出。
3 计费邮件服务器要解决的问题
3.1 计费信息获取
1. 流量。流量即邮件的长度,它是我们计费的基准。因为每一个来件都交由mail.local递送,所以在mail.local里可以容易地得到每个邮件的长度;另外,用户取信时与popper连接,popper要对信箱中每个邮件制作一个消息头(包括邮件的长度,在邮箱中的位置,是否被检索过等信息),同时也会获得邮箱的总长度,因此在popper中也可以获得流量,但程序上的开销会大一些(例如为了避免重复计费,在每次收信时需要判断邮件是否被检索过)。
2. 邮件的来源。目前在我国四大网(教育网CERNET,科学院网CASNET,电子部金桥网CHINAGBN和CHINANET)内,网络流量是不计费的,因此应当根据邮件源地址是免费区域和非免费区域按照不同的标准计费。这需要获取邮件的源地址,源地址在mail.local和popper中都可以获得。此外,对于Internet上一些反动或黄色站点的来信,应当禁止写到用户信箱里去。由于这个原因,我们应当在mail.local中就获得来信地址。
3. 用户取信的时间和客户机IP地址。这两种信息主要用于管理,例如,我们可以通过IP地址为用户限制可取信的客户机范围,从而可保护用户的邮件不被他人获取。另外,将这两条信息记入帐表后可为用户的查询提供极大的方便。这两种信息只能在popper中获得。
从上面的分析可以看到,我们需要一个基于mail.local和popper的综合解决方案,才能充分利用已有资源,在开销尽可能小的前提下获得以上信息。
3.2 帐户管理问题
1. 为非UNIX用户提供邮件服务。每一个UNIX的用户都有一个邮件帐户,Sendmail的缺省的帐户管理也是面向这些用户的,它需要对UNIX用户进行身份认证。例如,mail.local获得了一封给jdx的来信,要使用UNIX系统调用对“jdx”进行认证,如果系统没有一个叫做jdx的UNIX帐号,将返回空值,来信将被退回。在popper中除了以上步骤外,还要检查用户输入的密码与系统中密码是否相同。
这样的机制不适合对大量邮件用户的管理。首先,每增加一个邮件用户就需要建立一个UNIX帐号,用户所享受的将不仅是电子邮件服务,给用户权限管理带来不便;其次,对于大量帐号的快速检索也成问题。另外,所有的邮箱文件存放在一个目录下,例如/var/mail或/var/spool/mail下,在文件过多时,系统效率降低,还可能给管理带来麻烦。
因此,我们需要建立自己的帐户管理机制。这个机制应当能够实现:
1) 支持没有UNIX帐号的普通邮件帐户;
2) 用户的邮箱文件应能分类写入不同的目录;
3) 能够对用户信箱按照用户名进行快速检索。
2. 用户表的管理。用户表包括的信息有帐号,加密的口令,真实姓名,状态(正常,欠费,不受欢迎等等)。为了提供快速检索,用户表应当是一个排序的表。
用户表是一个动态的表,每天都可能有新用户加入,也可能有老用户注销。而且,计费邮件服务器要能根据用户的费用情况自动修改用户的权限。例如,用户申请帐号时需要预付一定数量的使用费,此时用户为正常用户;一旦使用超过限度(收取了大量邮件),系统将其置为欠费用户,暂时禁止取信,在用户补交足够的费用时,系统恢复其取信权利。因此,用户表应当及时更新。
为了减少管理难度,提供自我服务,应当向用户提供随时修改口令的机制。另外,用户表应当有一个安全可靠的后备,在用户表出现故障时能够及时地修复。
3.3 记帐的问题
用户每次取信的相关信息,例如时间、地点(IP)、取了多少封信、长度是多少等,都应当记录下来。作为专用邮件服务器,一般都要为大量的用户提供服务,假设有20000用户,每人每天取信一次。这样,一天大约有两万条记录,这些记录基本集中在从早上8点到晚上10点的14个小时(3600*14=50,400秒)内,即平均每2.5秒一条记录。在这样的情况下,同一时刻处理好几条记录的可能性非常大。如果将记录实时地交给数据库处理,将加重数据库负荷,延长响应时间,并增加丢失数据的可能性,严重时甚至可能导致系统崩溃。
一个解决办法是,每次的记录写入一个日志文件,每天在数据库负荷最低的时候,对这个文件进行批处理。
4 计费邮件服务器的实现
4.1 解决方案
针对上面提出的问题,我们可以设计如下解决方案:
1. 将计费邮件系统分为两个模块,邮件处理模块和数据库计费模块。邮件处理模块按照用户表进行常规的邮件收发和计费日志记录;数据库计费模块读计费日志、处理帐目、并更新用户表。两个模块通过共享用户表mail-usr-tab,邮件信息表mailmsgtab,和计费日志实现相互通信。
2. 修改mail.local,插入计费模块,以获取来信的源地址,识别邮件的长度及是否国外邮件,并将这些信息写到一个中间表mailmsgtab里;修改popper,插入计费模块,在用户使用popper取邮件时,将该邮件的信息从中间表mailmsgtab中取出来,连同用户的IP及取信时间一同写入日志文件,并将mailmsgtab中相应条目清空,以避免对同一封邮件的重复计费。这样,既获得了邮件的源地址,又获得了用户的本地IP。对于不受欢迎站点的来信,可以在mail.local中将其源地址识别出来之后,退回发送者或者丢弃。对于欠费用户或不受欢迎的用户,可以在用户通过popper获取邮件时拒绝用户的请求。
3. 如果直接由popper写计费日志的话,可能会出现几个进程同时写一个文件的情况。虽然用互锁机制可以避免写丢失,但在一个进程写的时候其它几个进程必须等待,延长了响应时间,降低了系统性能。因此采用一个专门的进程写计费日志,称为通信守护进程(cmpd,Communication Monitor Process Daemon)。所有的计费信息由popper使用UDP包发送给它,由它写入日志。
4. 提供自我服务,开发一个自我服务程序,用户可以通过远程登录启动该程序的一个进程来修改在用户表中的密码,象常规的密码修改程序一样,普通用户首先需要使用旧密码进行身份认证,管理员可以修改任何用户的密码。
4.2 计费邮件服务器实现模型
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -