用Linux作邮件服务器(3)
来源:中国站长站 时间:2008-07-28 17:30:00
别名可用于创建邮递列表(mailing list)。在上面的例子中,别名Webmaster实际上就是一个本地邮件管理员的邮递列表。对于大型的或是经常改动的名单,可以利用:include的别名形式指示sendmail从文件中来读取名单的成员。如果别名文件包含行
friends::include:/home/zhang/friends.aliases
并且文件/home/zhang/friends.aliases包含
zhang
li
liu
deng
其效果与下面的别名相同: friends:zhang,li,liu,deng
这一指令对于经常变动的邮递列表或哪些由用户而非邮件管理员管理的邮递列表非常方便。如果经常要求改变邮件别名,用户一般希望把它置于自己的控制之下。
别名文件也可被用于向某个程序发送邮件的内容。例如,许多邮递列表的设立使用户可以获取所列的信息或通过向某一特定地址list-request发送信件来订阅信息。这种信件的主体通常只包含一个单词,比如help或subscribe等。假设zhang的邮递列表具有名为zhang-request的一个地址: zhang-request:|/usr/local/lib/auto-zhang-reply
上面表达式的管道号(|)通知sendmail使用程序的邮件发送器,它通常被定义为/bin/sh。sendmail将把信息提供给/usr/local/lib/auto-zhag-reply的标准输入,如果它存在,sendmail便考虑投递信件。
用户还可以创建一个使sendmail向文件发送邮件的别名。下面是别名nobody的一个例子,它在运行NFS的系统上是经常用到: nobody:/dev/null
指定文件的别名让sendmail把它的消息追加到被指定的文件中。由于这一特殊的文件/dev/null是Linux/UNIX的“黑洞”,因此这一别名的作用其实是把邮件清除掉。
4.配置sendmail
sendmail是通过sendmail.cf文件来配置的。这个文件通常在目录/etc下面(有些系统是在/etc/lib目录下)。sendmail.cf文件使用的语法与其他配置文件不同,而且比较复杂。 配置文件的每一行都以一个单一的命令字符开头,这一字符用来说明这一行的功能和语法。以#开始的行是注释行;空行被忽略;以空格或制表符开始的行是上一行的续行,不过通常应该尽量避免续行。
下面介绍命令字符及它们的功能。
(1)# 注释符,以#开始的行是注释行。例如:
# This file is a sample for sendmail.cf
(2)D 使用格式为:DX string。其功能是把宏X定义为字符串string,例如:
DM mail.somedomain.gov
(3)C 使用格式为:CX word1、word2。其功能是把类X定义为wor1、word2。例如:
Cw localhost my_host
(4)F 使用格式为:FX/file。其功能是把类X定义为从文件中读取的值。例如:
Fw/etc/hosts
(5)H 使用格式为:H?mailerflag?name:template。其功能是定义邮件头。例如:
H?F?From:\ $ q
(6)O 使用格式为:OX option arguments。其功能是设置选X,例如:
OL8# sets the log level to 8
(7)P 使用格式为:Pclass=n。其功能是根据邮件的类来设置邮件投递的优先级。例如:
Pjunk=100
(8)V 使用格式为:Vn。其功能是把配置文件的版本级别通知给V8 sendmail。例如:
V3
(9)K 使用格式为:Kname class arguments。其功能是定义一个关键字文件(数据库映像)。例如:
Kuucphosts dbm/etc/uuphosts
(10)M 使用格式为:Mname,f1=v1,...。其功能是定义一个邮件发送器。例如:
Mpop,P=/bin/sh,F=lsD,A=sh-c $ u
(11)S 使用格式为:Sun。其功能是开始一个新的规则集。例如:
S22
(12)R 使用格式为:Rlhs rhs comment。其功能是定义一个匹配/改写规则。例如:
R $ + $>jg call ruleset jg
上面简要地列出操作符的类型,现在对它们进行详细介绍。
· D操作符
宏类似于shell变量,当为一个宏定义了值后,可以在配置文件的后面对它进行引用,宏将会被它的值所取代。例如,一个配置文件可能有多个行包含假设的邮件中心mailer.somedomain.gov,为了避免一次次地输入这一名字,可以像下面这样为它定义一个宏R(代表中继邮件发送器): DRmailer.somedomain.gov
当sendmail在sendmail.cf中遇到$ R时,sendmail将把它替换为字符串mailer.somedomain.gov。
宏的名字通常是一个单字符。sendmail定义有许多宏,这些宏不应该被再次定义,否则将会出现一些问题。sendmail使用小写字母来定义自己的宏;大写字母可以被自由地使用。
· C和F操作符
类与宏类似,但是在改写规则中用于不同目的。与宏一样,类的名字也是一个单字符,小写字母由sendmail使用,大写字母用于由用户定义的类。一个类包含有一个或多个单词。例如,可以像下面这样定义一个包含本地域中所有主机的类H:
CH host1 host2 host3
为了方便起见,大型的类可以续行定义。下面对类H的定义与前面定义的结果相同:
CH host1
CH host2
CH host3
另外,还可以通过从某个文件中读取它的单词来定义一个类:
CF/usr/usr/localhosts
如果/usr/localhosts文件包含host1、host2和host3,并且是每行包含一个,那么这一定义与前面的两个定义等价。
使用宏和类最主要的优点是它们可以集中配置文件中信息。在上面的例子中,如果决定把邮件中心由mailer.somedomain.gov改变为mail1.somedomain.gov,只需要改变宏$ R的定义就可以了,配置文件仅需作很少的改动。否则名字mailer.somedomain.gov散布在文件的各处,那么可能会在某些位置忘记对它们进行修改。另外,集中的重要信息,可以在单独的一个位置详细地对它进行注释。由于配置文件一般比较难于理解,因此如果很长一段时间之后对自己所做的某种修改感到迷惑时,在配置文件中添加详细的注释是很有帮助的。
· H操作符
一般情况下,不需要对V8 sendmail的配置文件所提供的邮件头进行修改,因为它们已经遵从了公认的标准。下面是一些邮件头的示例:
H? D? Date:$ a
H? F? Resent-From:$ q
H? F? From:$ q
H? x? Full-Nnme:$ x
可以看出,在邮件头的定义中可以使用宏。当插入到某封信中时,这些宏将被扩充,由它相应的值取代。例如,在上面的例子中,用于邮件头定义Full-Name:中的宏$ x将被取代为寄件人的全名。在邮件头的定义中,可选的?mailerflag?结构用来通知sendmail只有在被选择的邮件发送器具有这一邮件发送器标志时才插入邮件头。
假如在用户的本地邮件发送器的定义中具有一个标志Q,并且sendmail选择了这一邮件发送器来投递某一信件,如果配置文件包含一个像下面这样的邮件头定义,那么sendmail将把这一邮件头插入到通过本地邮件发送器被传递的信件中,并利用相应的值来取代宏$ F:
H? Q? It-is-a-wonderful-day:$ F
为什么要使用?mailerflag这一特性呢?这是因为不同的协议可能会需要不同的邮件头。另外,由于它们也需要不同的邮件发送器,因此可以通过在邮件发送器定义中定义相应的邮件发送器标志并使用邮件头定义中的?mailerflag?结构,来通知sendmail是否要插入相应的邮件头。
·0操作符
sendmail具有许多选项,这些选项用来改变sendmail的操作或者用来通知所使用的文件的位置。大部分选项既可以通过命令行提供,也可以在配置文件中给出。例如,用户既可以在命令行也可以在配置文件中指定别名文件的位置;要是在命令行上指定别名文件,可以使用-o选项:
$ sendmail-oA/etc/aliases[other arguments...]
要是在配置文件指定别名文件,可在配置文件中添加如下一行代码:
OA/etc/aliases
这两种方法是等价的。但由于像别名文件的位置这种选项很少改变,因此人们通常在配置文件sendmail.cf中设置它们。V8 sendmail的所有选项在SIOG中有详细的说明。
·P操作符
用户可以在邮件中包含用来指示邮件的相对重要性的标题,sendmail可以根据这些标题来确定信件的优先级。假设为V8 sendmail提供的优先级如下:
Pemergent=100
Pimportant=0
Pnormal=-30
如果用户在他们的信息中含有标题Drecedence:normal,那么sendmail为这些邮件提供的优先级要低于利用标题Precedence:importangt的邮件。
·V操作符
随着V8 sendmail的发展,许多Linux爱好者不断地为它添加新的特性。 V操作符能够使V8 sendmail知道在配置文件中它可以找到哪些特性。老版本的sendmail不能够识别这一命令。在SIOG中,对配置文件的版本级别有详细的说明。
注意:配置文件的版本级别与sendmail的版本级别并不是一回事。V8 sendmail能够理解从版本1到5的配置文件,而版本8的配置文件并不存在。
·K操作符
sendmail使用了一些关键字数据库,例如别名数据库。假如给定了关键词Webmaster,sendmail将查找与这一关键词相关的数据,V8 sendmail把这一概念扩充为任意的数据库,包括NIS mps(Sun的网络信息服务,以前称为Yellow Pages或YP)。K操作符可用来向sendmail通知数据库的位置、类别以及访问方法。V8 sendmail支持的数据库类别有:dbm、btree、hash和NIS。当在Linux下编译时,使用的缺省类别是dbm格式。
· M操作符
前面已经介绍,邮件发送器或者是MTA或者是最终投递代理。针对每种目标,可以定义一个专门的邮件发送器。即使SMTP,MTA被内置,它也必须有一个邮件发送器定义来定制sendmail的SMTP操作。邮件发送器的定义是很重要的,因为所有收件人的地址必须在0号规则集中解析到一个邮件发送器。解析到本地邮件发送器将通过定义在其中的最终投递代理(例如/bin/mail)把信件发送给某个本地用户,解析到SMTP邮件发送器将通过定义在其中的sendmail固有的SMTP传输器把信件发送给另一个主机。下面将介绍一个定义邮件发送器的具体例子,由于sendmail需要一个本地邮件发送器,因此给出下面的例子:
Mlocal,P=/bin/mail,F=lsDFMfSn,S=10,R=20, A=mail-d$u
所有邮件发送器的定义都以M操作符和邮件发送器的名字开头(在本例中是local),其他字段在后面并且分别由逗号隔开。每个字段由字段名和它的值组成,字段名和它的值之间由等号(=)隔开,允许使用的字段在SIOG中有详细的说明。在上面本地邮件发送器的定义中,P=用来提供投递邮件的程序所处位置的路径名/bin/mail,字段F=用来为本地邮件发送器提供sendmail标志。这些标志并不是要传递给字段P=中所指定的命令,而是由sendmail用来根据它所选择的邮件发送器来修改自己的操作。例如,sendmail通常在调用邮件发送器之前放弃它的超级用户身份,但可以利用S邮件发送器标志通知sendmail为某些邮件发送器保持这一身份。
字段S=和R=用来为sendmail指定在改写寄件人和收件人地址时用到的规则集。由于可以为所定义的每个邮件发送器提供不同的R=和S=标志,因此可以针对每个邮件发送器的不同特点来改写地址。例如,如果用户的UUCP邻居运行的是一个不理解域寻址的陈旧软件,就需要专门为他的站点说明一个特殊的邮件发送器,并编写针对特殊邮件发送器的规则集,从而把地址转化为它的邮件发送器能够理解的形式。S=和R=字段还可以指定用来改写信封和邮件头地址的各种规则集,像S=21/31这种表达式将通知sendmail利用21号规则集改写寄件人的信封地址,利用31号规则集改写寄件人的邮件头地址。这一功能对于要求不同地显示信封地址和邮件头地址的邮件发送器是很有用的。
A=字段用来为被运行的程序(在本例中是/bin/mail)提供变量向量(命令行)。在这一例子中,sendmail将运行命令mail-d $ u,其中的宏$ u将被替换为被投递的用户名:
/bin/mail-d zhang
用户可以在命令提示符下把与此完全相同的命令输人到shell中。
另外,还可以使用其他邮件发送器标志来调节邮件发送器。例如,可以根据不同的邮件发送器限制信息的最大尺寸。
· S和R操作符
配置文件sendlnail.cf由一系列规则集组成。规则集用来探测错误的地址,把地址改写为远程邮件发送器能够理解的形式,以及把邮件路由到sendmail内部的某个邮件发送器。sendmail按照固有的顺序向规则集传送地址,而规则集可以不按固有顺序调用其他规则集。固有顺序与处理的邮件地址以及选择用来投递信件的邮件发送器有关。
规则集由S命令宣布,跟在它后面的是用来标识规则集的编号。sendmail将收集下面的R(规则)行,直到它发现另一个S操作符或配置文件的结尾为止。下面的例子定义了编号为11的规则集:
# Ruleset 11
S11
R $+$:$>22$ I call ruleset 22
sendmail分三种途径来处理地址:一种用来选择投递代理,一种用来处理寄件人地址,一种用于处理收件人地址。
所有邮件地址首先被传送到3号规则集进行预处理从而变为规范的形式,以便其他规则集能够容易地处理它们。不论地址多么复杂,3号规则集的任务是确定某个邮件应该被发送到的下一个主机,3号规则集将尽力在地址中查找那一主机并利用尖括号来标记它。例如,在一种最简单的情况下,一个像zhang@somedomain.gov这样的地址将变为znahg<@simedomain.gov>。接下来,0号规则集将确定用于每个收件人的正确的投递代理(邮件发送器)。例如,一封由li@othedomain.com发给zhang@somedomain.gov(一个Internet站点)和posthost!zhao(一个老式的UUCP站点)的信件需要两个不同的邮件发送器:一个用于somedomain.gov的SMTP邮件发送器,一个用于posthost的老式UUCP邮件发送器。邮件发送器的选择决定以后对寄件和收件人的地址进行处理的方式。因为提供在“S=”和“R=”邮件发送器标志中的规则集根据邮件发送器的不同而不同。
经过0号规则集的地址必须解析到一个邮件发送器。因此,当某个地址匹配lhs时,rhs将提供一个由邮件发送器、用户和主机组成的三元组。下面这行代码表示的是解析到某个邮件发送器的规则的语法:
Rlhs $ # mailer $ @host $:user some comment here...
上面的mailer是利用M命令定义的一个邮件发送器的名字,例如SMTP。host和user通常是来自lhs的位置宏。
当sendmail在0号规则集中选择了一个邮件发送器后,它将通过1号规则集(通常是空的)来处理寄件人的地址,然后把它们发送到由这个邮件发送器中的S=标志所提供的规则集。
类似地,sendmail将把收件人的地址发送到2号规则集(通常也是空的)中进行处理,然后再把它们发送到邮件发送器的标志R=所提供的规则集。
最后,sendmail将在4号规则集中对所有地址进行后处理。4号规则集将把在3号规则集中插入的尖括号删除。
为什么不同的邮件发送器具有不同的S=和R=标志呢?考虑前面向zhang@somedomain.gov和posthost!zhao发送信件的例子。如果li@othedomain.com发送邮件,它必须针对每个收件人以不同的形式出现。对于zhang,它应该是一个域地址li@otherdomain.com;对于zhao,由于otherdomain.com希望老式的UUCP地址(假设它具有一个连向posthost的UUCP链接并且otherdomain.com的UUCP主机名是posthost),因此返回地址应该是otherdomain!li。zhang的地址也必须为posthost的UUCP邮件发送器进行改写,并且zhang的副本必须包括他的邮件发送器可以处理的posthost的地址。
当sendmail把一个地址传递给某个规则集后,规则集中的每一个规则行将逐行地对它进行处理。如果某个规则行的lhs与地址相匹配,地址将被Ihs改写;如果它不匹配,sendmail将继续到下一个规则,直到达到规则集的末尾为止。在规则集的最后,sendmail将把被改写的地址返回给调用规则集的规则集或是固有执行顺序的下一个规则集。
如果某个地址与lhs相匹配并且被rhs改写,这一规则将再次被调用——一个隐式循环。
如前面介绍,每一个改写规则都由R命令引人并且具有三个字段——左端(lhs或匹配端)、右端(rhs或改写端)以及可选的注释,各个字段之间必须由制表符隔开:
Rlhs rhs coment
sendmail将把地址和规则的lhs解析成标记,然后再逐个标记地把地址和lhs进行比较。宏$o包含sendmail用来把地址分隔成标记的字符,它通常具有如下的定义:
#address delimiter characters
Do.:%@!^/[]
$o中的所有字符,既是标记分隔符,又是标记。sendmail获取像xie@rainbow.org这样的地址,然后根据宏o中的字符把它分解成多个标记,就像下面这样:
“xie”“@”“rainbow”“.”“org”
sendmail也把改写规则lhs分解为标记,从而使它们可以同输人的地址一个个地相比较,以确定是否匹配。例如,lhs $ -@rainbow.org分解后的内容如下:
$-”“@”“rainbow”“.”“org”
其中“$-”是一个模式匹配操作符,类似于shell通配符,它用来匹配任何单一的标记符。现在可以把两组标记放在一起来揭示sendmail如何确定一个地址是否与规则的lhs相匹配。
“xie”“@”“rainbow”“.”“org”
“$”“@”“rainbow”“.”“org”
在此例中,由于来自地址的每个标记都匹配一个常量字符串(例如rainbow)或一个模式匹配操作符($-),因此地址和lhs相匹配并且sendmail将使用rhs来改写这一地址。接下来,考查一个改变$o值的效果。sendmail把地址xie@rainbow.org分解为五个标记;然而,如果@字符不在$o中,这一地址将完全不同地被分解为三个标记:
“xie@rainbow”“.”“org”
由此看来,改变$o将对sendmail的地址解析产生极大的影响。因此除非用户对自己所做的事情非常有把握,否则应该保持它的原状。
· 规则的左端(lhs)
lhs是sendmail用来与输入地址进行对比的模式,lhs既可以包含普通的文本,又可以包含任何模式匹配操作符。
lhs的模式匹配操作符主要包括:$-(匹配一个标记)、$+(匹配一个或多个标记)、$ *(匹配零个或多个标记)、$@(匹配空的输入,用来调用错误邮件发送器)。
在lhs中,还可以利用下列宏和类的匹配操作符:$ X(匹配宏X的值)、$=C(匹配类C中的任何一个词)、$~C(匹配不属于类C的任何一个标记)。
模式匹配操作符以及宏和类的匹配操作符是十分必要的,因为大多数规则必须匹配许多不同的输入地址。例如,某个规则可能需要匹配所有以somedomain.gov结尾、以一个或多个其他任何内容开头的地址。
· 规则的右端(rhs)
rhs是改写规则,其作用是通知sendmail如何来改写一个与lhs相匹配的地址。lhs可以包含文本、宏及对匹配内容在lhs中的位置把它匹配的内容赋值给一个数字宏$ n。例如,假设地址zhang@hostl.somedomain.gov被传递给下面的规则:
R $+@$+$:$1<@ $ 2>foucus on domain
在此例中,由于zhang与$+(一个或多个任何内容的标记)相匹配,因此sendmail将字符串zhang赋值给$ l;地址中的@与lhs中的@相匹配,但常量字符串不被赋值给位置宏;字符串hostl.somedomain.gov中的标记与第二个$十相匹配,因此它被赋值给了$ 2。这样被改写后的地址是$ l<@$ 2>,也就是zhang<@ hostl.somedomain.gov>。
现在考查下面的这条规则:
R $ * $:$ 1<@ $ j>add local domain
当在rhs改写一个地址后,sendmail将把改写过的地址再与当前规则的lhs相对比。在本例中,由于$*可以与0个或多个任何内容的标记相匹配,因此无论rhs如何改写地址,它将始终与$ *相匹配,因此这一匹配规则将无限制地循环下去。为了避免sendmail在这条规则上进人死循环,引用了$:,它用来通知sendmail只对这一规则使用一次。
当希望一个规则集立即终止并把地址返回给调用这一规则集的规则集或sendmail固有顺序中的下一个规则集时,可以在规则的rhs之前添加$@,它将使sendmail在rhs改写地址之后立即退出这一规则集。
一个规则集可以通过在rhs前面添加$>向另一个规则集传递地址,考查下面这一条规则:
R $ * $:$>22 $ 1 call ruleset 22
rhs的$ *匹配零个或多个标记,因此sendmail将不断地执行rhs,但由于在rhs前具有$:,所以这一规则只被使用一次。在这一规则中,$>22 $ 1 将以 $ 1 为输入地址调用22号规则集。由于$ 1匹配的内容正是lhs中的内容,因此这一条规则只是把当前的输入地址原封不动地传递给22号规则集。22号规则集返回的任何内容都将传递给这一规则集中的下一条规则。
8.2.3 测试sendmail
在安装一个新的或经过修改的sendmail.cf之前,必须对它进行彻底的测试,因为即使是很小的、看起来不会造成危害的修改也可能会导致严重的后果。
测试的第一步是创建一些地址,这些地址应该是用户的站点需要用到的有效地址。例如,在没有UUCP连接Internet站点somedomain.gov处,下面的地址有效。
zhang
zhang@host1.somedomain.gov
znang@somedomain.gov
即使somdomain.gov具有一个UUCP连接,也应该测试这些地址。其他一些需要考虑的地址包括各种别名(例如,Webmaster,include:列表,用来向文件邮寄的别名,用来向程序邮寄的别名)、非本地地址、源路由地址等等。
当创建好测试地址的列表后,可以使用-C和-bt选项来查看将要发生的情况。这些地址至少应该被传递3号规则集和0号规则集,从而确定它们是否能够被路由到正确的邮件发送器。完成这一任务的比较简单的方法是,创建一个包含被调用的规则集以及测试地址的文件,然后对它执行sendmail。例如,如果文件sdml.test包含下面的行:
3,0 zhang
3,0 zhang@hostl.somedomain.gov
3,0 zhang@somedomain.gov
那么,可以通过输入下面的命令来测试配置文件test.cf:
$ sendmail-Ctest.cf-bt< sdml.test
rewrite:ruleset 3 input: zhang
rewrite: ruleset 3 retruns:zhang
[etc.]
还可以对一个或多个地址执行改写过程进行全面测试。例如,如果某个地址被解析到SMTP邮件发送器。并且这个邮件发送器指定R=21,那么可以通过使用3,2,21,4等来测试收件人地址的改写是否正确。
如果到目前为止sendmail.cf的工作正常,那么可以准备发送一些真正的信件。执行如下命令:
$ sendmail-v-oQ/tmp-Ctest.cf recipient</dev/null
这里的一v选项使得sendmall以冗余方式显示信息,从而使用户可以看到所发生的事情。看到的内容或者像zhang…Sen这样的简单信息或者是一个完整的SMTP对话框。-oQ/tmp用来通知sendmail使用/tmp作为它的队列目录。使用这一选项很必要。因为当利用-C选项运行时,sendmail将放弃它的超级用户权限,并且不能把队列文件写到正常的邮件队列目录中。由于这里使用了-C和-oQ选项,因此sendmail在信件中也将包含如下的警告标题,从而向收件人提醒一些伪造的邮件:
X-Authentieation-Warning:somedomain.gov:Processed from queue/tmp
X-Authentication-Warning:somdomain.gov:Processed by zhang with-C srvr.cf
sendmail也将在信件中插入标题Apparemt;u-tp:zhang,这是因为虽然在命令上指定了收件人,但信件中却没有任何内容。在本例中,由于信件来自空文件/dev/null,因此没有可用的To:标题。如果以超级用户的身份执行测试任务,那么就可以不使用-oQ选项,sendmail便不会插人警告标题。通过创建像下面的文件并利用它作为输入来代替/dev/null,可以避免“Apparently-to:”标题的插入。
To:recipient
test
收件人是用户自己,以便检查邮件头的正确性。在一些特殊的情况下,返回地址行必须为SMTP包含一个FQDN。因此像From:zhang@somedomain这样的标题是不正确的,因为它不包含名字的域名部分,但像From:zhang@somedomain.gov这样的标题是正确的。
3 sendmail的工作方式
sendmail应用广泛而且灵活,它有多种工作方式,能充当多种角色。sendmail可以作为邮件路由器、SMTP客户程序和SMTP服务程序。不过,它不执行最终的邮件投递。
sendmail的主要角色是充当一个邮件路由器。即通过它获取信件、检查收件人地址并确定发送信件的最好路径。sendmail是如何来执行这一任务呢?
sendmail可以确定它自己的一些信息,例如当前的时间和它运行所在的主机名,但它的大部分智能是由用户(“邮件管理员”)通过配置文件sendmail.cf的形式来提供的。这个配置文件确切地告诉sendmail如何来处理各类邮件,它非常灵活和强大,并且乍看起来似乎难于理解。
但是,正像上面介绍的那样,V8 sendmail具有一套模块化的配置文件构造块,大多数站点都能够利用这些模块轻易地构造出配置文件,并且还包括许多示例。
像前面提到的那样,sendmail可以作为一个MTA运行,因为它理解SMTP协议(V8 sendmail还理解ESMTP)。由于SMTP是一个面向连接的协议,因此客户程序和服务程序(也称为发送器和 * )总是存在。SMTP客户程序可以向SMTP服务程序投递信件,SMTP服务程序将不断地监听计算机的SMTP端口。sendmail可以是一个SMTP客户程序,也可以是一个SMTP服务程序。当sendmail被MUA运行时,它成为一个SMTP客户程序,并把客户端SMTP通知给SMTP服务程序(不必是另一个sendmail程序);当系统引导并且sendmail以守护进程模式启动时,它不停地运行,为新发来的邮件监听着SMTP端口,这时sendmail就是一个服务程序了。
sendmail所不做的一件事情是邮件的最终投递。sendmail把这一任务留给了其他程序。sendmail是一个大型的、复杂的、利用超级用户权限运行的程序,因此需要有一些方法来解决安全性的问题,有关安全性的问题在sendmail的过去已经发生过许多次。邮件投递的额外复杂性是sendmail有待解决的最后一个问题。