qmail基本工作原理

qmail基本工作原理

qmail邮件投递工作基本原理:qmail-inject负责接收本地邮件用户投递的邮件,qmail-smtpd负责接收远端邮件用户投递的邮件,交给程序qmail-queue处理后放入邮件队列,qmail-send会依次检查邮件队列中的状态,如果邮件的状态是投递永久失败,qmail-send会调用qmail-clean将邮件从邮件队列中删除,如果邮件状态是没有投递过或者是暂时失败,qmail-send会把投递到本地邮件服务器的邮件传递给qmail-lspawn,把投递到远程邮件服务器的邮件传递给qmail-rspawn,qmail-lspawn会调度邮件投递的序列和时间,然后让qmail-local将邮件投递到本地邮件用户的邮箱中,qmail-rspawn也会调度邮件的投递序号和时间,然后调用qmail-remote将邮件投递到远端用户的邮箱中。

qmail的使用简介
编译前修改系统参数。
建立qmail工作目录:
默认情况下,qmail原代码会把qmail的目录设置成/var/qmail,如果你想把这个目录换成其他自己想要的位置,必须在编译前修改qmail-1.03目录中的conf-qmail文件。

qmail配置参数:

1. badmailfrom
这个配置文件是控制邮件系统拒绝接收的邮件地址和邮件域,主要是为了防止垃圾邮件。如果一个邮件地址或者邮件域被列入到这个文件中,系统就会拒绝接收这个邮件地址发来的邮件,或者拒绝邮件域下所有邮件地址发来的邮件。不过这个配置文件只是一般的垃圾邮件防范手段,对于比较全面的垃圾邮件过滤技术还要靠第三方软件来实现。该文件的格式如下:
11@11.com //拒绝这个地址发来的邮件
22@2w.com
@33.com //拒绝这个邮件域下的所有帐号发来的邮件
@44.com

2. boucefrom
bouceform是定义当邮件投递失败时系统返回给发送者一个包含失败信息的邮件时的发送者。如果不存在这个文件,默认的发送者是MAILER-DAEMON。比如本文的
echo postmaster > /var/qmail/control/bouncefrom
就是定义投递者为postmaster,这样所有投递失败的返回邮件的发送者就成了postmaster

3. concurrencylocal
这个文件定义了qmail可以同时投递的本地邮件的个数。这个参数的缺省值是10,也就是说系统允许同时有10个邮件在本地投递。concurrencylocal这个参数的最大值是由编译时的conf-spawn参数来决定的,缺省值是120,最大值是255。

4. concurrencyremote
这个参数定义了qmail可以同时投递的远端邮件的个数,这个参数的缺省值是20。这个参数的最大值也是由conf-spawn来决定的。

5. defaultdomain
它主要用于邮件用户在投递给同一邮件服务器的邮件用户时的邮件投递处理,比如邮件服务器cnunix.com.cn用户user发送邮件给另一个邮件用户user1@cnunix,这实qmail-inject会将defaultdomain中定义的邮件域名加入到这个邮件的目标地址中,qmail会自动认为这个邮件的目标地址是user1@cnunix.com.cn,并按照这个地址进行投递。当这个文件不存在的时候,qmail会从配置文件me中读取这个参数。如果系统变量QMAILDEFAULTDOMAIN已经设置,defaultdomain定义的参数将被忽略。

6. defaulthost
它和defaultdomain类似,当邮件系统接收到没有目标主机名的邮件时,这个设置文件定义了系统往这个邮件中加入的目标主机名名称。在缺省情况下,qmail-inject会将defaulthost中定义的名称加入到没有定义投递邮件地址的邮件的目标主机名中,如果defaulthost不存在,加入的目标主机名将是字符串“defaulthost”,defaulthost主要用于邮件用户在投递给同一邮件服务器的邮件进行投递处理。如果系统变量QMAILDEFAULTHOST已经设置,defaulthost定义的参数将被忽略。

7. databyes
它定义了qmail-smtpd所允许接收的邮件的最大字节数。这个参数的缺省值为0,表示对接收邮件的字节数没有限制。如果要限制最大的接收为10M,操作如下:
echo 10485760 > /var/qmail/control/databytes
这样任何大于10M的邮件都会被拒绝。这个参数最好设置上,以避免恶意的对你服务器发送大量的超大邮件,产生邮件服务器负荷过大,甚至系统崩溃的危险。

8. doublebouncehost
这个配置文件定义了出现“双重反弹”的时候,系统转发的邮件的主机名称。“双重反弹”就是当系统因为投递失败将邮件返回给发送者时,发送者又将此邮件返回,这个时候qmail将会把这个邮件转发到另一个邮件地址,一般这个地址就是管理员的地址。doublebouncehost就是定义这个转发的邮件地址的主机名部分,而这个邮件地址的用户名部分是由doublebouceto来定义的,因此这两个配置文件一般都是结合起来使用的。当doublebouncehost不存在的时候,系统将会从配置文件me中读取这个参数

9. doublebounceto
这个参说是结合blebouncehost来使用的,当这个参数不存在的时候,系统缺省值是postmaster用户

10. helohost
这个配置文件定义了当程序qmail-remote和远程邮件系统建立连接时所使用的主机名。如果这个配置文件不存在,系统将会从配置文件me中读取这个参数值,如果me不存在,qmail-remote将不能正常运行工作。

11. locals
这个配置文件是定义本地邮件域的,在qmail-send处理邮件投递时,将会使用locals定义的值和邮件的目标地址进行比较,如果相同,系统就会将这个邮件投递到本地交给qmail-lspawn来处理。如果一个邮件的目标主机名在locals找不到,系统将会把这个邮件投递到远端邮件服务器系统,即使这个邮件的目标地址可能是本地邮件系统的。如果locals不存在,系统将会从配置文件me中读取这个参数,如果me不存在,qmail-send将不能正常运行。

12. me
这个配置文件是qmail系统十分重要的一个文件,如果这个文件不存在,qmail系统将无法运行。me是用来定义本地邮件服务器的主机名的,上面已经介绍到了,有多个配置文件是和me有关联的,如果那些配置文件不存在系统默认会从me中读取参数值的。me这个配置文件一般都是在qmail系统安装时使用configure-fast来创建的,在上面qmail系统安装的时候已经使用过了这个参数。

13. queuelifetime
这个配置文件是定义一个邮件在邮件队列中存活的时间,缺省值为7天(604800s),这个期限到了以后qmail-send将会进行最后一次的投递尝试,如果投递失败,该邮件将会从邮件队列中删除。

14. rcphosts
这个配置文件也是qmail一个十分重要的文件,这个文件是定义系统允许转发邮件的邮件域,如果这个文件不存在或者为空,你的系统将会接收Internet上所有域的邮件转发,即你的系统是Open relay。配置文件rcpthosts最多可以支持50个主机名和域名,如果超个这个数字,就需要保存到他的扩充配置文件morercphosts中,然后使用qmail的命令程序qmail-newmrh(在本系统中,该文件在/var/qmail/bin目录下)来生成二进制的morercpthosts.cdb文件,这样qmail-smtpd才可以从这个二进制文件中读取信息。

15.virtualdomains
这也是qmail的一个非常重要的配置文件,它定义了qmail的虚拟邮件域,qmail结合vpopmail的虚拟域管理功能可以定义多个虚拟邮件域。

16. smtproutes
这个配置文件是定义qmail邮件系统的静态SMTP路由表信息的,他的格式应该是:
HOST: targetHOST //HOST可以是主机名或者域名。这行所表示的是意思是将所有目标是HOST的邮件转发到targetHOST邮件服务器中,这个是最常规的表示方式。
下面给一些例子和一些特殊的表示方法:
cnunix.com.cn:cnunix.com //这行表示的意思是将所有发往cnunix.com.cn的邮件全部转发到cnunix.com这个邮件服务器。qmail不会在cnunix.com.cn邮件服务器中投递时查询DNS中的MX记录,直接投递到cnunix.com邮件服务器中,加快了投递的速度。
.cnunix.net: //这行表示强迫qmail对DNS的MX记录进行查询,因为没有定义转发的主机名,qmail将在DNS中查询任何以.cnunix.net结尾的邮件服务器
:mail.cnunix.com.cn:2525 //这行定义表示所有发往该机器的所有邮件都将会转发到mail.cnunix.com.cn这个邮件服务器,并且转发到对方的2525端口,这个方式一般都是做邮件网关时用的到的。

注意:smtproutes如果设置不正确或者DNS的变动会产生邮件的循环投递。

17. timeoutconnect
这个配置文件定义了qmail-remote在和远端SMTP服务器在SMTP连接断开以前接受一个新的连接等待的最大时间(单位为秒s),默认值为一分钟(60s)。如果你的网络连接速率比较低,就需要相应的调整这个参数到一个适合的值。

18. timeoutremote
这个配置文件定义了qmail-remote等待远端的SMTP服务器相应时等待的最大时间,默认值为20分钟(1200s),如果到达这个最大值没有响应,qmail与对方断开连接并且把失败的记录写到qmail的日志中。

19. smtpgreeting
这个配置文件定义了用户在登录SMTP服务时显示的系统欢迎信息。修改这个信息可以掩盖一些你的系统的本身的信息,可以相对的迷惑一下登录者。

解决qmail DNS的问题:
qmail 1.03版本有个很龌龊的问题,就是默认情况下,qmail无法处理长度超过512字节的DNS响应包。这个错误出现在qmail的dns.c原码中,其中的DNS包的最大值被设置为512了。qmail 的开发者写了个补丁程序放到这个dns.c目录文件中,就可以避免这个问题。
这里我门采用更简单直接的办法修改dns.c源码,在dns.c文件的第24行
static union{ HEADER hdr; unsigned char buf[PACKETSZ]; }response;
编译代码前,把这里的PACKETSZ的值改为65536就可以避免这个问题。

编译qmail 源码:
一但你安置前面的说明配置完你的配置文件,就可以对qmail进行编译了。在qmail-1.03目录下键入:make setup check 就可以编译了。

qmail的基本结构

编译安装完qmail后,会在你的安装目录下生成下面这些目录。
alias 包含qmail别名文件
bin 包含所有qmail可执行文件
boot 包含在不同邮件环境下启动的qmail脚本
control 包含qmail所有配置控制文件
doc 包含qmail文档文件
man 包含qmail帮助文件
queue 包含qmail邮件队列目录
users 包含为qmail-lspawn创建任何qmail用户数据库。

运行ps命令可以查看到qmail的相关进程:

#ps –ax | grep qmail

下面介绍几个主要的qmail程序的用途。 这些配置文件都在qmail的control目录中,一般位于位于/var/qmail/control目录中。

1. qmail-clean 用来从邮件队列中将那些看起来已经投送失败的未发送消息删除掉。 qmail使用多种状态标示来标记邮件,每个邮件在每一次被处理后它的状态表示都会被改变。如果系统当机,系统重新启动以后,qmail-send仍然可以找到邮件队列中上次最后一次成功处理过的邮件的位置,并且从这里重新开始处理邮件队列。如果由于其他原因造成qmail-send不能处理的邮件队列,qmail-send会调用qmail-clean从邮件队列中删除邮件。qmail-clean也是常驻内存的进程。

2. qmail-inject 用来把新的消息插入到邮件队列中去。在邮件传递给qmail-queue之前,qmail-inject先扫面邮件的邮件头,来查看邮件头是否符合RFC822标准,如果不符合它将会自动的更改和修正这个邮件的邮件头。

3. qmail-local 是被qmail用来将消息投送给邮件服务器上的一个本地用户。 这个程序通常是用来检测因为转发命令使用不当造成的邮件循环故障

4. qmail-lspawn 用来调度本地邮件系统上的邮件投送进程的。 是负责目标地址是为本地邮件服务器的邮件。

5. qmail-pop3 是一个pop3服务程序的qmail版本,它允许远程客户使用POP协议直接连接到qmail邮件服务器并阅读他们的信息。这个程序工作在qmail 在Maildir邮箱格式下。

6. qmail-popup 是用在与qmail-pop3d程序的连接上的。Qmail-popup程序在激活qmail-pop3d程序前,验证pop3连接的pop用户id和密码。

7. qmail-qmtpd 用来接收来自远程客户端的qmail连接的。一但请求接收到,任何接收到的消息都会被传送给qmai-queue程序插入到qmail邮件队列中。

8. qmail-queue 用来处理来自qmail-inject 和 qmail-smtpd程序的邮件消息。并将他们移到一个邮件队列中等待发送。

9. qmail-remote程序是用来把邮件消息投送给远程目的邮件主机。

10. qmail-rspawn是被qmail-send程序调用,他用在将消息传送给远程邮件服务器上的用户时。 当qmail-send判明邮件目标地址是远端邮件服务器时,qmail-send就会将邮件交给qmail-rspawn,qmail-rspawn的作用是调度邮件的投递时间和顺序,然后激活qmail-remote来进行投递。qmail-rspawn还有一个作用是决定每一个邮件的目标邮件服务器,每次和远端邮件服务器的连接都会调用qmail-remote一次。qmail-rspawn也是常驻内存的进程。

11. qmail-send 用在当消息成功放到邮件队列中后,qmail-send 程序就被调用来处理消息。

12. qmail-smtpd 用来监听来自远程邮件服务器的网络连接请求。

13. splogger 用来向系统登陆日志程序插入项的。
安装配置实例

环境:redhat9.0, qmail-1.03, ucspi-tcp-0.88, checkpassword-0.90.

所需软件包
qmail-1.03.tar.gz (http://www.qmail.org
ucspi-tcp-0_88_tar.gz (http://cr.yp.to/ucspi-tcp.html
checkpassword-0_90_tar.gz (http://cr.yp.to/checkpwd.html

1. 卸载SendMail邮件系统
由于大多数Linux发行版本中都预装了SendMail邮件系统,所以在安装qmail 邮件服务器
前最好卸载SendMail邮件服务器,使用如下命令:

1
2
3
4
5
#ntsysv (取消系统boot时启动SendMail) 也可以用setup程序来实现。
#mv /usr/lib/sendmail /usr/lib/sendmail.bak
#mv /usr/sbin/sendmail /usr/sbin/sendmail.bak
#mv /usr/bin/newaliases /usr/bin/newaliases.bak
#mv /usr/bin/mailq /usr/bin/mailq.bak

2.配置qmail编译前的一些设置。
a)先在qmail的控制文件me中写入本地机器的域名

1
echo magicunix.com > /var/qmail/control/me

或者

1
./config-fast magicunix.com

b)建立相应的配置文件,使用如下命令:

1
2
3
#cd /var/qmail/alias
#touch .qmail-postmaster .qmail-mailer-daemon .qmail-root
#chmod 644 .qmail*

c)给root用户建立个别名用户alias

1
echo alias > /var/qmail/alias/.qmail-root

( 这里我把用户alias作为root 用户的别名)

d)配置qmail的主目录
这里采用qmail默认目录,所以不需要修改任何地方。

e)设置qmail系统用户组:

1
2
3
#cd /usr/sbin/
#./groupadd nofiles
#./groupadd qmail

f)设置qmail系统用户:

1
2
3
4
5
6
7
8
#cd /usr/sbin/
#./useradd -g nofiles -d /var/qmail/alias alias
#./useradd -g nofiles -d /var/qmail qmaild
#./useradd -g nofiles -d /var/qmail qmaill
#./useradd -g nofiles -d /var/qmail qmailp
#./useradd -g qmail -d /var/qmail qmailq
#./useradd -g qmail -d /var/qmail qmailr
#./useradd -g qmail -d /var/qmail qmails

g)解决qmail源码的几个问题:
DNS问题:
在dns.c文件的第24行

1
static union{ HEADER hdr; unsigned char buf[PACKETSZ]; }response;

把这里的PACKETSZ的值改为65536。

编译时出现errno没有定义的错误问题:
在qmail原代码里的error.h文件的头部填加一条:#include

3. 安装qmail软件包
a)手工创建/var/qmail目录。

1
#mkdir /var/qmail

b) 编译qmail源代码并安装,使用如下命令:

1
2
3
#tar zxvf qmail-1.03.tar.gz
#cd qmail-1.03
#make setup check

c) 选择邮件的存储方式:
这里采用$(HOME)/Maildir 方式

1
#cp /var/qmail/boot/home /var/qmail/rc

执行完rc内的内容应该为:

1
2
3
4
5
#!/bin/sh
# Using splogger to send the log through syslog.
# Using qmail-local to deliver messages to ~/Mailbox by default.
exec env - PATH="/var/qmail/bin:$PATH" \
qmail-start ./Maildir splogger qmail

4. 建立启动qmail的shell文件start.sh,其内容为:

1
2
#cat > start.sh
csh -cf ‘/var/qmail/rc &'

5. 添加Pop3和SMTP服务
可以使用xinetd超级服务器或TcpServer来建立相应的Pop3和SMTP服务,为了支持大容量的邮件用户,建议使用TcpServer来监听服务端口启动相应的服务,建立命令如下:

1
2
3
#tar zxvf ucspi-tcp-0_88_tar.gz
#cd ucspi-tcp-0.88
#make setup check

如果编译出现没有定义errno的错误,在原文件的error.h文件头部填加:#include

1
2
3
#tar zxvf checkpassword-0_90_tar.gz
#cd checkpassword-0.90
#make setup check

建立tcpserver规则数据库:

1
2
3
4
5
6
7
8
#cd /usr/local/bin/
#cat > rules.txt
192.168\. : allow, RELAYCLIENT=""
192.168.1.10: deny
192.168.2\. :deny
:deny
#
#at rules.txt | tcprules rules.cdb rules.tmp

修改上面建立的启动脚本start.sh文件,修改后的内容如下:

1
2
3
4
#cat start.sh
csh -cf ‘/var/qmail/rc &'
tcpserver -u 502 -g 501 -c 100 -x /usr/local/bin/rules.cdb 0 smtp /var/qmail/bin/qmail-smtpd &
tcpserver -c 100 0 pop3 /var/qmail/bin/qmail-popup magicunix.com /bin/checkpassword /var/qmail/bin/qmail-pop3d ./Maildir/ &

(注意: 最后一个参数必须和/var/qmail/rc中的qmail-start的第一个参数一致,比如qmail-start ./Maildir/ splogger qmail )

修改/var/qmail/control/rcpthosts文件,来增加SMTP接收邮件的域
例如:

1
2
3
4
5
#cat /var/qmail/control/rcpthosts
magicunix.com
hryj.com.cn
sina.com.cn
openet.com.cn

6. 启动qmail进程
a)启动qmail

1
2
#cd /var/qmail
#./start.sh

b)检察qmail个进程是否启动正常:

1
2
3
4
5
6
#ps -ef | grep qmail
qmails 29052 29051 0 Aug23 ? 00:00:00 [qmail-send]
qmaill 29055 29052 0 Aug23 ? 00:00:00 [splogger]
root 29056 29052 0 Aug23 ? 00:00:00 qmail-lspawn ./Maildir
qmailr 29057 29052 0 Aug23 ? 00:00:00 [qmail-rspawn]
qmailq 29058 29052 0 Aug23 ? 00:00:00 [qmail-clean]

c)检查smtp是否已经开起:

1
2
3
4
5
6
7
8
9
# telnet localhost 25
Trying 127.0.0.1...
Connected to LINUX-SERVER (127.0.0.1).
Escape character is '^]'.
220 localhost.localdomain ESMTP Sendmail 8.12.8/8.12.8; Wed, 25 Aug 2004 17:44:43 +0800
QUIT(输入QUIT可以退出)
221 2.0.0 localhost.localdomain closing connection
Connection closed by foreign host.
#

检查pop3是否已经开器

1
2
3
4
5
6
7
8
9
# telnet localhost 110
Trying 127.0.0.1...
Connected to LINUX-SERVER (127.0.0.1).
Escape character is '^]'.
+OK &lt;4114.1093427217@magicunix.com<wbr />&gt;
QUIT (输入QUIT可以退出)
+OK
Connection closed by foreign host.
#

上面情况证明一切正常。

3.测试
a)建立测试用户。
为了实现测试,我门需要建立2个测试的系统帐号。

1
2
3
4
5
#cd /usr/sbin/
#./useradd test1
#passwd test1 (口令为test1)
#./useradd test2
#passwd test2 (口令为test2)

下面分别给用户test1,test2分配Maildir邮箱。(注意这里必须用用户权限建立邮箱)

1
2
3
4
5
6
#su test1
$/var/qmail/bin/maildirmake Maildir
$echo ./Maildir &gt; .qmail
#su test2
$/var/qmail/bin/maildirmake Maildir
$echo ./Maildir &gt; .qmail

为了使root用户也能收mail?这样给root的别名用户alias也创建个邮箱。

1
2
3
#su alias
$/var/qmail/bin/maildirmake Maildir
$echo ./Maildir &gt; .qmail

为了测试远程投送,这里我们引用两个email地址:hlxia@0-100.com.cn, xhl_c@hotmail

b)测试qmail本地投送。
首先准备一个RFC822格式的测试文件:

1
2
3
4
5
6
7
#su test1
$cat &gt; test_mail1
to: test2
from: test1
subject:This is a local test mail.
This is a local test mail content ...
$

现在调用qmail-inject发送mail.

1
$cat test_mail1 | /var/qmail/bin/qmail-inject

现在检查mail的本地投送是否成功。(//后面的是我的注释)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
$ telnet localhost 110
Trying 127.0.0.1...
Connected to LINUX-SERVER (127.0.0.1).
Escape character is '^]'.
+OK &lt;4519.1093427483@magicunix.com<wbr />&gt;

user test2 //验证用户名
+OK //返回OK说明正确
pass test2 //验证用户口令
+OK
list //列出此用户的邮件列表
+OK
1 427 //此用户现在一共有3封mail
2 424
3 424
.
retr 3 //查看mail 3的内容

+OK
Return-Path:
Delivered-To: test2@magicunix.com
Received: (qmail 4462 invoked by uid 0); 25 Aug 2004 09:51:03 -0000
Date: 25 Aug 2004 09:51:03 -0000
Message-ID: &lt;20040825095103.4461.qmail@<wbr />magicunix.com&gt;
to: test2@magicunix.com
from: test1@magicunix.com
subject:This is local a test mail.

This is a local test mail content ...


.
dele 1 //删除mail 1

+OK
list //从新查看mail 列表,发现mail已经不存在了。
+OK
2 424
3 424
.
quit //退出连接。

+OK
Connection closed by foreign host.

c)测试qmail远程投送。
首先准备一个RFC822格式的测试文件:

1
2
3
4
5
6
7
#su test1
$cat &gt; test_mail2
to: hlxia@0-100.com.cn
from: test1
subject:This is a desc test mail.
This is a desc test mail content ...
$

现在调用qmail-inject发送mail.

1
$cat test_mail2 | /var/qmail/bin/qmail-inject

打开
hlxia@0-100.com.cn信箱查看是否收到test1发的mail.

d)测试qmail别名投送。
首先准备一个RFC822格式的测试文件:

1
2
3
4
5
6
7
#su test1
$cat &gt; test_mail3
to: root
from: test1
subject:This is a alias test mail.
This is a alias test mail content ...
$

现在调用qmail-inject发送mail.

1
$cat test_mail3 | /var/qmail/bin/qmail-inject

检查alias用户是否收到发给root用户的mail

1
2
3
4
#su alias
$cd ~
$cd Madir/new
1093427463.4465.LINUX-SERVER

说明已经收到了。也可以用telnet localhost 110来打开mail,这里就不做了,具体请参考本地投送那节。

e)测试qmail群发。
首先准备一个RFC822格式的测试文件:

1
2
3
4
5
6
7
#su test1
$cat &gt; test_mail4
to: postmaster
from: test1
subject:This is a postmaster test mail.
This is a postmaster test mail content ...
$

现在调用qmail-inject发送mail.

1
$cat test_mail4 | /var/qmail/bin/qmail-inject

现在检查/var/qmail/alias/.qmail-postmaster文件里有多少个别名
test1
test2
alias
hlxia
有4个用户,下面分别检查这4个用户是否都收到刚才发的test_mail4

f)用foxmail测试pop3是否工作正常。
找台能连接到主机的win机器,按下列方法配置foxmail:
电子邮件地址:test2@magicunix.com
SMTP:magicunix.com
POP3:magicunix.com
然后测试连接。如果一切正常,就可以连接上了。然后收一下mail,正常可以发现,2封刚才发送的mail,一个是test1用户发送给test2的,一个是test1群发的。
g)测试tcpserver的规则数据库过滤smtp的功能。
简单的tcpserver规则数据库创建及使用方法一节中有详细的说明,可以参照说明进行测试,这里我就不在重复实现了。