19.6. 消息路由与投递

Exim 将邮件投递分解为三个不同的任务:路由、定向和传输。每种类型都有许多代码模块,并且每个模块都可以单独配置。通常,在配置文件中设置了许多不同的路由器、定向器和传输器。

路由器解析远程地址,确定消息应发送到的主机以及应使用的传输方式。在连接到互联网的主机中,通常只有一个路由器,它通过在 DNS 中查找域来执行解析。或者,可能有一个路由器处理发送到本地 LAN 上主机的地址,而第二个路由器将任何其他地址发送到单个智能主机;例如,ISP 的邮件服务器。

本地地址被交给定向器,通常有多个定向器,用于处理别名和转发,以及识别本地邮箱。邮件列表可以通过别名或转发定向器来处理。如果地址被别名或转发,则生成的任何地址都由路由器或定向器根据需要独立处理。到目前为止,最常见的情况是将邮件投递到邮箱,但消息也可以通过管道传递到命令中,或附加到默认邮箱以外的文件中。

传输负责实现一种投递方法;例如,通过 SMTP 连接发送消息或将其添加到特定的邮箱。路由器和定向器选择用于每个收件人地址的传输方式。如果传输失败,Exim 要么生成退信消息,要么延迟该地址以便稍后重试。

使用 Exim,您可以自由地配置这些任务。对于每个任务,都有许多驱动程序可用,您可以从中选择所需的驱动程序。您在配置文件的不同部分向 Exim 描述它们。首先定义传输器,然后是定向器,最后是路由器。没有内置的默认值,尽管 Exim 附带了一个涵盖简单情况的默认配置文件。如果您想更改 Exim 的路由策略或修改传输器,最好从默认配置开始并进行更改,而不是尝试从头开始设置完整的配置。

19.6.1. 路由消息

当收到要投递的地址时,Exim 首先检查该域是否是本地主机处理的域,方法是将其与local_domains配置变量中的列表进行匹配。如果未设置此选项,则本地主机名将用作唯一的本地域。如果该域是本地域,则该地址将交给定向器。否则,它将被交给路由器,以找出要将消息转发到哪个主机。[1]

19.6.2. 将消息投递到本地地址

最常见的情况是,本地地址只是用户的登录名,在这种情况下,消息将被投递到用户的邮箱,/var/spool/mail/用户名。其他情况包括别名、邮件列表名称以及用户转发邮件。在这些情况下,本地地址扩展为新的地址列表,这些地址可以是本地的也可以是远程的。

除了这些“正常”地址外,Exim 还可以处理其他类型的本地消息目的地,例如文件名和管道命令。投递到文件时,Exim 会附加消息,并在必要时创建文件。文件和管道目标本身不是地址,因此您不能将邮件发送到,例如,/etc/passwd@vbrew.com并期望覆盖密码文件;仅当来自转发或别名文件时,投递到特定文件才有效。但是,请注意,/etc/passwd@vbrew.com是一个语法上有效的电子邮件地址,但如果 Exim 收到它,它将(通常)搜索登录名为/etc/passwd的用户,如果找不到,则会退回该消息。

在别名列表或转发文件中,文件名是以斜杠 (/) 开头的任何内容,且不会解析为完全限定的电子邮件地址。例如,/tmp/junk在转发或别名文件中被解释为文件名,但/tmp/junk@vbrew.com是一个电子邮件地址,尽管它可能不是一个非常有用的地址。但是,当通过 X.400 网关发送邮件时,会看到这种类型的有效地址,因为 X.400 地址以斜杠开头。

类似地,管道命令可以是任何 Unix 命令,前面带有管道符号 (|),除非该字符串解析为包含域的有效电子邮件地址。除非您更改了配置,否则 Exim 不会使用 shell 来运行该命令;相反,它将其分解为命令名称、参数本身,并直接运行它。该消息通过标准输入传递到该命令。

例如,要将邮件列表网关到本地新闻组,您可以使用一个名为 gateit 的 shell 脚本,并设置一个本地别名,该别名使用以下方式将来自此邮件列表的所有消息投递到该脚本|gateit。如果命令行包含逗号,则必须将逗号和前面的管道符号用双引号括起来。

19.6.2.1. 本地用户

本地地址最常见的是表示用户的邮箱。它通常位于/var/spool/mail并具有用户的名称,用户也拥有该文件。如果它不存在,则由 Exim 创建。

在某些配置中,该组设置为用户的组,模式为 0600。在这些情况下,投递过程以用户身份运行,并且用户可以完全删除邮箱。在其他配置中,邮箱的组是mail,它的模式为 660;投递过程在系统 uid 和组mail下运行,并且用户无法删除他们的邮箱文件,尽管他们可以清空它们。

请注意,虽然/var/spool/mail目前是放置邮箱文件的标准位置,但某些邮件软件可能会被编译为使用不同的路径,例如,/usr/spool/mail。如果投递到您机器上的用户始终失败,您应该查看将其符号链接到/var/spool/mail.

是否有帮助。地址 MAILER-DAEMONpostmaster 通常应出现在您的别名文件中,展开为系统管理员的电子邮件地址。MAILER-DAEMON 被 Exim 用作退信消息中的发件人地址。还建议将 root 设置为管理员的别名,尤其是在以收件人用户的权限运行投递时,以避免以 root 身份运行任何投递。

19.6.2.2. 转发

用户可以通过在他们的主目录中创建一个.forward文件来将他们的邮件重定向到其他地址。该文件包含一个由逗号和/或换行符分隔的收件人列表。读取并解释该文件的所有行。可以使用任何类型的地址。一个实际的.forward用于休假的文件的示例可能是
janet, "|vacation"
在对.forward文件的其他描述中,您可能会看到以反斜杠开头的用户名。这在一些较旧的 MTA 中是必需的,以停止搜索新的.forward名称,这可能会导致循环。在 Exim 中不需要反斜杠,Exim 会自动避免此类循环。[2]但是,允许使用反斜杠,事实上,它确实在同时处理多个域的配置中有所不同。如果没有反斜杠,未限定的用户名将使用默认域进行限定;使用反斜杠,传入域将保留。

转发文件中的第一个地址将收到的消息投递到 janet 的邮箱,而 vacation 命令会向发件人返回一条简短的通知。[3]

除了支持“传统”转发文件之外,Exim 还可以配置为允许更复杂的文件,称为 过滤器。过滤器文件不仅仅是一个转发地址列表,还可以包含对收到的消息内容的测试,例如,只有当主题包含消息“紧急”时,才能转发消息。系统管理员必须决定是否允许用户这种灵活性。

19.6.3. 别名文件

Exim 能够处理与 Berkeley 的 sendmail 别名文件兼容的别名文件。别名文件中的条目可以具有以下形式
alias: recipients

收件人是一个以逗号分隔的地址列表,将替换该别名。如果下一行以空格开头,则收件人列表可以跨越换行符。

一个特殊的功能允许 Exim 处理与别名文件分开保存的邮件列表:如果您指定:include文件名作为收件人,Exim 会读取指定的文件并将其内容替换为收件人列表。本章后面的第 19.6.4 节中介绍了处理邮件列表的替代方法。”

主别名文件是/etc/aliases。如果您使此文件对所有人可写或对组可写,Exim 将拒绝使用它并会延迟本地投递。您可以通过设置modemasksystem_aliases定向器中来控制它应用于文件权限的测试。

这是一个示例aliases文件
# vbrew.com /etc/aliases file
hostmaster: janet
postmaster: janet
usenet: phil
# The development mailing list.
development: joe, sue, mark, biff,
        /var/mail/log/development
owner-development: joe
# Announcements of general interest are mailed to all
# of the staff
announce: :include: /etc/Exim/staff,
        /var/mail/log/announce
owner-announce: root
# gate the ppp mailing list to a local newsgroup
ppp-list: "|/usr/local/bin/gateit local.lists.ppp"

当别名文件中存在文件名和管道命令时(如这里所示),需要告诉 Exim 使用哪个用户 ID 来运行投递。必须在 Exim 的配置文件中设置user选项(也可能是group),无论是处理别名的定向器上,还是它将这些项目定向到的传输器上。

如果在投递到从aliases文件生成的地址时发生错误,Exim 将像往常一样向消息的发件人发送退信消息,但这可能不合适。可以使用errors_to选项来指定将退信消息发送到其他位置;例如,发送到邮局管理员。

19.6.4. 邮件列表

邮件列表也可以通过aliases文件来管理,而不是通过forwardfile定向器来管理。这些列表都保存在一个目录中,例如/etc/exim/lists/,名为 nag-bugs 的邮件列表由文件lists/nag-bugs这个文件应该包含成员的地址,地址之间用逗号或换行符分隔。以井号(#)开头的行#被视为注释。一个使用这种数据的简单 director 如下所示:
lists:
  driver = forwardfile
  file = /etc/exim/lists/${local_part}
  no_check_local_user
  errors_to = ${local_part}-request
当这个 director 运行时,文件errors_to选项会被展开。 展开会导致以美元符号开头的字符串的某些部分在每次使用该字符串时被替换。 最简单的展开是插入 Exim 变量的值,这正是这里发生的事情。 子字符串${local_ part}替换了$local_ part的值,这是正在处理的地址的本地部分。

对于每个邮件列表,应该存在一个名为listname-request的用户(或别名或邮件列表);解析地址或向列表成员发送邮件时发生的任何错误都会报告给此地址。

注意

[1]

这是一个简化版本。 directors 可以将地址传递给发送到远程主机的 transports,同样,routers 可以将地址传递给将消息写入文件或管道的本地 transports。 在某些情况下,routers 也可以将地址传递给 directors。

[2]

如果 director 即将处理的地址是它在生成当前地址的过程中先前处理过的地址,则会跳过该 director。

[3]

请注意,如果您选择使用度假程序,请确保它不会回复从邮件列表发送的消息! 发现有人去度假,并且他们收到的每条消息都有一个度假消息,这是非常烦人的。 邮件列表管理员:这是一个很好的例子,说明为什么强制邮件列表消息的Reply-To字段为列表提交地址是一种不好的做法。