网络服务

目 录

  1. 网络服务
    1. wu-ftpd
    2. smbd
    3. apache虚拟服务器
    4. apache+php3+mysql
    5. apache+apacheJserv
    6. apache+tomcat
    7. java环境(中文)
    8. sendmaild
      1. sendmail扮演的角色
      2. 如何运行sendmail
      3. sendmail.cf
      4. 邮件中枢与分发代理
      5. 规则
      6. 规则集0
    9. qmail
    10. router
    11. cvs
    12. bind
    13. openssh
      1. 简单使用
      2. 配置文件
      3. scp
      4. 使用key做验证
      5. rsync与ssh配合使用
      6. cvs
    14. Khttpd


网络服务

[目录]


wu-ftpd

chmod 777 /home/ftpd/incoming
chown ftp /home/ftpd/incoming
chgrp ftp /home/ftpd/incoming


改etc下的ftpaccess,我的是这样:

loginfails 2

class   local   real,guest,anonymous *.domain 0.0.0.0
class   remote  real,guest,anonymous *

limit   local   20  Any                 /etc/msgs/msg.toomany
limit   remote  100 SaSu|Any1800-0600   /etc/msgs/msg.toomany
limit   remote  60  Any                 /etc/msgs/msg.toomany

readme  README*    login
readme  README*    cwd=*

message /welcome.msg            login
message .message                cwd=*

compress        yes             local remote
tar             yes             local remote

# allow use of private file for SITE GROUP and SITE GPASS?
private         yes

# passwd-check  <none|trivial|rfc822>  [<enforce|warn>]
passwd-check    rfc822  warn

log commands real
log transfers anonymous,real inbound,outbound
shutdown /etc/shutmsg

# all the following default to "yes" for everybody

  delete          no      guest,anonymous         # delete permission?
  overwrite       no      guest,anonymous         # overwrite permission?
  rename          no      guest,anonymous         # rename permission?
  chmod           no      anonymous               # chmod permission?
  umask           no      anonymous               # umask permission?

# specify the upload directory information

  upload  /home/ftp  /pub             no
  upload  /home/ftp  /pub1            no
  upload  /home/ftp  /incoming        yes
  upload  /home/ftp  /pub1/incoming        yes
  upload  /home/ftp  /bin          no
  upload  /home/ftp  /etc          no

  # directory aliases...  [note, the ":" is not required]
alias   inc:    /incoming

# cdpath
cdpath  /incoming
cdpath  /pub
cdpath  /

# path-filter...
path-filter  anonymous  /etc/pathmsg  ^[-A-Za-z0-9_\.]*$  ^\.  ^-
path-filter  guest      /etc/pathmsg  ^[-A-Za-z0-9_\.]*$  ^\.  ^-

# specify which group of users will be treated as "guests".

guestgroup ftponly

email user@hostname


[目录]


smbd

更改/etc/smb.conf,主要要确定下面的内容:
workgroup=MYWKGP
smb passwd file=/etc/smbpasswd
security=user (使用unix用户安全)
encrypt passwords= yes
然后就可以添加目录了,首先添加宿主目录:
(其实就是把对应的行的注释去掉)
[homes]
comment = home directory
browseable = no
writeable= yes
还可以再加一个共享目录:
[public]
comment = public directory
path=/public
public=yes
writeable=no
read only=yes
printable=no
最后当然是加入用户,比如想把fake用户当成windows 系统中的winfake,执行
smbadduser fake:winfake
输入访问口令,以后在windows 98中只要提供winfake和对应口令

就可以访问/home/fake和/public目录

有时windows 95/98会出现一些兼容问题,研究encrypt passwords=那一行吧

==================================================================

最好用swat来设置吧,把/etc/inet.conf最后swat前的注释去掉
然后重启inetd,就可以通过任意一个机器用浏览器来配置了
输入地址http://samba_server:901/即可

95/98/nt都可以的说,还好这里什么机器都有,嘿嘿,都可以互相
访问的,包括打印,95/98网络登录时要用samba里的用户名哦

[global]
        workgroup = IPC
        netbios name = DATASERVER
        server string = Data Server - Linux
        encrypt passwords = Yes
        log file = /var/log/samba/log.%m
        max log size = 50

#这三行设置超时,还没搞清楚,不过这样马马虎虎能工作,一定要设
#否则mount上的设备umount不掉,一直busy的说,:PP
        change notify timeout = 30
        deadtime = 1
        keepalive = 0

        socket options = TCP_NODELAY SO_RCVBUF=8192 SO_SNDBUF=8192
        dns proxy = No

#这三行是umask的设置,看情况设置吧

        create mask = 0755
        force create mode = 0755
        force directory mode = 0755

#用户的home目录,根据用户不同而变化,本来就有的,不是我加的说

[homes]
        comment = Home Directories
        read only = No
        browseable = No

[printers]
        comment = All Printers
        path = /var/spool/samba
        print ok = Yes
        browseable = No

#我自己加的一个共享目录

[public]
        comment = Public Software
        path = /home/ftp/pub
        guest ok = Yes


[目录]


apache虚拟服务器

多域名单IP的:

1. 首先要解决域名的问题,你可以用nslookup <你的域名>看看该域名是不是指向了你IP, *.yeah.net本来就是做的虚拟主机, 所以解析出来都是指向www.yeah.net (202.96.152.198), 通常情况下, apache启动时会检查你用作虚拟主机的域名。

2. 改httpd.conf, 加<virtualhost> 例如:

        <VirtualHost I007.com>  (写IP也可以)
        ServerAdmin [email protected]
        DocumentRoot /home/I007/public_html
        ServerName I007.com  (这里只能写域名)
        </VirtualHost>

多IP的:

1. 最好每个IP还是能对应一个域名
2. 加载ip_alias模块 (/sbin/insmod ip_alias)
3. 加IP  (/sbin/ifconfig eth?:? ???.???.???.???)
4. 改httpd.conf, 和上面差不多

可以看出, 两者可以共存,以上均做过实验, 没有问题..


[目录]


apache+php3+mysql

*安装apache-devel rpm包.
*安装MySQL相关的包.
   rpm -ivh MySQL-3.22.25-1.i386.rpm
   rpm -ivh MySQL-client-3.22.25-1.i386.rpm
   rpm -ivh MySQL-devel-3.22.25-1.i386.rpm
*下载php3源文件php-3.0.tar.gz
*解开源文件
   tar xzvf  php-3.0.tar.gz
*进入php-3.0源文件目录
   ./configure --with-mysql=/usr --with-apxs=/usr/sbin/apxs --with-config-file
-path=/etc/httpd (还有其他选项,可以运行 ./configure --help 看看)
*建一个modules目录
  mkdir modules
*进入regex目录
  运行 make r (这一步可能不必要)
  运行 make lib
*退回上一级目录
  运行 make install
* cp modules/libphp3.so /etc/httpd/modules/
* cp php3.ini-dist /etc/httpd/php3.ini
* 修改/etc/httpd/conf/httpd.conf 加入
        LoadModule php3_module        modules/libphp3.so
        AddModule mod_php3.c
  修改/etc/httpd/conf/srm.conf 加入
        AddType application/x-http-php3 .php3
*重起apache
    /etc/rc.d/init.d/httpd restart
*测试
    查看mysqld是否已经运行.如果没有,先运行mysqld
    将php3源文件下tests目录下的mysql文件拷贝到 /home/httpd/html目录下
    lynx localhost/mysql.php3

    如果有问题,可以将mysql.php3中的127.0.0.1改为localhost


[目录]


apache+apacheJserv

=============================================================================
环境:mdk7
首先使用BJLC光盘上的jdk1.2pre2
具体装过程略
rpm: apache-1.3.9 apache-devel jsdk-2.0
tgz: Apache-Jserv1.1 gnujsp-1.0.0
解开Jserv的src
./configure --enable-EAPI --with-JSDK=/usr/lib/classes/jsdk.jar \
--with-apxs=/usr/sbin/apxs
应该没有问题,很顺利编译通过
再/etc/httpd/conf/httpd.conf
在最后(一定要在最后否则有很多小麻烦)
Include ./jserv/jserv.conf
重新启动apache
http://localhost/servlet/Hello
如果能看到应该就差不多了
试http://localhost/jserv/
一般是403 forbidden
如果是forbidden
修改
jserv.conf
搜索Location
里面有一段allow
改成allow localhost.localdomain
好像就可以了
用localhost或者是127.0.0.1都不行
反正这是我碰到的倒霉事希望大家能一次成功
到这apache-jserv就算是完成了,也就是有了
一个servlet容器并且有了在apache里面的模块
能够同这个容器进行通信,通信协议最好用
apjv12,反正所由地方都设成apjv12就没有问题
否则很可能出现不匹配的情况,最后导致
500错误,在log中显示malformed fata

前面提到servlet容器建好了
Jserv调通了,还有两个很有用的log文件
在我的mdk7里面是
/usr/logs/里面的mod_jserv.log和jserv.log
这两个文件在调试中极有用
它们的权限要求nobody可以写
所以我的做法是
chmod 664 *
chgrp nobody *
大牛是不是这样做,我不知道
可能这样会有安全性问题
前面如果你看到了jserv的状态页点击apjv12://localhost:8007
应该能够看到各个zone的配置
如果看不到应该收到所谓selfservlet一个选项
没有打开的提示,打开就好了好像是在jserv.properties里面
接下来是搞gnujsp
解开了会发现jsp是完全用java写成的例程
主要是两个gnujsp-...jar和servlet-...jar
反正我用的是gnujsp10和servlet-2.0-plus
拷到/usr/servlet/jsp(这是随便给的)
编写gjsp.properties文件

关键是
servlet.gnujsp.class=org.gjt.jsp.JspServlet
servlet.gnijsp.initArgs=checkclass=true,\ //这样编译好的jspservlet可以利用
pagebase=/home/httpd/html,\
scratchdir=/usr/servlets/jsp/scratch,\ //这里要有文章
debug=true,\ //先打开,调好了再关掉
compiler=builtin-javac -classpath \ //这里builtin-javac需要jdk1.2的tools.jar
%classpath%:%scratchdir%:/usr/servlets/jsp/gnujsp.jar:\
/usr/servlets/jsp/servlet.jar:/usr/servlets/jsp/beans \ //指定编译用的classpath
-d %scratchdir% -deprication %source% //就是编译一个普通的java文件一样

首先要注意你选择的scratchdir一定要nobody可写,其次使用builtin的javac
需要在jserv.properties加上
wrapper.class...=/usr/jdk1.2/lib/tools.jar (这是我安装的路径)
到这里jsp的zone就差不多了

现在开始在jserv.properties里面指明zones
很简单不废话了

最后是在jserv.conf里面作mount,照着例子写
也不难,前面有人贴过了
最后就可以试试自己的jsp了
首先先从http://localhost/jserv/
里面看一看jsp的zone配制出来没有
验证一下参数都正确

====================================================================================================

1.先确认apachejserv已能正常工作
2.把gnujsp10.jar,servlet-2.0-plus.jar拷到/home/httpd/sevlets目录下
3.修改jserv.conf,加入
ApJServAction .jsp /servlets/org.gjt.jsp.JspServlet
ApjServMount /java /root(这个目录主要是用来存放jsp生成的java
和class文件,可视具体情况而定)
4.修改jserv.properties,加入
wrapper.classpath=/usr/jdk/lib/tools.jar
wrapper.classpath=/home/httpd/servlets/gnujsp10.jar
wrapper.classpath=/home/httpd/servlets/servlet-2.0-plus.jar
5.修改zone.properties,加入
repositories=/home/httpd/java/
servlet.gnujsp.code=org.gjt.jsp.JspServlet
servlet.org.gjt.jsp.JspServlet.initArgs=checkclass=true
servlet.org.gjt.jsp.JspServlet.initArgs=scratchdir=/home/httpd/java
servlet.org.gjt.jsp.JspServlet.initArgs=compiler=builtin-javac -encoding %enco
ding% -classpath %classpath% -d %scratchdir% -deprecation %source%


[目录]


apache+tomcat

    编译得到mod_jserv.so和ApacheJServ.jar
    在tomcat3.0的etc子目录里面有tomcat.conf,在httpd.conf里面Include一下
启动tomcat(./tomcat.sh start)然后启动httpd,看看成功了没有。
    如果不行,我碰到的一个问题是log file有问题,直接指定成下面的样子
        ApJServLogFile /var/log/httpd/mod_jserv.log
就过了。
    最后实验http://localhost:8080/一下确信tomcat跑了。然后实验http://localhost:8080/examples/jsp和http://localhost/examples/jsp


[目录]


java环境(中文)

    好象有许多人都碰到了这个问题, 我以前也碰到过, 在网上找了一些解决方法看了看, 在自己的机器上试了试, 都没有解决问题, 趁着这几天把 Debian的中文问题搞好了, 顺便把这个也弄一弄, 终于搞明白了一些东西. 错误之处还请大家指教. 这里主要指的是 Swing 中显示的中文, 关于其他中文操作问题,暂不作讨论.
    其实说起来 Java 的中文问题非常简单, 有两个重要因素, 一个是编译时的编码(encoding), 另一个是字体. 这两个缺一不可.

一, 编译时的编码:

    指的是编译 Java 的源程序时指定的编码格式, 即使用

            javac -encoding MyEncoding MyJavaFile.java

    编译 MyJavaFile 时 -encoding 所指定的 MyEncoding. 通常中文的编码有大陆和新加坡使用的 GB2312 以及 台湾和香港使用的 Big5, 普通英文用的是ISO_8859_1. 这里只关注 GB2312. 如果在编译的命令行中指定了编码, 那么就使用指定的编码格式, 如果没有指定, 将使用系统缺省的编码格式, 在 windows的中文简体版下都是 GB2312, 而 Linux 下不同的终端可以有不同的编码, 可以直接设置环境变量 LC_ALL LANG 等指定相应的不同类型信息的编码, 比如数字,日期, 货币, 姓名等等. 可以用 locale 命令来看当前终端所有的编码. 在Java 程序中可以使用 System.getProperty("file.encoding"); (注 1) 来获取当前的编码格式. 好了, 当编译的时候, 源码中的中文字符串都会按照指定的或者缺省的编码格式映射为相应的 UNICODE , 而不是看做简单的 ASCII 字符.所以编码格式就是处理源码中的各种各样的字符, 与运行时无关, 所以如果你的源文件中没有直接需要使用中文的地方, 比如菜单, 按钮, 标签什么的, 就不需要指定编码了.

二, 字体的选择:
    必须使用能够显示中文的字体, 好象有些废话. 但还是有必要说一说. AWT 可以在中文系统中直接显示中文, 但是在不是中文系统但是有中文字体的系统中不能, 需要修改一下 $JAVA_HOME/jre/lib/font.properties,加入中文字体, 至于怎么加入, 就不谈了. 而 Swing 不需要这样, 只要你的系统中有中文字体就可以使用这样的字体来显示中文. 怎么知道一种字体能不能显示中文呢? 看下面的程序片段: (注 2)

        Font[] fonts = GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFFonts();
        System.out.println("Chinese Fonts: \n");
        for (int i = 0; i < fonts.length; i ++)
        {
            if (fonts[i].canDisplayUpTo("\u4e00") > 0)   // 注 3
            {
                System.out.println(fonts[i].getFontName());
            }
        }

    好了, 知道了系统中有没有中文字体, 然后选择一个就是了. 如果选择了不能显示中文的字体, 通常的情况是显示方框.
    好了, Swing 的中文显示介绍完了, 输入和编辑中文就和显示差不多, 选一个字体就可以了, 具体得看你而 Linux 的具体设置了, 这里就不多说了.
    从以上两点来看, 其实最重要的还是字体的选择. 我的网站上有 ScreenShot和测试程序, 欢迎来访问. http://SuperMMX.dhs.org


注 1: 据 jdk1.2.2 的文档, 并没有这个属性, 很奇怪, 但是确实可用.
注 2: jdk 1.2 以上自带的字体支持 Unicode, 但是不支持 Unicode 汉字. 所以打出来的结果包含了 jdk 自带的字体.
注 3: UNICODE 中, UNICODE 汉字从 \u3400 到 \u9FFF 中间, \uF900 到 \uFAFF也有一些, 但是 GB2312 和 Big5 的汉字和字符都是在 \u4E00 到 \u9FFF中间.

参考文献:
jdk tools doc
一篇没有作者名字, 也没有出处的英文文章.

[目录]


sendmaild

最简提示:

    主要的问题是如何配置sendmail.cf.
    如果你用的是redhat,执行:

cd /usr/lib/sendmail-cf/cf
m4 redhat.mc > sendmail.cf
cp sendmail.cf /etc/sendmail.cf
sendmail -bd(或者reboot)

    上面的方法配置之后服务器发信没问题,而远端机器却不能发信,是因为缺省redhat.mc生成的sendmail.cf禁止了客户服务,
这样:找到下面一行(sendmail.cf):

R$*          $#error $@ 5.7.1 $: "550 relay denied"
改成
R$-          $@ ok

    就行了,不过当心有人用你的系统发垃圾邮件


[目录]


sendmail扮演的角色

1. sendmail扮演的角色

  sendmail程序扮演着多种不同的角色,最主要的角色就是传递电子邮件。它监听来自网络的电子邮件,传送电子邮件到另一台机器,通过本地传送,将本地信件传给本地程序。它能够在邮件中附加上文件,也能够使用管道将邮件发给其它程序。它能够维护一个邮件队列,有序地将邮件发送出去,还可以理解接收者的邮件别名,将其发送到真正的目标用户当中去。

1.1在文件系统中的角色

  sendmail程序的角色(位置)在本地文件系统中,就象一个倒置的树,如图3-1。当sendmail运行时,首先读取/etc/sendmail.cf配置文件。在这个配置文件中,能够指出sendmail所需要的其它文件与目录的位置。


  图3-1 sendmail.cf的层次结构

  安全起见,在配置文件/etc/sendmail.cf中指定的文件、目录名一般都使用绝对路径,例如:使用/var/spool/mqueue,而不使用mqueue。在我们讲解这些文件之前,首先运行如下命令以收集一个文件列表:

% grep = /  /etc/sendmail.cf


  注:如果你使用的是V8.7以上版本的sendmail,则你需要查找的应该改为”/[^0-9].*/”.

  这个命令将会出现类似如下的结果:


O AliasFile=/etc/aliases
#O ErrorHeader=/etc/sendmail.oE
O HelpFile=/usr/lib/sendmail.hf
O QueueDirectory=/var/spool/mqueue
O StatusFile=/etc/sendmail.st
#O UserDatabaseSpec=/etc/userdb
#O ServiceSwitchFile=/etc/service.switch
#O HostsFile=/etc/hosts
#O SafeFileEnvironment=/arch
Mlocal,P=/bin/mail, F=lsDFMAw5:/|@rmn, S=10/30, R=20/40,
Mprog,  P=/bin/sh, F=lsDFMoeu, S=10/30, R=20/40, D=$z:/,

  请注意,有些行是以字母O开始,有些行是以字母M开始,有些行是以#开始的。以字母O开始的行是配置选项。字母O后面紧跟着选项的名字。选项的值指出了sendmail使用的文件。例如,AliasFile定义了本地的aliases数据库文件。以M开始的行则定义了分发代理。以#开始的行就是注释。

  首先我们考察以O开始的选项行。然后再分析以M开始的分发代理选项。

1.1.1 aliases文件

  aliasing就是将接收者的名字转换另一个名字。一种情况下是将一些通用名字(如root、webmaster)转换成真正的用户名。另一种情况下是将一个名字转换成多个名字的列表(如使用邮件列表)。

  Aliases文件在sendmail.cf文件中的AliasFile选项中指定,例如:

O AliasFile=/etc/aliases


  以下是一个aliases文件的简要实例:

# Mandatory aliases.
postmaster: root
MAILER-DAEMON: postmaster
# The five forms of aliases
  John_Adams:  adamj
  xpres: ford,carter,bush
  oldlist: :include: /usr/local/oldguys
  nobody: /dev/null
ftphelp:  |/usr/local/bin/sendhelp

  你的aliases文件可能更长、更复杂的,不过,以上这个示例也显示了aliases所有可能的构成。

  以#开始的行,是注释行。空行被忽略不计。第一行就是一个注释行,它指出了2、3两行是每一个aliases文件被必须强制拥有的。所有的别名格式都是一样的,一个名字(别名)和一个要改为成的名字(原名)。别名在“:”号的左边,原名在“:”号的右边。名字是不区分大小写。例如:POSTMASTER,Postmaster以及postmaster都是相同的。

  如果信封上列出的接收者名字是本地用户的话,sendmail程序就会查找aliases文件。如果sendmail发现接收者名字如果与aliases文件中的“:”号左边的名字相匹配,就将接收者名字替换为“:”号右边的名字。例如:发给本地的postmaster的信,会被转变成为发给root的信。

  在一个名字被替换后,会使用这个被替换后的名字继续查找,直到没有匹配的名字为止。如MAILER-DAEMON首先被转变为postmatser,然后postmaster又被改被为root。由于没有一个以root开始的别名项,所以转换过程到此结束,信件最后被传送到root的信箱中去。

  任何一个aliases文件必须有一个将postmaster转变成实际用户的别名项。因为当邮件出现问题时,总是会生成一个错误报告的信,发给postmaster。所以最好将这样的信发给邮件的系统管理员。

  当电子邮件被退回时,将会发给MAILER-DAEMON。所以这个别名是必须的。没有这个设置的话,退回的信将会在发件人与收件人之间不断来回传送。

  Aliases文件中有五种类型:

John_Adams:  adamj
xpres: ford,carter,reagan,bush
oldlist: :include: /usr/local/oldguys
nobody: /dev/null
ftphelp: |/usr/local/bin/sendhelp

  首先我们看一下第一行,这一行的格式与我们前面的例子相似,这一句让sendmail程序将发给John_Adams的信都给真实用户adamj。

  xpres那一行则象大家演示了如何将一个名字扩展到一组名字,发给xpres的信件将扩展为ford、carter、reagan、bush,并使用这些名字进行aliases处理,直到无匹配为止。然后将信件的副本分发给每一个。

  而oldlist这行,则象大家演示了如何从一个文件中读取一组名字,本例中就是让sendmail将发给oldlist的信息扩展为在/usr/local/oldguys文件里的用户列表。请记住它的格式,在文件绝对路径名前需加上“:include:”。

  nobody这一行,象大家演示了如何用文件名代替别名。发到这个邮件中的内容将添加到所指定的文件后面去。这里指定的是/dev/null。这样就是指发到nobody的信件将会被简单地丢弃。

  最后一行,ftphelp那一行象大家演示了如何用程序名代替别名。字符“|”使sendmail将这个邮件信息通过管道发给所指定的程序。

  aliases文件可能变得非常复杂。它能够用于解决许多特殊的问题。aliases文件的更多的内容将在第24章:别名中详述。

1.1.2 邮件队列目录

  在很多情况下,都可能使一个电子邮件临时无法发送,例如:远程主机已经down了,或临时出现了磁盘错误。为了确保邮件最终能够发送成功,sendmail将会把它们存到邮件队列目录中,直到发送成功为止。

  配置文件中的选项QueueDirectory用于指定sendmail的邮件队列目录:

O  QueueDirectory=/var/spool/mqueue


  这里指出的目录名必须是全路径名。

  如果你用足够的权限,看一看队列目录。如果没有邮件等待发送的话,它们可能是空的。如果它们不是空的,那么可能包括形如以下的文件:

dfQAA07038   dfMAA08000   qfQAA07038   qfMAA08000


  当一个邮件信息进入了邮件队列,将分成两个部分,每一个部分都保存在一个文件中。头信息存在一个文件名是以qf开头的文件中。邮件内容部分则存在一个文件名是以df开头的文件中。

  上例中,有两个邮件在邮件队列中。其中一个被标识为QAA07038,而另一个被标识为MAA08000。

  队列文件的格式与处理方法,我们将在第23章:邮件队列中详细说明。

1.2本地分发的角色

  sendmail的另外一个角色则是分发电子邮件信息给本地用户。一个本地用户在本地系统上有一个邮箱。分发本地邮件,就是将其附加到这个用户的邮箱中。

  通常,sendmail不是直接将邮件信息直接放到文件中去。在上一节中,我们看到,只有指定sendmail程序将邮件附加到一个文件中时,才这样做的。但这是一个例外,不是规则,sendmail调用其他程序执行分发。被调用的程序叫做分发代理。

  在你的sendmail.cf文件中,有两行用来定义本地分发代理,其中一个用于在本地系统中分发邮件:

Mlocal,P=/bin/mail, F=lsDFMAw5:/|@rm, s=10, R=20/40,
Mprog,P=/bin/sh, F=lsDFMeu, S=10, R=20/40, D=$z:/,

  程序/bin/mail用来将邮件附加到用户的邮箱中。程序/bin/sh用来运行其它程序来处理分发。

1.2.1 分发到邮箱

  配置文件中,以Mlocal开始的行定义了邮件如何附加到用户的邮箱文件中去。通常是使用/bin/mail程序,也可以使用deliver或mail.local程序。

  在UNIX系统中,用户的邮箱是一个单独文件,其中个邮件信息。通常UNIX系统约定(但不是唯一的可能)每一个在邮箱文件中邮件信息以一个五字节长的“From ”(4个字母、一个空格)开始,并以一个空行结束。

  Sendmail程序并不知道也不关心用户的邮箱文件是什么样的。而只关心将邮件添加到邮箱文件中的程序名称。例如:/bin/mail。以M开头的配置行定义了分发代理,详细的介绍可以参看第6章:Mail中枢和分发代理,以及第30章:分发代理。

1.2.2 借助程序分发

  在1.1小节中的aliases文件示例中的ftphelp行,是以字符“|”开始的程序名作为Mail的目的地:

ftphelp:  |/usr/local/bin/sendhelp


  在此情况下,发送到ftphelp中的mail,经过别名转换到|/usr/local/bin/sendhelp中。以字符“|”开始的目的地地址告诉sendmail启动这个程序,而非添加到一个文件中去。这主要是实现使用一个邮件程序对接收的邮件作一些有用的处理。

  Sendmail程序不直接运行邮件的分发程序。而是运行一个shell,并告诉shell运行这个程序。这个shell的名字在配置文件中以Mprog开始的行中定义:

Mprog,P=/bin/sh, F=lsDFMeu, S=10, R=20/40, D=$z:/,


  在这个例子是,指定的shell是/bin/sh。也可以使用/bin/ksh或smrsb。

1.3网络传输角色

  sendmail还有一个角色就是负责将邮件传送到另一台机器。当sendmail确定接收者不在本地系统中时,邮件将传送出去。下列是典型的配置文件中定义负责将邮件传送到其它机器的分发代理:


MsmtpP=[IPC], F=mDFMuX, S=11/31, R=21, E=
, L=990,
MuucpP=/usr/bin/uux, F=DFMhuUd, S=12, R=22/42, M=10000000,

  而在实际的配置文件中可能会有一些不同。上面例子中,smtp可以写为ether或ddn或其它的一些东西。而uucp可以写为suucp或uucp-dom。一个重要的知识点就是有一些分发代理处理本地分发,另外一些处理跨越网络的分发。

1.1.1 TCP/IP

  sendmail程序内在拥有在同一种网络中传输邮件的能力,那就是使用TCP/IP;下列的行就是用来指示sendmail去处理:


MsmtpP=[IPC], F=mDFMux, S=11/31, R=21, E=
, L=990

  其中[IPC]可以写作[TCP],它们是完全等价的。

  当sendmail程序在TCP/IP网络中传输mail时,首先发送“信封”上的发信人主机名到另一个站点。如果这个站点认可这个发信人的主机名是合法的,本地的sendmail程序将发送“信封”上的收信人列表。这个站点针对每一个收件人确定接受或拒绝。如果一些收件人被接受,则本地的sendmail程序发送出邮件信息(信头和信体)。

1.1.2 UUCP

  在配置文件中设置sendmail如何通过UUCP传输邮件的行如下所示:

MuucpP=/usr/bin/uux, F=DFMhuUd, S=12, R=22/42, M=10000000,

  这行告诉sendmail程序使用/bin/uux来通过UUCP网络传输邮件。

1.1.3 其它协议

  sendmail程序还可通过其它网络协议传输mail。你可以从我们前面做过grep操作的输出结果中发现它们,它们看上去象:

Mfax,  P=/usr/local/lib/fax/mailfax, F=DFMhu, S=14, R=24, M=100000,
Mmail11, P=/usr/etc/maill11, F=nsFx, S=15, R=25, A=mail11 $g $x $h $u
Mmac,P=/usr/bin/macmail, F=CDFMmpsu, R=16, S=16, A=macmail –t $u

  Mfax行定义了使用sendmail发送FAX的途径。FAX通过电话线传输文件的图形影象。在这个配置中,程序/usr/lib/fax/mailfax,将一个邮件文件的图形影象FAX出去。

  Mmail11行定义了使用mail11程序在DEC网络上传输邮件,一般应用于DEC系统。

  Mmac行定义了在Macintosh电脑系统的AppleTalk网络上传输邮件。

  在所有的这些例子中,sendmail通过专用的服务程序在网络上发送电子邮件。记住,sendmail本身只能直接在基于TCP/IP的网络上工作。

1.4 Daemon角色

  就象sendmail能够在基于TCP/IP的网络上传输电子邮件一样,它也能够接收来自于网络的电子邮件。为了实现这个,就必须运行在daemon(守候进程)模式。Daemon是一个运行在后台,不受终端约束的程序。

  作为一个daemon,sendmail通常在系统启动时就运行。当一个电子邮件发送到你的机器时,远程机器将与运行在你机器上的sendmail daemon“商谈”。

  想观察你的系统如何将sendmail运行在daemon模式下,你可执行以下任何一条命令:

% grep sendmail  /etc/rc* (BSD系统)
% grep sendmail  /etc/init.d/*  (SysV系统)
% grep sendmail  /etc/*rc (HP-UX系统)

  一个典型的输出是:

/etc/rc.local:if  [-f /usr/lib/sendmail –a –f /etc/sendmail.cf]; then
/etc/rc.local: /usr/lib/sendmail –bd –q1h; echo –n  ‘ sendmail’

  上面的第二行是sendmail在系统启动时运行的命令。

/usr/lib/sendmail –bd –q1h

  命令选项-bd是使sendmail运行在daemon模式下。命令选项-q1h是让sendmail每小时唤醒一次,处理队列。

[目录]


如何运行sendmail

2.如何运行sendmail

  一种运行sendmail的方法是,直接在命令行上附加接收者参数。例如:下面命令就是发一个邮件消息给george。

% /usr/lib/sendmail george

  你也可以同时给出多个接收者。例如,发一个邮件给george,truman和teddy:

% /usr/lib/sendmail george,truman,teddy

  sendmail程序可以接受两种不同的命令行参数。不以“-”字符开始的参数是接收者,如上面的george。而以“-”字符开始的参数则是影响sendmail运行的开发选项。所有的命令行开关选项的解释,

  标记   说明
    -b   设置运行模式
    -v   运行于冗长模式
    -d   运行于调试模式


2.1适宜的模式(-b)

  sendmail程序的“-b”参数可以衍生出许多功能。例如:使sendmail显示队列的内容,使sendmail重建别名数据库。本章将介绍一些比较常用到的子参数。

  格式   说明
    -ba   使用ARPAnet(灰皮书书)协议
    -bD   以守候进程模式运行,但不fork
    -bd   以守候进程模式运行
    -bH   清除固有的主机状态
    -bh   打印固有的主机状态
    -bi   重建别名数据库
    -bm   成为一个邮件发送者
    -bp   打印邮件队列内容
    -bs   在标准输入处运行SMTP
    -bt   测试模式:仅解析地址
    -bv   检验:不收集、分发
    -bz   冻结配置文件

  选项可以使sendmail执行时象其它名字。每个名字可以是一个硬连接,一个符号连接,或sendmail的一份拷贝。

hoststat -bh 打印固有主机状态
mailq -bp 显示邮件队列内容
newaliases -bi 重建别名数据库
purgestat -bH 清除固有主机状态
smtpd -bd 以守侯进程运行

2.1.1 守候进程模式 (-bd)

  sendmail程序能够在后台以守侯进程模式运行,监听来自其它机器的邮件。Sendmail当第一次以守侯进程模式运行时,程序只读取配置文件一次,然后就一直运行,不再读取配置文件了。也就是说,执行后将不会发现配置文件的变化。

  当你对配置文件sendmail.cf做了任何修改,都需要kill掉sendmail进程,然后重新启动它。但当你kill掉这个守候进程前,必须知道如何正确地重新启动它。这些信息是/etc/sendmail/pid或一个系统rc文件。

  在BSD系列的UNIX系统,守候进程通常使用以下命令启动:

/usr/lib/sendmail –bd –q1h

  命令行开关选项“-bd”指定sendmail以守候进程方式运行。“-q”选项告诉sendmail多久去查看一次待处理邮件队列。“-q1h”就是将其设置为1小时。

  在你的机器上启动sendmail的命令,可能与我们这儿给出的是不同的。如果你管理许多不同的UNIX系统,你无须知道每一种是如何运行的。

2.1.1.1 kill并重启sendmail v8.7

  在sendmail 8.7以后的版本中,kill并重启sendmail变得比较简单。一个单一的命令将会完成这一工作:

% kill –HUP `head –1 /etc/sendmail.pid`


  这个单一的命令与下一个小节中的两个命令的效果完全相同。

2.1.1.2 kill并重启sendmail v8.6

  当你要以守候进程方式启动sendmail,你须确认没有一个已运行的sendmail守候进程。在8.6版sendmail中,可以在/etc/sendmail.pid文件的第一行中找到进程ID号pid。你可以执行以下命令来kill掉sendmail:

% kill `head –1 /etc/sendmail.pid`

  当你kill掉当前运行的守候进程,你可以使用以下命令来重新运行sendmail:

% `tail –1 /etc/sendmail.pid`

2.1.1.3 kill并重启老版本sendmail

  在老版本的sendmail中,你必须使用ps来获得sendmail的进程ID号pid。在BSD UNIX和System V UNIX使用ps的方法不尽相同。

  针对BSD UNIX来说,ps命令的将得到类似以下的输出:

% ps ax | grep sendmail | grep –v grep
   99  ?  IN  0:07  /usr/lib/sendmail -bd –q1h
% kill 99

  这个输出的最左边的就是进程ID号。

  针对System V UNIX系统来说,ps命令的参数及命令输出都不尽相同:


% ps ae | grep sendmail
   99  ?0:01 sendmail
% kill 99

  在老版本的sendmail中,你必须通过查看rc文件来获知如何重启sendmail。

2.1.1.4 如果你忘记了kill守候进程

  如果你在重启sendmail时,忘了kill掉原来的进程的话,你就会看到类似以下列出的错误消息,每5秒显示一次。

Getrequests: cannot bind: Addredd already in use
Getrequests: cannot bind: Addredd already in use
Getrequests: cannot bind: Addredd already in use
Getrequests: cannot bind: Addredd already in use
Getrequests: cannot bind: Addredd already in use
Getrequests: cannot bind: Addredd already in use
Getrequests: cannot bind: Addredd already in use
Getrequests: cannot bind: Addredd already in use
Getrequests: cannot bind: Addredd already in use
Getrequests: cannot bind: Addredd already in use
Getrequests: cannot bind: Addredd already in use
Opendaemonsocket: Server SMTP socket wedged:exiting

2.1.2 显示邮件队列模式 (-bp)

  sendmail程序能够显示邮件队列的内容。可以通过两种方法实现:一种是运行mailq,另一种是运行带开关选项“-bp”的sendmail。无论你使用哪种方法,邮件队列将会显示出来。如果这个队列是空的,sendmail将打印出:

Mail queue is empty

  如果有一个邮件正在等待队列中,那么将输出更多的消息,其中包含类似于下列的信息:

)   Mail Queue (1 requests)
--Q-ID---   --Size--   ----Q-Time
-------------------Sender/Recipient-------------------
GAA29775*   702   Thu Mar 12 16:51<[email protected]>
   Deferred:Host fbi.dc.gov is down
  <[email protected]>

  在此,带开关选项“-bp”的sendmail显示了仅有一个邮件信息在队列中。如果有多个,那么每一个邮件都会像这样列出来。每一个至少有两行输出。

  第一行显示邮件和发送者的细节信息。GAA29775是这个邮件在队列中的标记。“*”号则代表由于这个邮件正在被处理,所以已被锁定。“702”是邮件体的字节数。这里的时间则是邮件被放入队列的时间。地址则显示了发送者的名字。

  第二行则可能显示出错的原因,这个邮件就是因为暂时无法分发,所以才暂存在队列中的。

  第三行则可能显示接收者地址。

  如果你想更完整、清楚地了解这里的输出,参见第23章:队列。

2.1.3 重建别名库模式 (-bi)

  由于sendmail有可能需要在存放上千条别名记录的aliases文件中寻找别名,为了提高效率,可以使用dbm或db格式来存储。使用这种数据库格式来存储将大大提高检索速度。

  尽管sendmail可以在aliases文件改变时自动更新数据库,但它并不总能及时完成。你可以通过运行newaliases命令或带“-bi”开关选项的sendmail来完成,以下这两个命令是相同的:

% newaliases
% /usr/lib/sendmail -bi

  稍过一会儿,将显示出统计信息:

/etc/aliases: 859 aliases, longest 615 bytes, 28096 bytes total


  这一行表示数据库已成功重建。从8.6版以后,就可以存在多个别名文件,所以每一行都是以别名文件名开始的。然后是显示处理了的别名,最大的一项的长度,总长度, 同时有多少个出错也会显示出来。

  关于aliases文件更详细的信息,参见第24章:别名。

2.1.4 校验模式 (-bv)

  带开关选项“-bv”的sendmail是一个简单方便的检查别名的工具。它能够在别名中递归地查找,并显示出最终的用户名称。

  假设aliases文件中有如下别名设置:

animals:  farmanimals, wildanimals
bill-eats:  redmeat
birds:  farmbirds, wildbirds
bob-eats:  seafood,whitemeat
farmanimals:  pig, cow
farmbirds:  chicken, turkey
fish:  cod, tuna
redmeat:  animals
seafood:  fish,shellfish
shellfish:  crab, lobster
ted-eats:  bob-eats, bill-eats
whitemeat:  birds
wildanimals:  deer, boar
wildbirds:  quail

  虽然你也可以通过演算得知ted-eats最终的用户名称,但远不如使用sendmail来帮你完成那样方便。使用sendmail也将更加准确,而且对于很大很复杂的aliases文件来说,更加显得重要。

  另外,sendmail –bv还有一个附加的功能,那就是可以检验别名是否真的可分发。假设在aliases文件中包含以下一行:

root:  fred, larry

  假定fred是一个拥有本地机器帐户的系统管理员,但用户larry已经离开,帐号已经被删除。你可以运行sendmail –bv检查所有的用户是否有效:

% /usr/lib/sendmail –bv root

  这个命令将在aliases文件检查root用户,由于larry不存在,输出将会如下所示:

larry … User unknow
fred … deliverable: mailer local, user fred

2.2冗长模式(-v)

  命令行开关选项“-v”,将使sendmail运行在冗长(verbose)模式下。在这个模式下,sendmail将会打印出转发邮件的每一步的详细说明。为了观察运行在冗长模式下sendmail的运行情况,可以执行:

% /usr/lib/sendmail –v you < sendstuff

  邮件传送在本地进行,输出如下所示:

you …  Connecting to loca…
you …  Sent

  当sendmail通过TCP/IP网络传送邮件到其它机器,它将使用一个叫SMTP(简单邮件传输协议)。为了观察使用SMTP的情况,我们再次运行sendmail程序,但这次,我们使用一个不在本地的E-mail地址代替“you”:

% /usr/lib/sendmail –v [email protected] < sendstuff

  这个命令的输出看起来类似:

[email protected] … Connecting to remote.domain via smtp …
220-remote.Domain Sendmail 8.6.12/8.5 ready at
Fri, 13 Dec 1996 06:36:12 –0800
220 ESMTP spoken here
>>> EHLO here.us.edu
250-remote.domain Hello here.us.edu,pleased to meet you
250-EXPN
250-SIZE
250 HELP
>>> MAIL From:<[email protected]>
250 <[email protected]> … Sender ok
>>> RCPT To:<[email protected]>
250 <[email protected]> … Recipient ok
>>> DATA
354 Enter mail, end with “.” on a line by itself
>>> .
250 GAA20115 Message accepted for delivery
[email protected] … Sent (GAA20115 Message accepted for delivery)
Closing connection to remote.domain
>>> QUIT
221 remote.domain closing connection

  以数字开头的行和以字符串“<<<”开头的行组成了SMTP的会话过程。我们马上说谈论一下它们。其它行显示本地sendmail尝试做的操作和成功完成的操作:

[email protected] … Connecting to remote.domain via smtp …

[email protected] … Sent (GAA20115 Message accepted for delivery)
Closing connection to remote.domain

  第一行显示使用网络发送信件到远程主机remote.domain上。最后两行显示邮件已经发送成功。

  在SMTP会话中,以“<<<”开始的行显示本地机器对远程机器的交谈。而来自远程机器的应答行则以数字开始的行。现在我们来看一下会话过程。


220-remote.Domain Sendmail 8.6.12/8.5 ready at
Fri, 13 Dec 1996 06:36:12 –0800
220 ESMTP spoken here

  一旦sendmail与远程机器连接上后,sendmail就等待远程机器初始化会话。远程机器说它准备好发送,详细的主机名。如果远程主机也运行了sendmail,也将说sendmail的名字与版本。还有就是日期与时间。

  第二行也以220开始,“ESMTP spoken here”的含义是远程站点能够使用扩展的SMTP协议。如果远程机器跑的sendmail是8.7或以上版本,ESMTP将可能会出现在第一行。

  如果sendmail等待接收这个初始化信息太久,就会打印“Connection timed out”信息,并将这个邮件放入邮件队列中。

  接着,本地sendmail发送EHLO(以>>>开始),传送扩展的HELLO信息,和本地主机名:

>>> EHLO here.us.edu
250-remote.domain Hello here.us.edu,pleased to meet you
250-EXPN
250-SIZE
250 HELP

  在EHLO中的E说明本地sendmail也是使用ESMTP的。远程主机以250开始的回执ESMTP支持的服务列表。

  如果本地机器发送EHLO消息时,传送的是短主机名(如here)就可能会遇到一个问题。远程主机无法得知这个短主机名的位置,因为它不在远程主机的域remote.domain中。这也就是为什么sendmail一直使用完整的主机名来表示。一个完整的主机名是由主机名加上一个点,然后再加上DNS域名项。

  如果到现在为止,一切正常的话,本地机器将说明邮件的发件人:

>>> MAIL From:<[email protected]>
250 <[email protected]> … Sender ok

  在此,发件人的地址是远程机器认可的。

  下一步,本地机器将说明收件人的名字:

>>> RCPT To:<[email protected]>
250 <[email protected]> … Recipient ok

  如果远程主机上并无用户you的话,远程主机将返回“User unknown”错误,在此,收件人OK。注意这里的OK不一定能确保地址是完好的。只是确认了这个地址是可接受的。

  当信封信息发送完成后,sendmail程序将试图发送信件信息(包括信头和信体)。

>>> DATA
354 Enter mail, end with “.” on a line by itself
>>> .

  DATA告诉远程主机准备好了。当远程主机指示发送信息时,本地主机照做。最后的一个点用来标记一个邮件结束。这是SMTP的规定。因为邮件消息可能包含多行,而用一个小点开始也是合法,所以sendmail将会把这些小点转换成两个再发送出去。例如,假定当我们要发送以下文件时:

My results matched yours at first:
126.71
125.72

126.79
But then the numbers suddenly jumped high, looking like
Noise saturated the line.

为了防止以小点开始的行照成疑义,sendmail将会在以小点开始的行,插入一个附加的小点,所以实际传输的内容如下所示:
My results matched yours at first:
126.71
125.72
.…
126.79
But then the numbers suddenly jumped high, looking like
Noise saturated the line.

  而另一方收到邮件后,再将这个附加的小点去掉,还原成原来的邮件内容。

  远程主机上的sendmail将显示队列管理附于的标识:

250 GAA20115 Message accepted for delivery
>>> QUIT
221 remote.domain closing connection

  本地主机上的sendmail发送QUIT,说明全部工作完成。远程主机返回应答信息确认。

  注意,-v参数在发送信息到远程主机上时十分有用。它能够显示出SMTP会话过程,以帮助我们了解邮件转发的过程,也有利于我们排错。

2.3调试模式(-d)

  sendmail程序也能产生并输出调试信息。要使用调试模式运行sendmail的话,就需要使用-d参数。这个参数将产生比-v参数更多、更详细的信息。输入以下命令行,用自己的帐户名代替you:

% /usr/lib/sendmail –d  you  < /dev/null


  这个命令行产生很冗长的处理信息。我们在此不打算说明这些输出信息,在此,只需记住在sendmail程序在调试模式下运行会产生大量的信息。

  同时,也会产生大量的调试信息,你可以修改、显示这些调试信息。你可以在-d参数后加上一个数字,输出将会限制在只输出指定类的调试信息。

  输入以下命令用自己的帐户名代替you:

% /usr/lib/sendmail –d40  you  < /dev/null

  在此,-d40是调试第40类的信息。这类的信息是关于邮件队列的。以下是一个输出实例:

>>>>> queueing GAA14008 (new id) queueall=1 >>>>>
queueing 95688=you:
mailer 4 (local), host ‘’
user ‘you’, ruser ‘<null>’
next=0, alias 95460, uid 0, gid 0
flags=6008<QPRIMARY,QPINGONFAILURE,QPINGONDELAY>
owner=(none), home=”/home/you”, fullname=”Your FullName”
orcpt=”(none)”, statmta=(none), rstatus=(none)
<<<<< done queueing GAA14008 <<<<<

  对于一个类,还可以指定一个级别,这个级别是用来调整输出的量。一个低级别将产生较少的输出,一个高级别将产生更多、更复杂的输出。它们的格式是在-d参数之后加上:

category.level

  例如:

% /usr/lib/sendmail –d0.1 -bp


  参数-d0指示sendmail产生通用的调试信息。而级别1则让sendmail的输出减少到最小限度。这个值可以省略,因为.1是缺省值。参数-bp让sendmail打印出邮件队列内容。输出看起来如下所示:

Version 8.8.4
Compiled with:LOG  NAMED_BIND  NDBM  NETINET  NETUNIX  NIS  SCANF
  XDEBUG
= = = = = = = = = = = SYSTEM IDENTITY (after readcf) = = = = = = = = = = =
(short domain name) $w = here
(canonical domain name) $j =here.us.edu
(subdomain name) $m =us.edu
(node name) $k = here
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
Mail queue is empty

  在此,开关选项“-d0.1”让sendmail打印出版本号、一些关于编译的信息,以及你的主机名。现在你提高级别看一下:

% /usr/lib/sendmail –d0.11 -bp

  这时将显示如下信息:

Version 8.8.4
Compiled with:LOG  NAMED_BIND  NDBM  NETINET  NETUNIX  NIS  SCANF
XDEBUG
OS Defines: HASFLOCK  HASGETUSERSHELL  HASINITGROUPS  HASLSTAT
HASSETREUID  HASSETSID  HASSETVBUF  HASUNAME  IDENTPROTO
  IP_SRCROUTE
Config file: /etc/sendmail.cf
Pid file: /etc/sendmail.pid
Canonical name: here.us.edu
UUCP nodename: here
a.k.a.: [123.45.67.89]
= = = = = = = = = = = SYSTEM IDENTITY (after readcf) = = = = = = = = = = =
(short domain name) $w = here
(canonical domain name) $j =here.us.edu
(subdomain name) $m =us.edu
(node name) $k = here
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
Mail queue is empty

[目录]


sendmail.cf

3. sendmail.cf文件

  在sendmail的配置文件sendmail.cf中的文本,有的形如MODEM的噪声,有的形如Dithers的咒语一样:

R$+@$=w       ?  sendmail.cf文件
{$/{{.+          ?  modem的噪声
!@#!@@!   ?  Dithers的咒语
对于生手而言,要构建象下面的配置文件,一定是一件恐怖的事情:
R$+@$=W   $@$1@$H user@thishost -< user@hub
R$=W!$+$@$2@$H   thishost!user -< user@hub
R@$=W:$+  $@@$H:$2   @thishost:something
R$+%$=W   $@$<#$1@$2   user%thishost

  不过,如果回想起以前学习C语言时,你是否会对这个表达式感到恐惧呢?

# define getc(p)(--(p) -<_cnt<=0? ((int)*(p)-<ptr++):_filbuf(p))

  就象任何一个新语言一样,学习sendmail.cf文件中所使用的语言需要时间和实践。在本章中,我们将介绍这个语言,不会在此只是对其作一个入门性的概述。

3.1概要

  sendmail.cf文件是sendmail每次启动时要读取的配置文件。它包含了sendmail启动时必须的信息。它列出了所有重要文件的位置,指定了这些文件的缺省权限。包含了一些影响sendmail行为的选项。更重要的是,它还包含了地址重写(rewriting addresses)规则。

  Sendmail.cf文件是按行组织的。一个配置命令行,均是由字符开头的,而且每行只有一个命令:

V7 ? 正确的
V7 ? 不正确,前面多了个空格
V7 Fw/etc/mxhosts ? 不正确,一行中有两个命令
Fw/etc/mxhosts ? 正确

  每一个配置命令行,是由命令加上特定参数构成的。例如,命令V的参数是一个数字,而F命令的参数则是字母w,再加上绝对路径。

命令   说明

V     定义配置文件的版本(从8.6版开始才要求)
M     定义一个邮件传送代理
D     定义一个宏
R     定义一个地址重写规则
S     声明一个规则集
C     定义一宏集
F     从一个文件与管道中定义一宏集
O     定义一个选项
H     定义一个信头
P     定义传送优先级
T     声明受托用户(在8.1版忽略,8.7版重用)
K     声明一个key字数据库(从8.1版开始使用)
E     定义一个环境变量(从8.7版开始使用)
L     包括扩展的负载均衡支持

  有一些配置命令,象V只会在sendmail.cf文件中出现一次,而有些象R命令就会多次在配置文件sendmail.cf中出现。

  空行、以“#”开始的行将被处理为注释行而被忽略。一个以tab开始的行,则说明是上一行的继续,如:

# a commnet ? 注释行
V7
/Brekeley ? V7命令的继续
-
tab

  除了一个命令,一个空行,一个空格,一个tab,或一个#字符以外,其它情况都是错误的。如果sendmail程序发现这种情况,将打印出如下警告,并忽略此行,然后接着读取后面的配置:

sendmail.cf : line 15:unknown control line “v6”

  在这,sendmail找到了一行以v开始的行。由于小写的v不是一个有效的命令,sendmail将发出警告。而行号则指出了这个错误所在行。

  下面几个小节将对每一种命令进行简单的实例说明。而这些命令的作用将会在本教程中阐述。所以如果在本节中无法完全理解的话,也不必担心。因为这里所有神秘的东西,在本书结束时都将变得十分清晰明白。

3.2最小配置

  最小的配置的文件可以是空文件。你可以使用以下命令来创建这个文件:

% cp /dev/null  client.cf


  我们会慢慢地往这个文件中添加配置。将其命名为client.cf是为了避免覆盖了系统中的sendmail.cf文件。

  现在,我们再运行sendmail,测试这个新配置文件的有效性:

% ./sendmail –Cclient.cf –bt >/dev/null
%

  命令行开关选项-C用于指定sendmail使用一个指定的配置文件。而开关选项-bt则告诉sendmail运行在rule-testing模式下。注意,sendmail读取你的空配置文件,运行,没有任何提示。同时注意当在第2章编译完后无法运行sendmail,但现在你可以了。那是因为当时你没有配置文件,而现在有一个了(尽管这个文件是空的)。

3.2.1 版本

  为了防止旧版本的sendmail因读取新版本的配置文件而破坏,在sendmail 8.1开始引入了一个V命令,这个命令的格式如下:

V7

  编辑文件client.cf,然后加上这一行。“V”必须位于行首。后面跟的版本号必须是7,才能够使所有8.8版的sendmail.cf中的新功能生效。数字7是sendmail.cf的语法,表示有7个主要的变化。

3.2.2 注释

  注释语句能够帮助其他人理解你的配置文件。同时,他们也能通过注释记起你几个月前的修改。注释语句对sendmail的执行速度影响极小,所以你无需担心这一问题。前面我们说过,以#开始的行,被sendmail认为是注释行,整行将被忽略。例如:

# This is a comment

  另外,注释语句也可以在命令之后,如:

V7#This is another comment

  增加一些注释语句到你的sendmail.cf文件中,使其成为:

# This is a comment
V7# This is another comment
Sendmail程序读取这个配置文件也将没有任何提示。
% ./sendmail –Cclient.cf –bt >/dev/null
%

3.3快速导览

  你将发现,在配置文件中的其它命令远比V命令来得复杂。这儿,我们现在对每个命令作一个快速的导览,仅够你能够对它们有一个初步的了解。

3.3.1 邮件传送代理

  通常情况下,sendmail程序不自己传送邮件,而是调用一个程序来完成。M命令就是定义一个邮件传送代理。例如,就象以前我们看到过的:

Mlocal,  P=/bin/mail, F=lsDFMAw5:/|@rmn, S=10, R=20/40,

  这将告诉sendmail,本地的邮件使用/bin/mail传送。这一行中的参数将在第6章:邮件中枢与传送代理,第30章:传送代理中详细说明。

3.3.2 宏

  义一个值,然后可以在多次使用,提高sendmail.cf的可维护性。D命令用于定义宏。一个宏名可以是一个单字母或用大括号包起来的多个字符。定义一次后,就可以在其它地方使用。

DRmail.us.edu  ? 一个单字符
D{REMOTE}mail.us.edu   ? 用大括号包含起来的多个字符(从8.7版开始)

  在这,R和{REMOTE}都是宏名,值是mail.us.edu。这个值可以在任何地方用$R和${REMOTE}访问。宏将在第7章:宏中介绍,更详细的在第31章:定义宏中。

3.3.3 规则

  sendmail.cf文件的核心是一系列的地址重写规则。这是非常重要的,因为地址必须符合多种标准。R命令用来定义这些规则:

R$-   $@ $1 @ $R   user -< user @ remote


  邮件地址将与最左边的规则($-)进行比较。如果与这个规则匹配,它们将根据右边的规则($@ $1 @ $R)进行重写。而在最右边的文本则是注释(注意,这里不需要使用#号)。

  如果使用多字符的宏名,并用#号标出注释的话,将使语句减少一些神秘:


R$- # If a plain user name
$@ $1 @ ${REMOTE}  # append “@” remote host

3.3.4 规则集(Rule Sets)

  由于地址重写可能需要好几步,规则可以组织成为规则集,用S命令开始规则集:

  S3该命令定义规则集3,从sendmail 8.7版开始,规则集也可以使用字符来命令,如:

SHubset


  该命令定义Hubset规则集,这种方式的命名,sendmail将会自己对其编号。

  所有的跟在S命令后的R命令(规则)组成规则集。一个规则集结束于定义另一个规则集的S命令。

3.3.5宏集(Class Macros)

  用D命令定义的宏只能有一个值,但这通常是不够的。我们经常需要定义一个拥有多个值的宏,然后就像数组一样来组织这些值。C命令定义一个宏集。一个宏集就像一个数组一样,能够有多个项。宏集的名字是一个单字母,从8.7版开始,也可以用大括号包含多个字符作为名字,例如:

CW  localhost  fontserver ? 一个单字符作为名字—W
C{MY_NAMES}  localhost  fontserver  ? 多个字符作名字---{MY_NAMES}

  在这里,每一个宏集都包含两个值:localhost和fontserver。我们可以通过表达式$=W和$={MY_NAMES}来访问这些宏集。有关于宏集的更多信息,可以参考第12章和第32章。

3.3.6 文件宏集(File Class Macros)

  为了管理更加容易,我们常将比较长的信息或经常变化的信息存放到一个文件中去。命令F可以定义一个文件宏集。这个宏集的值是这个文件的内容,如:

FW/etc/mynames
F{MY_NAMES}/etc/mynames

  在此,文件宏集W和{MY_NAMES}将从文件/etcmynames中获得它们的值。

  文件宏集也可以从一个程序的输出中得值。它定义为:

FM|/bin/shownames
F{MY_NAMES}|/bin/shownames

  在此,sendmail将运行程序/bin/shownames,这个程序的输出将成为文件宏集的值。

3.3.7 选项

  选项将告诉sendmail程序许多有用的和必要的事情。它们指定key文件的位置,设置超时时间,以及定义sendmail在出错时如何处理。它可以调整sendmail,以使它符合你特定的需要。

  命令O用来设置这些选项。以下就是一个例子:

OQ/var/spool/mqueue
O QueueDirectory=/var/spool/mqueue

  在此,Q选项定义邮件队列文件为/var/spool/mqueue。

3.3.8 信头

  邮件消息由两个部分组成:一个是信头部分,另一个是在空行后的主体部分。主体部分可以包括任何内容。而信头部分,则需要严格按照标准。H命令用来指定信头的格式:

Hreceived:$?sfrom $s $.by Sj ($v/$Z) $?r with $r$. Id $I$?u for $u$.; $b


  这个特定的H命令告诉sendmail收到后,必须在每一封信的信头加上这一行。

3.3.9 优先级

  并不是所有的邮件具有相同的优先级。邮件列表的信应该在只有一个收件人的信的后面发送。P命令用于设定邮件的优先级。这个优先级用于邮件队列处理时决定邮件的顺序。

Pjuck=-100

  这个P命令告诉sendmail,信头中用juck的信最后处理。关于这个命令的更详细内容,参见第14章和第35章。

3.3.10 受托用户

  为了使某些软件(如UUCP)能够正确地生效,就必须能够告诉sendmail邮件是谁发来的。这要求软件与From:中指出的用户运行在不同的uid上。T命令列出这些用户的受托用户。所有包含在信头的其他用户将收到一个警告。

Troot daemon uucp

  这个T命令指出有三个用户是受托用户。它们是root(UNIX系统中的上帝),daemon(sendmail通常以伪用户daemon身份运行),以及uucp。

3.3.11 key数据库

  一些认证信息,例如UUCP主机列表等,最好在sendmail.cf外维护。外部的数据库(叫作keyed数据库)提供了更快的访问速度。Key数据库在8.6版中引入了几种格式,如:

Kuucp hash /etc/mail/uucphosts

  这个K命令声明一个key数据库叫uucp,类型是hash,文件为/etc/mail/uucphost。

3.3.12 环境变量

  sendmail程序的安全是十分重要的。一种破坏的的方法是运行时伪造一些环境变量。为了阻止它,8.0版的sendmail将在启动时去除所有的环境变量。然后根据预先设置的值进行设置。设置环境变量使用E命令。

EPOSTGRESHOME=/home/postgres

  在此,环境变量POSTGRESHOME被赋予值/home/postgres。这个程序使用postgres数据库访问信息。

[目录]


邮件中枢与分发代理

6. 邮件中枢与分发代理

  使用一个强大的中心机器处理所有的邮件,比让网络中所有的工作站自己处理自己的邮件好得多。这样的一个中心机器就叫作邮件中枢(Mail hub),它就象美国联邦快运公司处理包裹一样工作。在过去,当你通过联邦快运公司将包裹从旧金山寄到巴黎,包裹将首先被送到孟菲斯的田纳西,甚至你想寄到洛杉机,它也是先送到孟菲斯(参见图6-1)。这是因为孟菲斯是联邦快运公司的处理中心。所有的包,不管它们从哪来,到哪去,都先送到处理中心(中枢,hub),然后再从那里发送出来。

  这种处理方法的好处就是,联邦外运公司只有处理中心需要知道如何发往全世界(当然,联邦快运是有多个处理中心的),而其它地方的分支机构只要知道如何发往处理中心就可以了。

  类似地,你的工作站也可以将自己看成分支机构(客户机),将处理邮件的中心机器看成处理中心。这个邮件中枢将为整个机构处理邮件,这样做有许多好处:

  1) 所有发往这个机构的信都发给邮件中枢,而不直接发往客户机。由邮件中枢来处理有几个优点:客户机无需运行sendmail守候进程来侦听邮件。而且客户机的名字无需告知外部,因此更容易安全地将客户机与Internet隔离。

  2) 所有从机构往外发的邮件都先发给邮件中枢,然后由邮件中枢转发到最终目的地,而不是由客户机直接向外发送。这个机制可以使客户机无须一直观察Internet的变化;所有的待发邮件都保存在邮件中枢中,而不是客户机处,这样管理起来更简单;而且一个单一的sendmail.cf文件就可为所有的客户机服务。

  3) 所有发出去的邮件都将被修改,使得其看起来象来自邮件中枢的邮件。这样回复的邮件就不会直接返回到每一个客户机。这样做,所有的邮件好象都来自一台机器,一台很大的机器。

  4) 所有发往本地用户的邮件都将传送到邮件中枢,存放在spool中。而无需每台机器都建一个spool目录来存放收到的邮件。这样做的优点是所有本地的邮件都由一个机器处理,所有本地机器都无需进行处理。而且这样管理spool也更加容易。

  但如果你的机构的电脑网络是不同体系结构的电脑组成的,或散在许多网络中,使用邮件中枢就不是太好了:

  1) 如果一个机构经常有数量巨大的邮件收发,那么这台邮件中枢就可能负载过大,以使所有的邮件都被缓存,一直等待发送。如果负载厉害的话,甚至可能使本地邮件转发也会出现这样的现象,那是十分可怕的。

  2) 为了让邮件中枢正常工作,它需要知道系统中所有用户的登录名,也就是意味着需要一个主/etc/passwd文件,它是所有主机上的/etc/passwd的集合,或者使用NIS系统来构建。

  3) 因为所有的邮件都通过邮件中枢传送,而不是直接送给接收者,这就意味着一定会延迟。对于一些小的机构,这个延迟可以忽略不计,而让邮件的总量上去时,这会成为一个严重的问题。

  4) 如果一个客户机是直接使用UUCP连接,或是与多个网络连接,那样配置文件会变得十分复杂。

4.1 client.cf文件

  这个教程的目的是产生一个简单的邮件中枢的sendmail.cf文件。为了区别与前面几个章节中的sendmail.cf文件区分开来,在此我们把它称为client.cf文件。我们将在后面的几个小节中逐段地生成这个文件,并做一些相应的说明。本章中生成的文件将完成两个任务:

  1) 它将指示sendmail运行为邮件中枢,发送所有给其它机器的mail服务器。

  2) 它将使所有外出的邮件表现为从邮件中枢发出的一样。

  在这章中,我们主要处理第一个任务:如何让客户机上的邮件送到邮件中枢。

4.2 定义一个邮件分发代理

  sendmail除了在TCP/IP网络转发邮件外,并不能自己处理邮件传送。而是调用一个程序来实现。这个程序就叫做分发代理。哪个代理程序处理哪类邮件分发任务在sendmail.cf文件中定义。

  加入你的client.cf文件中的第一项是定义负责将信件转发给邮件中枢的转发代理程序。最初,sendmail只需要转发代理程序的名字与路径。设置形如:


# This is a comment
V7  # this is another comment
# Delivery agent definition to forward mail to hub
Mhub, P=[IPC], A=IPC $h

  这是一个最小的配置文件。将这些行加入到client.cf文件中。配置的第二行定义了一个分发代理。它包含三个部分,每个部分使用逗号隔开。

Mhub

  以M开始的行用于定义邮件分发代理。紧跟着在后面的hub是分发代理的标识名。

  这个标识名供你以后写的配置文件部分参考。

P=[IPC]

  P=用于设置路径。这一句配置用来指出处理邮件分发的程序的全路径名,在这里使用的是[IPC]。([IPC]是一个Sendmail内部使用的专有名称,说明它有能力在TCP/IP网络上完成邮件分发工作。)而对于其它情况下,则设置为其转发程序的名称,如/bin/mail等。

A=IPC $h

  A=后面跟着参数列表,用来指定P=指定的程序运行时的参数。第0个参数就是程序的名称(在此就是IPC,注意没有包含括号)。而其它的参数就是在A=后面指定的,这此例中仅有一个:$h。这个$h是一个宏,表示接收者的主机名。我们会在第7章中更详细地说明宏。它通常是关于分发代理定义的最后一项。

4.2.1 测试client.cf文件

  在命令行下执行:

% /usr/lib/sendmail –oQ/tmp –Cclient.cf –bp

  注意,你需要在命令行中加上-oQ/tmp,它将阻止sendmail改变mqueue目录。如果你忽略这个开关,sendmail会出现混乱、错误:

cannot chdir((null)):Bad file number

  而-Cclient.cf开关则告诉sendmail使用当前目录中的client.cf文件,而不是系统中的/etc/sendmail.cf文件。而-bp开关则是让sendmail打印出队列的内容。以上命令行将产生如下所示的输出:

No local mailer defined

  然而,缺少一个local mailer并没有什么大不了的。Sendmail程序在输出它后,就结束了,说明没有发现其它错误。

4.3 本地分发代理

  当你运行了sendmail,它曾抱怨没发现本地分发代理定义。为了让它更快乐,我们就将下面的定义加入到client.cf文件中去。你可以使用以下命令,从/etc/sendmail.cf中抄过来:

% grep “^Mlocal” /etc/sendmail.cf << client.cf

  注意,^M是真实的两个字符:^和M,而不是CTRL-M。现在,

  我们启动编辑器,然后调入client.cf文件,你将看到类似下面的内容:

# This is a comment
V7  # this is another comment
# Delivery agent definition to forward mail to hub
Mhub, P=[IPC], A=IPC $h
Mlocal, P=/bin/mail, F=lsDFMAw5:/|@rmn, S=10, R=20/40, A=mail –d $u

  你将注意到有三个新的等式,它比我们原来的hub等式复杂。这个新的M配置命令声明一个符号名,就象hub一样。在这里,我们用的名字是local。尽管,local定义对于一个真正在运行的sendmail.cf配置来说是十分重要的,而在此呢,我们仅仅是为了让sendmail不抱怨。

  这个新的分发代理定义是由六个部分组成的(每个部分使用逗号隔开),一个符号名和五个等号。其中F=、S=和R=是新出现的。而关于M,P=和A=都在介绍hub那一节中介绍过了。

  M 所有的邮件分发代理的定义都是从M开始的,如:

Mhub,  P=[IPC],A=IPC $h
Mlocal, P=/bin/mail, F=lsDFMAw5:/|@rmn, S=10, R=20/40, A=mail –d $u

  分发代理的符号名就紧跟在M命令之后,中间没有空格。在以上的例子中,符号名是hub和local。名叫hub的分发代理用于将邮件转发给邮件中枢。而local分发代理则将邮件分发给本机的用户。

  P=在等号后面指出邮件分发程序的全路径名:

Mhub,  P=[IPC],A=IPC $h
Mlocal, P=/bin/mail,  F=lsDFMAw5:/|@rmn, S=10, R=20/40, A=mail –d $u

  你的程序名可能与这里不同,但通常,local邮件分发代理都是将邮件放入用户的邮件spool文件中。

  A= 在等号后面指定这个邮件分发代理运行时的参数。

Mhub,  P=[IPC],A=IPC $h
Mlocal, P=/bin/mail,  F=lsDFMAw5:/|@rmn, S=10, R=20/40, A=mail –d $u

  注意,在local中使用了$u宏,而hub则使用了$h宏。$u宏包含接收者的名字(如bob),而$h宏则是包含接收主机的名字(如here.us.edu)。宏将在下一章说明。习惯中,这个部分一般放在最后。

  以下三个部分是在local定义中新出现的,在hub的定义中并未使用。

  F= 在等号后面指定确定的标记,用来告诉sendmail更多关于分发代理的东西。每一个标记都是单个字符(要么设置,要么不设置)。

Mhub,  P=[IPC],A=IPC $h
Mlocal, P=/bin/mail,  F=lsDFMAw5:/|@rmn, S=10, R=20/40, A=mail –d $u

  这儿有许多标志可供选择。这些标志将在第30章分发代理中说明,而在后面的几章中也会说明一些。

  S= 在等号后指定使用哪一个规则集来重写发送者地址:


Mhub,  P=[IPC],A=IPC $h
Mlocal, P=/bin/mail,  F=lsDFMAw5:/|@rmn, S=10, R=20/40, A=mail –d $u

  由于不同的分发代理使用的地址格式不尽相同,所以有时需要重写发送者地址。

  例如,[IPC]代理使用[email protected]的格式,而uucp代理则使用host!user格式。

  再此,指定了分发代理使用第10规则集来重写发送者地址。我们将在第8章:地址和规则中详细地说明。

  R= 在等号后面指定使用哪一个规则集来重写发送者地址:


Mhub,  P=[IPC],A=IPC $h
Mlocal, P=/bin/mail,  F=lsDFMAw5:/|@rmn, S=10, R=20/40, A=mail –d $u

  同样的道理,我们需要重写接收者地址。在此,指定使用规则集20重写信封地址,使用规则集40重写信头地址。

  而从sendmail 8.7版开始,引入了一个新的定义:

  T= 这用来指定一些相关的信息,如:

Mlocal, …, T=DNS/RFC822/X-Unix

  第一个信息是MTA使用的(这儿是DNS,因为sendmail是使用DNS来查寻地址的);接着在“/”之后的第二个信息是地址使用的(这儿是RFC822,也可以是X.400);最后是错误消息类型(这儿是X-Unix,说明/bin/mail将产生Unix的错误)。

4.3.1 略过规则集

  由于我们有时并不需要转换规则集,这很简单,我们只需将S=和R=后面的规则集做如下的修改就可以了。

Mhub,  P=[IPC],A=IPC $h
Mlocal, P=/bin/mail,  F=lsDFMAw5:/|@rmn, S=0, R=0, A=mail –d $u

  规则集设为0,代表不使用规则集。

4.3.2 增加注释

  注释对于每一个配置文件都是十分重要的部分,它将提醒你现在在做什么,以及以前做过什么。现在编辑client.cf文件,拿掉两行原来的注释,加入一行新的注释,使其内容为:

V7
# Delivery agent definition to forward mail to hub
Mhub, P=[IPC], A=IPC $h
# Sendmail requires this,but we won’t use it.
Mlocal, P=/bin/mail, F=lsDFMAw5:/|@rmn, S=10, R=20/40, A=mail –d $u

  我们拿掉的那两行注释语句是没有意义的两名,它们只是演示用的,并无法起到注释的作用。

4.3.3 增加注释

  现在,我们采用不同的方式运行sendmail:

% /usr/lib/sendmail –d0.15 –Cclient.cf –bt

  -d0.5是一个调试开关,它告诉sendmail显示你定义的分发代理是如何处理的。而-bt使得sendmail以规则测试模式来运行。以上命令将产生如下的输入。

mailer 0 (prog):P=/bin/sh S=0/0 R=0/0 M=0 U=0:0 F=Dlos L=0 E=
T=DNS/RFC822/X-Unix A=sh –c $u
mailer 1 (*file*):P=[FILE] S=0/0 R=0/0 M=0 U=0:0 F=DEFMPlosu L=0 E=
T=DNS/RFC822/X-Unix A=FILE
mailer 2 (*include*):P=/dev/null S=0/0 R=0/0 M=0 U=0:0 F=su L=0 E=
T=

  这个输出,将彻底地解释client.cf文件,通过M=、U=、L=和E=提示出来。

  在前面的输出中,大家可以发现有几个等式并未在定义中定义。如hub的定义中只包含了A=和P=:

Mhub, P=[IPC], A=IPC $h

  而当sendmail看到这个定义,将使其复杂化,加上了E=(换行符),T=
mailer 3 (hub):P=[IPC] s=0/0 R=0/0 M=0 U=0:0 F= L=0 E=
T=

  注意,当F=0时,就表示空列表,没有定义标记。

4.4 为Mhub增加缺少的部分

  最后一步,我们为Mhub增加它缺少的那部分:F=、S=、R=和T=。

  编辑client.cf文件,然后增加Mhub那么,使其成为:

# Delivery agent definition to forward mail to hub
Mhub, P=[IPC], S=0, R=0, F=mDFMuXa, T=DNS/RFC822/SMTP, A=IPC $h

  在此,我们让S=和R=都为0.S=用来指定重写发送者地址的规则集,而R=用来指定重写接收者的地址规则集。然而在此不需要地址重写,因此被赋予0值。

  而T=部分则与local的定义类似,唯一不同的是,它使用SMTP代替了X-Unix,即local是报告UNIX的错误消息,而hub则是SMTP错误消息。关于这一点,我们在后面会专门说明。

  而标志列表F=mDFMuXa,是最典型的设置。你可以根据自己的需要修改。所有的可用的标志在第30章中有详细说明。以下是一个概括性的说明表。

标志说明

m 这个代替能够同时为超过一个用户分发
D 在信头中包括Date:(日期)
F 在信头中包括From:(信从哪来)
M 在信头中包括Message-ID:(消息ID编号)
u 保持接收者姓名
X 遇到单独的点,变为“..”(两个点)
A 运行扩展的SMTP协议

  关于邮件分发代替的定义,就简单地说到这里。

[目录]



5. 宏

  在sendmail.cf文件中有一个十分重要的组成部分,就是那些使用代表文本的符号。这与bsh和csh中的变量的使用极为类似:

REMOTE=mailhost   (bsh)
set REMOTE=mailhost   (csh)
D{REMOTE}mailhost(sendmail.cf)

  以上几个语句都是定义了一个名为REMOTE的变量,被为其赋值为mailhost。

  而要使用变量中存储的值,可以使用以下表达式:

$REMOTE  (bsh)
$REMOTE  (csh)
${REMOTE}(sendmail.cf)

  也就是说,以上表达式将得到存储在变量REMOTE是值,在这个例子中就是文本字符mailhost。一经定义了REMOTE的值为mailhost,你就可以在任何地方使用以上表达式来代替文本字符mailhost。

5.1 概要

  宏能极大地简化您的工作。它允许你在一个集中的地方定义一些文本符号。而你只需修改这里的文本字符,则改变将自己传播到文件中其它部分。例如,假设我们在sendmail.cf文件中定义了:

D{REMOTE}mailhost

  如果你在sendmail.cf文件的其它地方都使用${REMOTE},则只需简单修改定义D{REMOTE}mailhost中的字符串,就将使所有表达式${REMOTE}的值改变。

  宏定义的语法格式如下:

DXtext

  必须使用字符D开始。这个字母D的后面紧跟着宏的名字(在这就是X),注意中间并没有空格隔开。接着在宏名的后面紧跟着值,注意在宏名与值之间也没有用空格隔开。这个值从宏名开始,直到本行结束。

  宏名可以由一个字符组成,也可以由多个字符组成。如果你使用多个字符做为宏名,就必须使用“{ }”把它们括起来。如果是使用单个字符做宏名,则也应该使用“{ }”包括起来,而在V8.7之前也可以不使用“{ }”。

DRmailhost (sendmail V8.7以前版本)
D{R}mailhost
D{REMOTE}mailhost

  通常,当读取配置文件的时候就会自动展开宏。因此,你必须在使用宏之间定义它们:

D{ROLE} son
S${ROLE}(此时的值是son)
D{ROLE}mother
S${ROLE}(此时的值是mother)

  在这里,ROLE首先被赋值为son,当处理到第一个S命令时,sendmail会将${ROLE}替换为前面定义的值:son,因此得到:

Sson

  而后,配置文件又将ROLE的值修改为了为mother,所以在处理到第二个S命令时,sendmail会将${ROLE}替换为ROLE的值mother,因此得到:

Smother

  不过请注意这是一个十分不好的风格,通常,为了不产生混乱,每个宏应该只被定义一次。

5.2 定义宏

  在上一章里,我们粗略了解了分发代理hub和local本地分发代理的定义。我们可以看到,在这两个分发代理的定义中的最后一部分“A=”都使用到了宏。

V7
# Delivery agent definition to forward mail to hub
Mhub, P=[IPC],S=0,R=0,F=mDFMuXa,T=DNS/RFC822/SMTP, A=IPC $h
# Sendmail requires this,but we won’t use it.
Mlocal, P=/bin/mail, F=lsDFMAw5:/|@rmn, S=10, R=20/40, A=mail –d $u

  宏有两种:一种是你自己定义的,而另一种则是sendmail定义的。你自己定义的宏,宏名一定要以大写字母开始;如果宏名是以小写字母开始的,如h、u,则是sendmail定义的宏。

  你已经看到过一个以大写字母开始的宏:

D{REMOTE}mailhost

  在你的网络中,邮件中枢机器会使用了mailhost或类似的(如mailrelay)的别名,然而有时则不存在这样的别名。此时你必须使用它的实际机器名(如mail.us.edu)。现在,我们编辑client.cf,然后加入第一个宏{REMOTE}。

V7
# Defined macros
D{REMOTE}mailhost # The name of the mail hub
# Delivery agent definition to forward mail to hub
Mhub, P=[IPC],S=0,R=0,F=mDFMuXa,T=DNS/RFC822/SMTP, A=IPC $h
# Sendmail requires this,but we won’t use it.
Mlocal, P=/bin/mail, F=lsDFMAw5:/|@rmn, S=10, R=20/40, A=mail –d $u

  在此,我们在client.cf文件中新增了三行。第一行是一个注释语句,第三行则是一个空行,有来可视地分隔开宏定义部分与分发代理定义部分。第二行是新的宏定义。正如注释中说的,这个{ROMOTE}宏将包含邮件将转发到的机器名。

  现在我们花一些时间来测试一下这个新的client.cf文件:

% ./sendmail –Cclient.cf –bt </dev/null

  sendmail程序将读取并分析client.cf文件。由于在这个配置文件中并没有错误,所以sendmail将不会打印错误信息。

5.3预定义宏

  在sendmail中,有一些内置的宏。你已经在分发代理的定义中看到过了u(接收者的用户名)和h(接收者的主机名)。它们无须在配置文件中定义,它们是sendmail定义的。这种宏,我们称之为预定义宏。下表中列出部分预定义宏:

宏名   描述

n      发送者错误消息标志符
v      当前运行的sendmail的版本
w      短主机名
j      规范的主机名
m      域名
k      UUCP节点名
b      RFC1123格式的日期
_      身份鉴别信息
opMode 当前操作模式(在V8.7版之后才有)

  在运行sendmail时,只需加上-d35.9参数,就可以显示出所有宏的定义:

% ./sendmail –d35.9 –Cclient.cf –bt </dev/null

  尽管client.cf文件如此小,但这个命令的输出惊人地长:

define(* as $*)
define(+ as $+)
define(- as $-)
define(= as $=)
define(~ as $~)
define(# as $#)
define(@ as $@)
define(: as $:)
define(> as $>)
define(? as $?)
define(| as $|)
define(. as $.)
define( [ as $[)
define(] as $])
define(( as $()
define() as $))
define(& as $&)
define(0 as $0)
define(1 as $1)
define(2 as $2)
define(3 as $3)
define(4 as $4)
define(5 as $5)
define(6 as $6)
define(7 as $7)
define(8 as $8)
define(9 as $9)
define(n as $MAILER-DAEMON)
  define(v as 8.8.4)
define(w as here.us.edu)
define(j as here.us.edu)
define(m as us.edu)
define(k as here)
define(b as Fri,13 Dec 1996 07:11:47 –0700 (PDT))
define(_ as you@localhost)
define(opMode as t)
redefine(w as here)
define(REMOTE as mailhost)

  不同版本的sendmail的输出将不同。例如,l、o和e在V8.7以前的版本中存在,而在8.7以后的版本中就没有了。前面27行显示出了sendmail为operators(操作者,将在下一章中具体说明)预留的宏。而后面的11行则是我们现在所感兴趣的。这11行中,前10行都是senmail程序预定义的,最后1行则是用户自己定义的。

  这些输出可以显示出另一个概念。一些内部宏是在读取配置文件之前定义的,你可以在client.cf文件中改变。例如我们要改为w,则只需在配置文件中加入一行:

Dwmyhost.my.domain

  使用了这行配置文件后,宏w的值就从here.us.edu变成了myhost.my.domain了。

  注意,在以上输出中并没有显示h和u宏。这些宏,尽管在内部定义了,但它们是直到邮件发送后才被赋予实际的值的。你不能够在你的配置文件中修改它们,因为它们在配置文件被读取之后才定义的。

  最后,请注意只有V8的sendmail会为宏设置适当的缺省值。所有老版本的sendmail需要你手动地在client.cf文件中设置e、l、n、o以及q的缺省值。

5.3.1 主机名

  本地主机名由两部分组成。主机名部分就是机器名,不包括“.”(如here)。而域名部分则至少有由“.”隔开的两个部分(如us.edu)。而全规范名(fully qualified)则是由主机名加上“.”,然后再加上域名构成。(例如:here.us.edu)。全规范名才能唯一标识一台机器。

  本地机器的主机名也需要规范化。一台机器可以有多个名字(如mailhost或printserver等等),但只能有一个是正式的。你可以执行hostname命令来获取这个正式的名字:

% hostname
here.us.edu

  下面我们就来看一下sendmail是如何解释你的本地主机名。我们以-d0.1的参数来执行sendmail:


% ./sendmail –Cclient.cf –d0.1 –bt </dev/null
version 8.8.4
Compiled with:LOG NAMED_BIND NDBM NETINET NETUNIX NIX SCANF XDEBUG
= = = = = = = = = SYSTEM IDENTITY (after readcf) = = = = = = = = =
   (short domain name) $w = here
   (canonical domain name) $j = here.us.edu
  (subdomain name) $m = us.edu
   (node name) $k = here
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

  这里经常容易出错。例如,如果你并未与Internet连接,你可能就不会有一个合法的域名。或者你可能运行NIS而使得在hosts.byname映射中只有短主机名。一个很重要的事情就是观察以上输出中,sendmail是否正确地发现了你的短主机名和规范主机名:

(short domain name) $w = here
(canonical domain name) $j = here.us.edu
(subdomain name) $m = us.edu

  如果这里有错误,你需要修复引起这个错误的系统问题。在系统级,调查以下问题:

  1)/etc/hosts文件。你可能在这个文件中仅列出了短主机名。对于sendmail 8.7以前的版本,它需要你在这个文件的最开始一行列出全规范主机名。而从sendmail 8.7开始,新版的sendmail只需要在这个文件中存在全规范主机名就可以了。

  2)nis映射。确认主机名能够被DNS查寻。

  3)nsswitch.conf或svc.conf文件。确定正确的主机名查寻方法在此列出。

  如果你无法发现,或没有足够的权限去做系统级的事情,你可以在你的client.cf文件中指定。例如,我们假设全规范名显示成了:

(canonical domain name) $j = here

  为了修复这个错误,你可以在client.cf文件中重新定义宏j,也就是在client.cf中加入:

Dj$w,$m # The local official domain name

  这个定义使得全规范名变成了主机名加“.”再加域名。这时,你可以再让sendmail显示一下,肯定会出现:

(canonical domain name) $j = here.us.edu

[目录]


规则

6. 地址与规则

  在研究到规则的内部工作原理之前,我们需要新建一个虚构的网络以提供一个公用的环境,以支持我们要谈论的mail地址。

6.1 一个虚构的网络

  我们拥有一个如图8-1所示的网络。它包括三个站点(每个云图表示一个站点),三个站点之间使用高速网络相连(有线连接)。每个站点都是一个由许多独立的计算机组成的一个DNS域。每一个域的设置都不相同,但从用户角度来看,发给一封信给本域内的用户与发一封信给其它域用户并没有什么区别。


  图8-1 在我们这个虚拟网络中的域分布

  一个域名是从右往左解释的。例如,acme.com是指acme是顶级域名com(供商业组织使用)的一部分。类似的,edu是供教育机构使用的顶级域名,gov是供政府组织使用的顶级域名。

  一个域可以包括许多机器。每一台域内的机器都有一个由主机名、“.”、域名组成的全规范域名。例如,sec.acme.com就是一个全规范域名,它包括主机名sec,一个点,以及域名acme.com。

  主机和域名是不区分大小写。就是说,sec.acme.com与下面几种写法完全等价:


SEC.Acme.COM
sEc.aCmE.cOm

6.1.1 域dc.gov

  图8-2显示了dc.gov域的内部结构。它是由三台主机组成(在真实世界可能会由更多台主机组成)。这三台主机互相之间使用私有网络(有线连接)连接起来,以保证其安全性。仅有一台主机:fbi.dc.gov拥有与外部网络的连接。所有从外面进来的邮件,将首先到达fbi.dc.gov,然后由fbi.dc.gov转发给内部网内的适当的主机上。例如:用户george最终还是在他自己的机器wash.dc.gov上收到他的邮件,即便邮件是从fbi.dc.gov转给他的。在这种情况下,fbi.dc.gov所称为网关(gateway),因为它就象是一个在内部网与外部网之间的一个门;它也可以被称为转发器(forwarder),因为它从外部网接收所有给内部机器的信件,然后转发给他们。


  图8-2 整个dc.gov域只有fbi.dc.gov可到达

  不论网关(gateway)是否存在,在.dc.gov域中的用户仍然是在他们自己的机器上收到邮件。在.dc.gov域中,内部邮件通过内部网络直接从一台机器发往另一台机器,网关(gateway)并不关心。但发往外部网的邮件则必须先发给网关(gateway)。

  网关(gateway)只会转发那些以域名为地址的邮件(如wash.dc.gov)。不幸地,因为网关(gateway)并不知道其它机器上有什么用户,所以象[email protected]这样的邮件,很可能会被网关(gateway)退回去。

6.1.2 域acme.com

  在图8-3中显示了.acme.com域的内部结构。它与.dc.gov域不同,在这个域中所有的主机都是直接与外部网络(Internet)相连的。所有的机器都可以直接从外部网接到邮件。例如,用户tim同样是在他自己的机器boss.acme.com上接收和阅读邮件,但与.dc.gov域不同的是,这里不需要网关(gateway)。


  图8-3 acme.com域中的所有主机都可以访问外部网

  就象fbi.dc.gov,机器sec.acme.com将接收到发往所有地址是acme.com的邮件(如[email protected])。但是与网关fbi.dc.gov不同,sec.acme.com知道整个域中的用户情况。所以,发往[email protected]的邮件将被正确地转发给主机boss.acme.com。

6.1.3 域uc.edu

  在图4中显示了第三种设置方法。在这个域中,所有的邮件(无论内部的还是外部的)都将发往mail.us.edu。与前面两个例子不同的是,所有的邮件都不再发到其它机器上,而是存放在mail.us.edu这台机器的spool目录中。其它机器通过mount这个目录来阅读他的邮件(或使用POP协议收信)。


  图8-4 只有mail.us.edu接收整个域的邮件

  在这个系统中,邮件地址仅需要包含本地接收的名字,一个“@”符,再加上域名(如[email protected])。这样不再需要知道每个用户使用哪台机器。

  以上三种实现方法各有优点,也各有缺点。我们并不完全赞成某个,也不完全否决某个。在本章中,假设我们的机器在us.edu域中。我们选择这个域是因为其sendmail配置文件最简单。

6.1.4 UUCP和主机通路

  在这个虚构的网络中,我们假定有两台机器通过拔号连接到acme.com域中(如图8-5)。在UUCP中,一台机器需要知道确切的连接方法。例如,sonya想从sec.acme.com中取得邮件,必须知道它还需要通过主机lady。


  图8-5 UUCP连接到sec.acme.com

6.2 为什么需要规则(Rules)

  规则(Rules)是在sendmail.cf文件中用来重写(修改)邮件地址,检查地址错误,选择邮件转发代理的。地址需要重写,是因为很多情况下,你需要根据转发代理的不同,命名不同的地址格式。如图8-6所示:


  图8-6 规则修改了地址,检测到错误,选择分发代理

  如果uuhost是一个通过拔号与你连接的主机,那么邮件就可能通过UUCP发送出去,因此地址friend@uuhost就需要转换成为UUCP的格式:uuhost!friend。

  而由于地址@neighbor未指定接收邮件的用户名,所以将被认为是一个错误的格式。

  分发代理是sendmail程序用来完成实际的邮件传送的。规则(Rules)通过检查每一个接收的地址,并根据地址为其选择分发代理。例如,对于[email protected]这个接收者地址而言,规则(Rules)检测到here.us.edu就是本机,因此选择本地(local)分发代理程序将最终分发到root的邮箱中。

  关于分发代理和宏我们已经在前两章中说明了。这一章将讲解规则集(Rules Set)是如何处理各种地址的。

6.3 规则集(Rule Set)

  一连串成组的规则将构成规则集。每一个规则集就象是一个子程序。在sendmail.cf文件中,我们使用S命令来说明规则集。例如:

S0


  这条语句说明了规则集0。规则集是从0开始往下编号的。规则集0-5是sendmail内部定义来用于特殊用途的,参见表8-1。我们将在后面详细地展开说明。在配置文件中并不需要按规则集编号来编写,也就是说,完全可以先定义规则集S5,然后再定义S2、S7等。Sendmail能够自己来排序。

  表8-1 规则集的用途


规则集  目的
0   邮件分发代理解析
1   处理发送者地址
2   处理接收者地址
3   预处理所有地址
4   后处理(Postprocess)所有地址
5   非别名本地用户重写

  到现在为止,client.cf文件内尚未包含规则集定义。我们执行以下命令再启动sendmail,观察不存在规则的效果:


% ./sendmail –Cclient.cf –bt
ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)
Enter <ruleset> <address>
>

  命令行参数“-bt”令sendmail运行在地址测试模式。在这种模式下,sendmail等待你输入一个规则集和一个地址。然后显示规则集如何重写这个地址。在“>”提示符后,输入一个规则集编号,一个空格,再跟上Mail地址,例如:


  > 0 [email protected]
  rewrite:ruleset 0  input:gw @ wash . dc . gov
  rewrite:ruleset 0 returns:gw @ wash . dc . gov
  >

  在地址测试模式下,所有的输出都将以“rewrite:”开始,以与其它的调试信息区别开来。而“input:”的意思是sendmail将其放入工作区,“return”则显示了通过规则集重写后产生的地址。

  我们将[email protected]送给sendmail的地址,首先会被基于两个分隔字符集分成几个部分。这两个分隔字符集均是内部定一个的,一个可以在配置文件中修改,另一个则不能修改:

  1). : @ [ ] 你可以修改这个分隔字符集

  2)() <> , ; “

  你不可以修改这个分隔字符集

  因为“.”和“@”都属于分隔字符集,所以地址[email protected]被分成七个部分。我们还发现,返回的地址没有任何变化,因为我们没有定义任何规则。

  如果没有定义规则集,那么结果将与输入一样。就好象一个只包含return语句的C语言的子程序一样。它们什么也不做,也不会报错。

  如果你想退出这个规则测试模式,输入Ctrl+D。

6.4 规则(Rules)

  一个规则集可以包含零个或多个单独的规则。规则以R命令开头,每个规则均由三个部分组成:


  S0
  Rlhsrhs comment

  在第一行,S0,说明了这是规则集0的开始,所有在这行之后的以R开头的规则定义都属于这个规则集。一个新的规则集将以一个新的S语句开始(S后应该是一个不同的规则集编号)。

  每一个R开头的行表示一个规则,在一个规则集可以有许许多多的规则,但我们在些假设在规则集0中只有一个规则。

  每一个规则包含三个不同的不分,每个部分之间使用tab键分开。你可以在一个部分中包含空格键,但一定要使用tab键将各个部分分开。

  规则的最左边部分是LHS,中间部分是RHS。这组成了一个规则。最后一部分是注释语句。它必须与RHS之间用一个或多个tab分开。

  LHS和RHS组成了一个if-then语句,如果LHS是真值,那么就执行RHS,如果LHS是假的,那么将跳到一下个规则:

6.5 工作区(workspace)

  管理LHS是真还是假,都需要进行比较。当对地址根据规则集进行重写处理时,sendmail将首先将它们标记化,存入内部的缓冲区,这就是工作区,workspace。

“gw” “@” “wash” “.” “dc” “gov”存储在工作区内。


  当检测LHS时,也会被标记化,然后将其与工作区内的标记(地址标记化后的结果)进行比较。如果相同,就说明匹配,LHS的结果就true(真)。现在,我们临时在client.cf中添加一个规则来讲解这一过程。


  S0
  Rleft.side  new.stuff

  别忘了LHS与RHS之间要使用tab键隔开。接着我们执行以下命令,使其以规则测试(地址测试)的方式运行:


  % ./sendmail –Cclient.cf –bt
  ADDRESS TEST MODE(ruleset 3 NOT automatically invoked)
  Enter <ruleset> <address>
  >

  在这一模式下,我们可以用“=S”命令查看所设置的规则:


  > =S=0
  Rleft . side new . stuff
  >

  这个显示说明,sendmail发现规则集0中仅包含一个规则。

  现在,我们使用规则集0,以及一个邮件地址:


  > 0 [email protected]
  rewrite:releset 0   input:gw @ wash . dc .gov
  rewrite:releset 0  return:gw @ wash . dc .gov

  地址并没有被重写,是因为工作区的内容与LHS并不匹配。


[email protected] 工作区内容
Rleft.side new.stuff 规则

  不匹配

  现在,我们在提示符状态下,输入与LHS完全相同的字符:


  > 0 left.side
  rewrite:releset 0   input:left . side
  rewrite:releset 0  return:new . stuff
  >

  一个惊人的事情发生了。规则重写了地址。地址left.side被规则集0重写为一个新的地址:new.stuff了。这个转变是因为输入的地址与LHS匹配,使得LHS值为真了。


  left.side  工作区内容
  Rleft.side new.stuff 规则
  匹配  ----->  执行new.stuff

  我们再做一个实验,输入大写的LEFT.SIDE,看一看:


  > 0 LEFT.SIDE
  rewrite:releset 0   input:LEFT . SIDE
  rewrite:releset 0  return:new . stuff
  >

  我们发现,这样仍然认为工作区与LHS是匹配的,说明这个判断是不区分大小写的。

6.6 地址通过规则的流程

  当规则集中包含多个规则时,整个流程是从第一个规则开始,一直到最后一个规则(从顶向下)。为了更好的说明这点,我们在client.cf文件中删除掉刚才的那个示例规则,然后换成以下规则:


  Rx  y
  Rz x
  Rx w

  再次提醒你注意LHS与RHS之间必须用tab键隔开。

  在测试这个新规则之前,你可以先猜测一下它们是如何工作的。第一个规则将工作区中的所有x重写成y。第二个规则则将工作区中的所有z重写成为x。最后一个规则将工作区中的所有x重写成w。

  现在我们以规则测试的方法再次启动sendmail:


  %./sendmail –Cclient.cf –bt
  ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)
  Enter <ruleset> <address>
  >

  然后我们输入规则集0和字母x


  > 0 x
  rewrite:ruleset 0  input:x
  rewrite:ruleset 0 returns:y

  这个输入显示了所有在工作区中的x被第一个规则重写成了y,然后执行第二个规则,结果不区配,第三个规则也是不匹配:


   x原来的输入
  Rx y  第一个规则
   匹配!重写成为y
   y
  Rz x  第二个规则
  不匹配!跳过
  Rx w  第三个规则
  不匹配!跳过
------> 最后得到了y

  这里有一个十分重要的一个问题,就是我们始终用的是当前工作区的内容与LHS比较。前面的规则会改变工作区。例如,本来第三个规则会将x改成w的,但由于第一个规则已经将其重写成为y了。

  现在我们输入规则集0和字母z:


> 0 z
rewrite:ruleset 0  input:z
rewrite:ruleset 0 returns:w

  为什么z被重写为w了呢?我们来看看发生了什么:


   z原来的输入
  Rx y  第一个规则
  不匹配!跳过
  Rz x  第二个规则
  匹配!重写为x
  x
  Rx w  第三个规则
   匹配!重写为w
  ------> 最后得到了w

  在这个实例中,第一个规则没做任何事,第二个规则将z重写为了x,而第三个规则又将x重写成为了w。

  现在,我们再输入一些不是x或z的字母,看看:


  > 0 b
  rewrite:ruleset 0  input:b
  rewrite:ruleset 0 returns:b

  我们发现,返回的地址与输入一样,由于三个规则都不匹配,所以工作区内的字符不有任何改变。

6.7 通配符

  如果规则中的字符要与工作区内的完全相符的话,那么规则就没有存在的意义了。幸运的是,并非是这种情况。我们可以在规则中使用通配符以使得规则能够匹配一类的文本。例如,假设有一个规则:

  R$+ RHS


  LHS是以R开头的,也就是说在这个例子中,LHS是:$+。这是一个通配符。这个LHS能够匹配多个标记。地址在与LHS比较时,会被标记化,然后将分解的标记存放在工作区中,形如:

  [email protected]


  标记化

  gw  @  wash  .  gc  .gov---- > 存放在工作区内


  当比较工作区与LHS模式时,sendmail将从左到右检查工作区内的标记。每一个将与通配符($+)比较。如果标记与这个模式匹配,那么就将使LHS为true。

  通配符$+能够匹配一个或多个标记。


   工作区   模式
gw  $+   一个字符,匹配!
@  多个字符,匹配!
   wash
.
dc
.
   gov


  正如你所看到的一样,工作区中的所有标记都与$+匹配。也就是只要工作区不是空的,就与$+匹配。(有点象DOS命令行下的通配符*)。

6.7.1 LHS中的其它文本

  一个$+是不足以处理所有可能的地址(一些特殊的错误地址),例如:

  [email protected] 与$+匹配
  @wash.dc.gov 也与$+匹配

 为了使得LHS更加有效,sendmail允许其它文本字符出现在模式中。例如,我们想确保地址中有一个用户名,一个@符,和一个主机名组成,那我们就可以在LHS中使用模式:

  $+@$+


  这个模式被分解为三个标记:

  $+  @  $+


  现在一个正确的、有效的邮件地址就会与我们新的LHS模式相匹配了。


  工作区  模式
   gw  $+   一个字符,匹配!
   @   @  匹配!
   wash$+  区配多个字符
   .
  dc
   .
   gov

  而一个无效的邮件地址,如@wash.dc.gov就会不匹配了:


工作区 模式
  @$+  匹配多个字符!
wash
  .
  dc
  .
  gov
@   没有相匹配的项
$+

  这样的结果就是不匹配了。

6.7.2 最小限度的匹配

  到现在为止,还可能有一些产生混淆的小问题。当使用通配符$+时,sendmail一直采用的是最小限度匹配策略。

  这个策略使用言语难以描述清楚,我们还是通过一个实例来说明,假设有规则:

   R$+@$+

  工作区内的地址是

  a@b@c

  那么匹配的结果是什么呢?


工作区 模式
a$+  匹配多个字符!
@ @
b $+
@
c

6.7.3 LHS匹配实验

  花一点时间将client.cf文件中前面的示例规则改成:


   S0
   R@   one
   R@$+ two
   R$+@$+   three

  然后,我们再使sendmail运行在规则测试模式:


   %./sendmail –Cclient.cf –bt
   ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)
   Enter <ruleset> <address>
   >

 现在,我们查看一下已经设置的规则集

> =s 0
R@ one
R@ $+ two
R$+@$+ three


  下面,我们就开始测试,首先我们输入单独的@符:

> 0 @
rewrite:ruleset 0  input:@
rewrite:ruleset 0 returns:one

  可见,单独一个@将匹配规则一。

  然后,我们输入:

> 0 @your.domain
rewrite:ruleset 0  input:@ your . domain
rewrite:ruleset 0 returns:two

  而,我们输入:

> 0 [email protected]
rewrite:ruleset 0  input:you @ your . domain
rewrite:ruleset 0 returns:three

[目录]


规则集0

7. 规则集0

  规则集0是用来为一个专门的接收地址选择邮件分发代理。

  对于邮件消息的每一个接收者都将调用一次规则集0。对于每一个地址规则集0将做三次决定。这三次分别处理:邮件分发代理的符号名,邮件地址中的用户名,邮件地址中的主机名。我们在本章以及后的关于规则集的章节中都会展开说明。

7.1 引入规则集0

  记得我们曾经在第6章“邮件中枢与分发代理”,设计过一个client.cf文件,使得sendmail将所有的邮件都转发给了邮件中枢来处理。当时我们在client.cf文件中做了以下声明:

# Delivery agent definition to forward mail to hub
Mhub, P=[IPC], S=0, R=0,F=mDFMuXa,T=DNS/RFC822/SMTP,A=IPC $h

  在这种情况下,我们就无需设计任何重写接收者地址的规则,我们只需简单地将信发给邮件中枢就可以了。

  以下就是一个规则集的实例:

  S0
  R$+   $#hub  $@${REMOTE} $:$1  forward to hub

  第一行(S0)说明规则集0定义开始。你已经在上一章中看到过这样的LHS,$+是一个通配符,它能够与任何一个地址匹配,当然如果是空地址就无法匹配了。

7.2 RHS的三个部分

  规则集0的任务是负责将每一个地址解析成为三个部分:分发代理的符号名,主机的名字,在那台主机上的用户名。

  大家应该还能记得“规则”就象是一个条件语句,如果LHS与工作区的内容匹配,那么sendmail就会执行RHS中的内容。

  在规则“R$+ $#hub $@${REMOTE} $:$1”中,RHS将解析这三个部分:


$#hub 分发代理
$@${REMOTE} 主机名
$:$1 用户名


  一些转换的通配符的含义如下表所示:

  表9-1 规则集0转换符


转换符   说明
  $#    邮件分发代理
  $@    主机名部分
  $:    用户名部分


7.2.1 分发代理($#)

  第一个部分是分发代理的名字,在$#后面跟着的就是分发代理名。在RHS中$#符号告诉sendmail后面跟着的就是分发代理的用户名。

  我们在client.cf文件中定义了符号名hub:

# Delivery agent definition to forward mail to hub
Mhub, P=[IPC], S=0, R=0, F=mDFMuXa,T=DNS/RFC822/SMTP,A=IPC $h

  当RHS拷贝到工作区时,首先将$#符号拷贝到工作区,然后拷贝分发代理的符号名,并且将它进行标记化。由于hub中并没有任何分隔符,所以最终拷贝到工作区的内容就是:

$#  hub


7.2.2 主机名($@)

  第二个部分就是主机名。转换符$@告诉Sendmail,后面的内容就是邮件将要发送到的主机。如果在RHS中遇到类似“${name}”的表示式,则意味着我们使用了宏。在这个例子中,我们使用了${REMOTE},这个宏在client.cf文件中有以下定义

D{REMOTE}mailhost


  这样,最终

$@${REMOTE}


  被拷贝到工作区后,就成为了:

$@ mailhost


7.2.3 用户名($:)

  第三个部分就是用户名,RHS中的转换符$:意味后面跟着的是邮件接收者的用户名。它告诉sendmail后面跟的是用户名。在我们的例子中,使用的是$1。

  那么这个$1是什么呢?请看下图:

$+ . $+
|   |
$1  $2

  也就是说在第一个“.”之前的部分就是$1,而之后的就是$2。

7.3 测试规则集0

  为了使大家能够观察到实际的运行结果,我们花一些时间来做一些测试。首先我们在你的client.cf文件中加入规则集0:

  S0
  R$+   $#hub  $@${REMOTE} $:$1 forward to hub

  现在,我们以规则测试模式运行sendmail,然后输入地址boss@acme:


  % ./sendmail –Cclient.cf –bt
  ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)
  Enter <ruleset><address>
  > 0 boss@acme
  rewrite: ruleset 0  input: boss@acme
  rewrite: ruleset 0 returns:$# hub $@ mailhost $: boss@acme

  正如我们预料的一样,返回的是

$# hub $@ mailhost $: boss@acme


  规则集0是与其它规则集不同的。我们将会从下一章开始介绍其它规则集。

  1) 规则集0的规则可以使用$#,用于返回分发代理的符号名。如果其它规则集 使用$#,就会出现无法预测的错误。

  2) 在规则集0的$#后面的$@和$:具有特别的用途,它如果出现在其它规则集中,或者不包含$#的其它规则集0中的规则时,用途不一样。这一点我们会在后面的章节中说明。

7.4 错误的分发代理

  在邮件中枢模式下,尽管所有的邮件都将交由邮件中枢处理,但某些错误仍然会在本地处理。如果在本地已经发现了错误,将会立即对用户进行提示,而不是等邮件被退回。

  在我们讨论邮件的分发代理的那一章中,我们曾经告诉您sendmail需要一个本地(local)的邮件转发代理。所以我们在client.cf文件中定义了一个:

Mlocal,  P=/bin/mail,  F=lsDFMAw5:/|@rmn, S=0, R=0, A=mail –d $u


  在sendmail中,已经定了一个特殊的分发代理,它叫做error,这个分发代理你无法去定义它。这个分发代理是sendmail的一个处理机制。它对那些不合法的地址进行处理。

  记得,我们曾经使用过以下演示规则:

  S0
  R@ one
  R@$+ two
  R$+@$+ three

  当使用这个规则集时,如果处理地址“@host”的话,将会在工作区内重写为two。因此,我们使用一个sendmail内置的错误处理功能进行。

  我们在client.cf文件的规则集0定义中加入 一行。

R@$+ $#error $: Missing user name

  然后我们再次以规则测试模式启动sendmail,这个时候我们输入@acme会出现什么。

  % ./sendmail –Cclient.cf –bt
  ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)
  Enter <ruleset><address>
  > 0 @acme
  rewrite: ruleset 0  input: boss@acme
  rewrite: ruleset 0 returns:$# error $:Missing user name

  而如果我们输入合法的地址:boss@acme仍可得到正确的结果:


  > 0 boss@acme
  rewrite: ruleset 0  input: boss@acme
  rewrite: ruleset 0 returns:$# hub $@ mailhost $: bos

[目录]


qmail

1 Qmail简介

1.1 什么是Qmail
  Qmail是一种可以完全替代Sendmail-binmail体系的新一代Unix邮件系统.

1.2 为什么选择Qmail?

  A. 安全----Qmail将Mail处理过程分为多个分过程,尽力避免用root用户运行.同时Qmail也禁止对特权用户(root,deamon等等)直接发信.

  B. 可靠----Qmail的直接投递保证Email在投递过程中不会丢失.Qmail同时支持新的更可靠的信箱格式Maildir,保证系统在突然崩溃情况下不至破坏整个信箱.

  C. 高效----在运行于奔腾的BSD/OS上,Qmail每天可以轻松的投递200000封信件.

  D. 简单----Qmail要比其他的Internet Mail系统小得多.Qmail通过统一的向前机制完成forwarding,alias和maillist等功能,Qmail使用             简单高效队列来处理投递.Qmail-smtpd可以由inetd启动,节省了一定资源.

  Qmail支持: host and user masquerading
             full host hiding
             virtual domains
             null clients
             list-owner rewriting
             relay control
             double-bounce recording
             arbitrary RFC 822 address lists
             cross-host mailing list loop detection

  1.3 如何获得Qmail?
    可以通过访问www.qmail.org或mirror站点下载qmail-1.03.tar.gz,以及获得
  更多qmail的资料.

2 安装Qmail

  获得qmail-1.03.tar.gz后,用tar命令解包

  #tar xzvf qmail-1.03.tar.gz

  进入qmail目录后,仔细阅读一下README和INSTALL文件.然后开始编译qmail.

2.1 建/var/qmail目录:
   #mkdir /var/qmail

2.2 按照INSTALL.ids中方法建立qmail用户和组:

   # groupadd nofiles
   # 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
   # groupadd qmail
   # useradd -g qmail -d /var/qmail qmailq
   # useradd -g qmail -d /var/qmail qmailr
   # useradd -g qmail -d /var/qmail qmails

2.3 make setup check
2.4 阅读INSTALL.ctl和FAQ,配置qmail,最简单的方法是
   #./config
    或者
   #./config-fast your.full.home.name

2.5 建立系统别名
   # (cd ~alias; touch .qmail-postmaster .qmail-mailer-daemon .qmail-root)
   # chmod 644 ~alias/.qmail*

2.6 复制/var/qmail/boot/proc到/var/qmail/rc
   # cp /var/qmail/boot/proc /var/qmail/rc

2.7 开始测试Qmail投递程序

    启动qmail:
   # csh -cf '/var/qmail/rc &'

    先查看一下/var/log/maillog,搜索
    qmail: status: local 0/10 remote 0/20
    qmail-send通常是输出"status"或者"cannot start"如果不能正常启动.

    用ps监视一下qmail守护进程,应该有五个相关进程:
                qmail-send,以qmails用户运行
        qmail-lspawn,以root用户运行
        qmail-rspawn,以qmailr用户运行
        qmail-clean,以qmailq用户运行
        splogger,以qmaill用户运行

    本地Mail测试:

    % echo to: me | /var/qmail/bin/qmail-inject
    注意:要用你的用户名代替me
    Mail应该立即出现在你的信箱之中./var/log/maillog中应该有如下记录:
           qmail: new msg 53
           qmail: info msg 53: bytes 246 from <me@domain> qp 20345 uid 666
           qmail: starting delivery 1: msg 53 to local me@domain
           qmail: status: local 1/10 remote 0/20
           qmail: delivery 1: success: did_1+0+0/
           qmail: status: local 0/10 remote 0/20
           qmail: end msg 53

    53是inode号,20345是进程号,你的数字应该有所不同.

    本地错误测试:

    给一个不存在的本地用户发信:
   % echo to: nonexistent | /var/qmail/bin/qmail-inject
           qmail: new msg 53
           qmail: info msg 53: bytes 246 from <me@domain> qp 20351 uid 666
           qmail: starting delivery 2: msg 53 to local nonexistent@domain
           qmail: status: local 1/10 remote 0/20
           qmail: delivery 2: failure: No_such_address.__#5.1.1_/
           qmail: status: local 0/10 remote 0/20
           qmail: bounce msg 53 qp 20357
           qmail: end msg 53
           qmail: new msg 54
           qmail: info msg 54: bytes 743 from <> qp 20357 uid 666
           qmail: starting delivery 3: msg 54 to local me@domain
           qmail: status: local 1/10 remote 0/20
           qmail: delivery 3: success: did_1+0+0/
           qmail: status: local 0/10 remote 0/20
           qmail: end msg 54
    你将立即收到弹回的信件.
    远程投递测试: 向你在其他机器上的账户发信:
   % echo to: me@wherever | /var/qmail/bin/qmail-inject
           qmail: new msg 53
           qmail: info msg 53: bytes 246 from <me@domain> qp 20372 uid 666
           qmail: starting delivery 4: msg 53 to remote me@wherever
           qmail: status: local 0/10 remote 1/20
           qmail: delivery 4: success: 1.2.3.4_accepted_message./...
           qmail: status: local 0/10 remote 0/20
           qmail: end msg 53

    投递过程将在starting delivery和success中有个间断,SMTP传送是相对较慢的然后去检查一下me@wherever是否正确收到email.
    后面还有三个测试,分别测试本地postmaster,两次弹回mail和通过mail执行命令.具体方法可以在TEST.deliver中找到.

2.8 从Sendmail象Qmail转移.

2.8.1 首先找到sendmail的启动程序,一般是在/etc/rc.d目录中
      看上去象这样的命令:
           sendmail -bd -q15m
      将其注释掉.

2.8.2 杀掉Sendmail进程.
          如果sendmail有子进程,可以反复用-STOP和-CONT信号杀,直到没有子进程后用-TERM后加-CONT杀就可以了.

2.8.3 检查mail队列中是否还有信,如果认为有必要将他们发送出去,可以在以后一段时间不定期的运行sendmail.bak -q直到队列清空.

2.8.4 去掉sendmail等文件的setuid bit
      # chmod 0 /usr/lib/sendmail
      # chmod 0 /usr/sbin/sendmail
      # chmod 0 /usr/lib/sendmail.mx

2.8.5
      # mv /usr/lib/sendmail /usr/lib/sendmail.bak
      # mv /usr/sbin/sendmail /usr/sbin/sendmail.bak

2.8.6 将
      csh -cf '/var/qmail/rc &'
      添加到启动文件中去.

2.8.7 安装qmail的sendmail外壳:
       # ln -s /var/qmail/bin/sendmail /usr/lib/sendmail
       # ln -s /var/qmail/bin/sendmail /usr/sbin/sendmail

2.8.8 在/etc/inetd.conf中设置qmail-smtpd:(是一行)
       smtp stream tcp nowait qmaild /var/qmail/bin/tcp-env
       tcp-env /var/qmail/bin/qmail-smtpd

2.8.9 重启动inetd,用-HUP信号杀inetd及可.

2.8.10 进行收信测试:仔细阅读TEST.receive

2.8.10.1 SMTP Server测试,me是你的用户,domain是你的机器名.
       % telnet 127.0.0.1 25
       Trying 127.0.0.1...
       Connected to 127.0.0.1.
       Escape character is '^]'.
       220 domain ESMTP
       helo dude
       250 domain
       mail <me@domain>
       250 ok
       rcpt <me@domain>
       250 ok
       data
       354 go ahead
       Subject: testing

       This is a test.
       .
       250 ok 812345679 qp 12345
       quit
       221 domain
       Connection closed by foreign host.

         看看信箱中是否有新信.

2.8.10.2 从其他机器上发信给me@domain,看看是否能正确接收.

         后面还有三个测试:远程错误测试,UA测试和远程PostMaster测试,具体方法可以参照TEST.receive

如果以上都没有问题,Qmail就算基本安装成功.


3 qmail的一些特殊配制

3.1 如何设置Smart Host:
         # echo ":your.smart.host" > /var/qmail/control/smtproutes

3.2 如何设置允许转信:

    将/etc/inetd.conf中smtp服务做如下修改:
   smtp stream tcp nowait qmaild /usr/local/bin/tcpd
   /var/qmail/bin/tcp-env /var/qmail/bin/qmail-smtpd

    然后阅读tcpwarpper相关文档,在/etc/hosts.allow中加入

   tcp-env: 1.2.3.4, 1.2.3.5: setenv = RELAYCLIENT

    1.2.3.4和1.2.3.5是你的客户机ip地址,也可以使用tcpwarpper的所允许的其他地址形式.

3.3 如何设置虚拟主机
    将虚拟主机名入/var/qmail/control/local和/var/qmail/control/rcpthosts然后在/var/qmail/control/virtualdomains中加入

    your.virtual.domains:youracct

    这样所有发往[email protected]都将以[email protected]的形式发给youracct,配合procmail和fetchmail,可以方便的分捡进行再投递.


[目录]


router

  利用 Linux 当 router 其实很简单.首先你在 compile kernel 时要选IP forwarding. 用这个 kernel 开机,看看能不能抓到两块网路卡.如果不行的话在 /etc/lilo.conf 上加上

append="ether=irq,io-port,eth0 ether=irq,io-port,eth1"

再重跑 lilo. 或者如果你用 loadlin 的话,用

loadlin vmlinuz root=/dev/??? ro ether=irq,io,eth0 ether=irq,io,eth1

应该就可以了.注意看开机时有没有抓到? 或看 /proc/net/dev 有没有 eth0和 eth1? 有的话就对了(如果没有就要 recompile kernel 了.请参考 "为何我的网路连不出去" 一文).

  接下来就是 config 这两个界面了.假设你的网路环境如下:

                                                |
                                                |   +-----+
                                                +---| DOS |  140.122.52.236
  +---------+             eth0 +-----+ eth1     |   +-----+
  | gateway |------------------|Linux|----------+           分出来的次子网
  +---------+                  +-----+          |   +-----+
140.122.52.254            140.122.52.235       +---| Sun | 140.122.52.234
                               router           |   +-----+
                                                |
                                                可再接多台机器


    eth0 界面是用来跟外面的网路相连的.你可以用一般的 netconfig 来设定它.(若不会的话还是请参考精华区中的文章).
    我们是利用 Linux 的 Proxy Arp 功能,当外界有封包要送到你的次子网路时,Linux 会回应此封包.所以对外界来说你的次子网就是存在的了.因此很重要的一点是,你的次子网必须要是从你原来的子网路上割出来的!!
    以上面的情形为例,如果预估你的次子网约有五、六台左右的机器,你可以决定要割出来的次子网的 IP 范围是从 140.122.52.232 -- 140.122.52.239 共8个位址(一定是 2^n 啦! 不懂吗? 自己想...:) 算一算
netmask 应是 255.255.255.248,
broadcast 是 140.122.52.239.

於是 eth1 应该这样 config:

# ifconfig eth1 140.122.52.235 netmask 255.255.255.248 broadcast 140.122.52.239
route add -net 140.122.52.232 netmask 255.255.255.248 gw
140.122.52.235 eth1

    这样你所有从 140.122.52.232 到 140.122.52.239 的封包都会往 eth1 送.试试看能不能从你的次子网 telnet 到 Linux 上? 若可以的话就成功了一大半....^_^
    不知你是否注意到,我将 eth0 和 eth1 的 IP address 设为一样! 常有人认为,有两块网路卡就要有两个 IP address. 但这是没有必要的! 你可以将两个设为相同!!
    但是你若想试著直接 telnet 到外面去,就会发现不通! 这是因为尚未设定 arp table 的关系.以上面为例,如果你想要 DOS 那台机器可以跨出次子网,就要在Linux 上设 arp:

# arp -s 140.122.52.236 08:00:77:14:00:00 pub
                        ^^^^^^^^^^^^^^^^^
                        ↑
                        这是你 eth0 界面的 hardware address, 可用 ifconfig 查看

  你可以把这些指令都放在/etc/rc.d/rc.inet1 中,这样开机时就可以设定好两个界面了!

[目录]


cvs

    在/etc/services文件中有如下的两行,这是配置服务的:

cvspserver      2401/tcp                        # CVS client/server operations
cvspserver      2401/udp                        # CVS client/server operations

    然后如果是使用inetd来启动,那么需要修改修改/etc/inetd.conf文件,如果使用xinetd,那么就需要在/etc/xinetd.d/目录下加入一个启动cvs的脚本我的系统使用的是red hat7.0,所以就加入如下的脚本到/etc/xinetd.d/下

[root@local /]# less /etc/xinetd.d/cvspserver
# default: off
# description: The SPOP3 service allows remote users to access their mail \
#              using an POP3 client with SSL support such as Netscape \
#              Communicator or fetchmail.
service cvspserver
{
        socket_type             = stream
        protocol                = tcp
        wait                    = no
        user                    = root
        server                  =  /usr/bin/cvs
        server_args             =  --allow-root=/source pserver
        log_on_success          += USERID
        log_on_failure          += USERID
        disable                 =  no
}

    如果使用inetd,也可以修改inetd.conf,很简单的。完成这些之后,就可以重新启动inetd/xinetd了。然后执行cvs的命令就可以正常使用cvs了。不过需要注意的是远程使用需要设置一下客户机:

CVSROOT=:pserver:user@host:/cvsrootdir:
export CVSROOT
cvs login

需要将前面两句加入到你的启动脚本中去。如果还有什么问题,请访问http://www.cvshome.org/docs/manual/


[目录]


bind

    主域名服务器正常工作的必要条件:
1. 安装bind8软件
    这在很多unix的发行版中都可以找到,在小红帽里有rpm包可以直接安装.或者去http://freesoft.online.sh.cn,
ftp://studio.sinet.net.cn都可以找到。

2. 几个必须的配置文件:
        named.conf
        named.ca
        named.local
        mater file(就是bind4里的zone file)

    在这几个配置文件中,最主要的是named.conf,在/etc下,
它是named启动时缺省的启动文件.一个典型的named.conf文件至少包括
options,和zone.如:

        options {
        directory        "/var/named";
};

zone "0.0.127.in-addr.arpa" in {
        type master;
        file "named.local";
};

zone "." in {
        type hint;
        file "named.ca";
};

zone "99.cn.net" in {
        type master;
        file "db.99.cn.net";
};

    在这里面,options定义master file存放的路径,对应于某一个域,named就会到这里找数据文件,如有一个www.99.cn.net的require请求,named就会到/var/named下找db.99.cn.net这个文件,查出www.99.cn.net的ip. zone定义一个域,如99.cn.net这个域,type定义域名服务器的类型,master说明这是一个主域名服务器,第一个zone定义本地服务器为它自己的回送域的主服务器,将地址127.0.0.1映射为localhost,几乎在所有类型的域名服务器里都可以看见这个域。第二个zone用来定义一个高速缓存初始化文件,在named.ca里至少包含着根服务器的名字和地址(这些根服务器也会变化). 一般named.ca不需要修改,named.local里只需要修改SOA纪录里域和联系人。(有些书上说named.local里的NS纪录是个摆设,但我建议最好还是有这一句)
    file定义99.cn.net这个域的master file.以下就是这个master file的内容:


@       IN SOA  ns.your.domain. root.your.domain. (
                1999110901              ; Serial Number
                10800                   ; Refresh after 3 hours
                3600                    ; Retry after 1 hour
                3600000                 ; Expire after 6 weeks
                86400 )                 ; Minimum TTL of 1 day

@       IN NS   ns.your.domain.
localhost       IN A    127.0.0.1
www     IN A    202.98.106.12

    这里面的@定义当前域,也就是your.domain, IN定义这是一个internet类型的纪录,SOA(start of authority)标志一个授权域的开始,ns.your.domain.为开创该域的服务器,可以用主域名服务器,root.your.domain.定义联系人,root后的.就是email里的@,括号里的几个数字定义和本域有关的几个参数,单位是秒,前四个参数用于辅域名服务器更新master file,其中;后面的是说明,serialnumber用于辅域名服务器判断主域名服务器的master file是否更新,所以如果你有辅域名服务器,在每次修改master file后就应该修改这个序列号,以便辅域名服务器更新这个域的 master file. refresh定义辅域名服务器刷新的时间,
    retry定义如果主服务器未响应,辅服务器重试的时间间隔,expire定义这个域的过期时间,就是说如果辅服务器在连续42天里都没有从主服务器取到该域的信息,辅服务器就丢弃该域。第五个参数定义这个域在其他域名服务器的cache里的有效期,过了这个时间其他的域名服务器就会到这里来重新查询相关的信息。

        NS表明这个域的域名服务器是ns.your.domain. NS纪录可以有多个。localhost和www这两条A纪录的含义是将localhost解析到127.0.0.1和将www.your.domain解析到202.98.106.12,现在一个基本的主域名服务器就建立好了,但需要特别说明的是,在master file里,ns.your.domain.后面的.这个.表示这是一个完整的纪录,否则服务器就会自动给你加上当前域,例如,www表示www.your.domain,而www.your.domain就会变成www.your.domain.your.domain,正确的表示方法可以是www或www.your.domain.如果不给这个.足够的重视,就很容易出错。

        如果你需要维护很多域,可以在named.conf里加上相应的zone,然后在/var/named下建立该域的master file,最后用SIGHUP来reload域名服务器。如果你需要作反向解析,可以仿照上面named.conf里第一个zone进行,在它相应的master file里用PTR指针将ip转换为域名。

        辅域名服务器的建立

        建立辅域名服务器的方法和主域名服务器基本相同,主要的区别在于named.conf里的zone类型,它的type为slave,以下是一个辅域名服务器的named.conf:

        options {
        directory        "/var/named";
};


zone "0.0.127.in-addr.arpa" in {
        type master;
        file "named.local";
};

zone "." in {
        type hint;
        file "named.ca";
};

zone "99.cn.net" in {
        type slave;
        file "db.99.cn.net";
        masters { 202.98.21.141; };

        可以看出,这个配置文件与主域名服务器的区别,type用salve定义该服务器为辅域名服务器,然后指出主域名服务器的ip。但是需注意第一个zone的type仍为master.另一个区别就是辅域名服务器的master file不用手动建立,它自己会从主域名服务器传过来,named-xfer缺省一次传10个域。辅域名服务器的master file和主域名服务器的master file基本没有什么区别。这样一个可以工作的辅域名服务器就建好了,如果这是一个新建的辅域名服务器,那么在你启动它后很快就会看见/var/named下多了很多master file. :)

        还有一点我想说明的是,如果internet上的域名服务器乱了,那么整个internet就乱了,所以维护域名的人一定要谨慎从事。因为以前在internet上工作的都是bind4,如果你维护的域比较多,在从bind4转到bind8时可能会为建立格式完全不同的named.conf而头痛,一个好消息是在bind8的发行版中有一个named-bootconf的shell脚本和named-bootconf.pl的perl脚本可以帮你完成从bind4的named.boot到bind8的named.conf的转换,为了偷懒,我也写了一个perl脚本完成从主域名服务器的named.conf到辅域名服务器的named.conf的转换,见附录(仅供参考)。

        附录:named-masterslave.pl

         作用:将主域名服务器的named.conf转换为辅域名服务器的named.conf
         用法:将主域名服务器的named.conf和这个脚本放在同一个目录下,将脚本中masters里的ip改为你自己的主域名服务器的ip,运行这个脚本,就会得到一个named.confd的文件,将文件中有关named.local和named.ca的zone按上面的例子修改,然后存成/etc/named.conf就可以了。

#!/usr/bin/perl
        $filename="named.conf";
        open(In,"$filename");
        @lines=<In>;
        close(In);
        open(Out,">named.confd");
        foreach $line(@lines) {
                if ($line=~ /type/)
                { $line=~ s/master/slave/;}
                if ($line=~ /file/) {
                  $line=~ s/;/;\n       masters { 202.98.21.141; };/;}
                print Out "$line";
                }
        close(Out);

=========================================================================================

建立安全的 DNS 服务器(Linux版本)

摘自:绿色兵团

    和其它大型软件一样,BIND(DNS服务器)也因其体积庞大和功能繁杂而存在许多问题。因此针对BIND安全漏洞的系统入侵数量也在大幅度上升,最严重的甚至可获取目标主机的所有远程控制权。由于DNS服务器主机对网络系统有着很大的影响,如何避免这些系统
入侵也变得至关重要了。

    这篇短文的主旨是讲述如何利用chroot()环境在RedHat Linux(或类似系统)中建立安全的BIND 8.x服务器。本文内容主要来自于Adam Shostack和他在这方面的文章(Solaris版本)。

步骤一:软件获取和安装

    请到ISC FTP站点下载BIND的最新版本(本文内容在BIND 8.x版本中经过测试)。到Obtuse System FTP站点下载本文必需的免费软件:holelogd(及其它有用的工具)。该软件用于在chroot环境中建立/dev/log套接字(socket),从而使syslogd能够记录named进程的日志。OpenBSD系统的syslogd已内建了这一功能("syslogd -a /chroot/dev/log"),但Linux系统尚未实现这一功能。Holelogd软件就是用来模仿OpenBSD的这个功能。

    按照软件文档安装holelogd(通常被安装到/usr/local/sbin)。

步骤二:构造静态(static)的named和named-xfer二进制文件
    在编译和安装后,你需要构造可执行文件的静态链接版本。只要对%BIND%/src/port/linux目录下的Makefile.set文件稍加修改后即可。
修改文件内容:

'CDEBUG= -O2 -g'
替换为:
'CDEBUG= -O2 -static'

    切换到BIND的源代码路径,执行"make clean"和"make"命令。在下面的步骤中将会把这些文件复制到chroot()目录下。
    本步骤构造的静态链接执行文件在运行时无需装载动态链接库。在chroot()环境中,这种“独立”可执行文件可避免出现缺少链接库文件问题。它在chroot()环境中无需任何静态链接库,可使服务配置简单化。其它所有的网络守护进程也可以编译和使用这种静态链接版本。

步骤三:构造BIND目录
    为chroot()环境构造BIND目录。这个目录将在chroot()环境中被BIND当作系统根目录。

/dev
/etc
/namedb
/usr
/sbin
/var
/run
需要复制以下文件到其下的相应子目录中,和进行一些必要的处理:
/

/etc
复制系统/etc目录下的named.conf文件
复制系统/etc目录下的localtime文件(为syslog提供正确的named日志记录时间)
创建仅包含named GID的/etc/group文件
/etc/namedb
复制系统/etc/namedb目录下的所有“区(zone)”数据库和文件
/dev
mknod ./null c 1 3; chmod 666 null(请参阅相应版本的mknod命令)
/usr/sbin
复制系统%BIND%/src/bin/named目录和系统%BIND%/src/bin/named-xfer目录下的named和named-xfer二进制文件(静态链接版本)
/var/run

另外还可根据需要指定日志记录目录(如/var/log)。

步骤四:添加named用户和组
    在/etc/passwd和/etc/group文件中添加named用户和组。它们是DNS服务器运行时的UID/GID。
    此时,你可以到chroot环境中执行"chown -R named.named /etc/namedb"命令。这样当你向系统发送中断信号(kill -INT )时,named进程能够保存服务器缓存和统计信息。如果该目录为root所有则named进程无法将输出写到目录中,但不会影响named服务器功能。另一个选择是仅改变目录权限(使named用户具有写权限),而属主仍然是root。这种方法也是可行的,但必须小心设置,确保其它用户不会修改named记录!

*** 重要警告***
    不要用一个已存在的UID/GID(如"nobody")运行named。记住,以chroot环境中使用任何已存在的UID/GID都可能会影响到服务的安全性。必须养成在chroot环境中为每一个守护进程提供独立的UID/GID的习惯。

步骤五:编辑启动脚本

    Linux使用SYS V风格的init文件,所以有几个地方都可以放置运行named的命令。(大多数情况下)最好将named初始化脚本放置到/etc/rc.d/init.d/named中。在其中你会找到有关named启动的那一节内容。我们需要添加和修改其中的某些行。

1、在运行named前插入一行以启动holelogd。需要向holelogd提供远程套接字位置的参数,它应该是在上面步骤中创建的chroot named dev目录。命令行内容如下:
# Start daemons.
echo -n "Staring holelogd: "
daemon /usr/local/sbin/holelogd /chroot/named/dev/log
echo
echo -n "Starting named: "
daemon named
echo
touch /var/lock/subsys/named
;;
2、另外还需要修改BIND的启动参数。BIND 8.x版本允许指定运行用户ID和组ID,它也应该是在上面步骤中特别创建的UID/GID:
# Start daemons.
echo -n "Staring holelogd: "
daemon /usr/local/sbin/holelogd /chroot/named/dev/log
echo
echo -n "Starting named: "
daemon /chroot/named/usr/sbin/named -u named -g named -t /chroot/named
echo
touch /var/lock/subsys/named
;;
3、named附带的"ndc"脚本可用于控制named的工作。需要编辑这个文件以将PID文件位置从/var/run/named.pid修改为/chroot/named/var/run/named.pid。

步骤六:服务器测试
    输入如下命令启动holelogd进程
/usr/local/sbin/holelogd /chroot/named/dev/log

    进入/chroot/named/dev/目录并输入ls -al。应该得到与下面类似的输出:
srw-rw-rw- 1 root wheel 0 Jan 01 12:00 log

设定的"s"位指示这是一个套接字(socket)文件。Chroot()环境中的named进程将通过该套接字与syslog通讯。
现在输入:
/chroot/named/usr/sbin/named -u named -g named -t /chroot/named
    如果一切正常,named进程将启动,日志文件将记录named服务器"Ready to answer queries."。
    进行适当的DNS测试以确保服务器能正确工作,然后重新启动系统并检验所有配置。BIND正常启动后会报告其chroot()目录和运行UID/GID。你可以使用lsof之类的程序列出主机中所有网络套接字进行检查。
    当一切工作正常后,建议将/etc/namedb改名为/etc/namedb.orig或其它名字,同时chmod 000 /usr/sbin/named,这样可以确保这些旧版本的named不会因偶然的失误而使用。

2000-02-22


[目录]


openssh

openssh

    ssh是Secure Shell的简称. 相当于一个在安全管道中的telnet, 所有数据都是加密传输的, 可以很好的防范网络窃听.openssh是Open BSD的一个部分, 是完全free的ssh实现, 同时支持ssh1, ssh2, 已经被移植到各种Unix平台上.openssh依赖于openssl, 因此安装openssh前, 必须先要安装openssl.

[目录]


简单使用

=================================
Server端的起动
=================================
单独运行, 直接运行
sshd

使用inetd起动
在/etc/inetd.conf中添加一行

ssh stream  tcp nowait  root    /usr/sbin/tcpd ssh -i
然后重新起动inetd就可以了.

=================================
Client端
=================================
ssh -l user host
或者
ssh user@host

[目录]


配置文件

sshd的配置文件( /etc/ssh/sshd_config)


# This is ssh server systemwide configuration file.

Port 22
#Protocol 2,1
ListenAddress 0.0.0.0
#ListenAddress ::
HostKey /etc/ssh/ssh_host_key
ServerKeyBits 768
LoginGraceTime 600
KeyRegenerationInterval 3600
PermitRootLogin yes
#
# Don't read ~/.rhosts and ~/.shosts files
IgnoreRhosts yes
# Uncomment if you don't trust ~/.ssh/known_hosts for RhostsRSAAuthentication
#IgnoreUserKnownHosts yes
StrictModes yes
X11Forwarding no
X11DisplayOffset 10
PrintMotd yes
KeepAlive yes

# Logging
SyslogFacility AUTH
LogLevel INFO
#obsoletes QuietMode and FascistLogging

RhostsAuthentication no
#
# For this to work you will also need host keys in /etc/ssh_known_hosts
RhostsRSAAuthentication no
#
RSAAuthentication yes

# To disable tunneled clear text passwords, change to no here!
PasswordAuthentication yes
PermitEmptyPasswords no
# Uncomment to disable s/key passwords
#SkeyAuthentication no

# To change Kerberos options
#KerberosAuthentication no
#KerberosOrLocalPasswd yes
#AFSTokenPassing no
#KerberosTicketCleanup no

# Kerberos TGT Passing does only work with the AFS kaserver
#KerberosTgtPassing yes

CheckMail no
UseLogin no

#Subsystem        sftp        /usr/local/sbin/sftpd

[目录]


scp

scp 的使用方法
  scp [-pqrvC46] [-P port] [-c cipher] [-i identity_file]
      [[user@]host1:]file1 [...] [[user@]host2:]file2

使用ssh-keygen生成key

ssh-keygen [ -f key_file ] 生成RSA key, 用于ssh1协议
ssh-keygen -d [ -f key_file ] 生成DSA key, 用于ssh2协议

注意: 作为Host Key使用的key, passphrase必须为空

[目录]


使用key做验证

使用key做验证
    缺省情况下, ssh仍然使用传统的口令验证. 可以通过简单的配置, 实现通过密匙的验证.

过程:

    用ssh-keygen生成用户密匙, 存放在~/.ssh/下, 文件名为 identity.将~/.ssh/identity.pub复制到远程机器用户帐户上, 并添加到~/.ssh/authorized_keys中.必须确定远程机器上~/.ssh/authorized_keys属性为400.这样就可以通过key来验证用户了.可以通过一个命令来完成以上操作

ssh-keygen -f ~/.ssh/identity; cat ~/.ssh/identity.pub | \
ssh user@remote "mkdir ~/.ssh; > ~/.ssh/authorized_keys ; \
chmod 400 ~/.ssh/authorized_keys"

[目录]


rsync与ssh配合使用

rsync与ssh配合使用
    rsync是个非常好的网络文件传输程序, 很适合大量文件的复制.
    rsync可以与rsh, ssh配合使用, server/client都需要安装ssh/rsync

rsync -e /usr/bin/ssh ......
    或者定义环境变量
export RSYNC_RSH=/usr/bin/ssh (bash)
    就可以直接让rsync去使用ssh了.


[目录]


cvs

cvs中使用ssh
cvs可以使用rsh, ssh作为传输管道.
export CVS_RSH=/usr/bin/ssh
export CVSROOT=:ext:user@host:/path


[目录]


Khttpd

一、简介


    从linux2.4.13开始,在Networking options出现了一个试验性的选项-"[ ] Kernel httpd acceleration (EXPERIMENTAL)",什么是kHTTPd呢?它是一个Linux环境下的web服务器。kHTTPd和其它web服务器的不同之处在于其是作为内核的一部分运行在Linux的内核中(可以看成是一个设备驱动)。

    kHTTPd仅仅处理静态(基于静态文件的)的web页面,而将所有的对于非静态内容的请求传递给正常的运行于用户空间的web服务器来处理,如apache、Zeus等,而这些运行在用户空间的web服务器并不需要任何修改。

    对于静态web页面的http请求的处理不是一个非常复杂过程,但是这却是web服务中非常重要的一个部分,因为至少网站中大多数图形都是静态的,而且还有很多html文件时静态的。一个常规的web服务器对于静态页面的http请求处理非常简单,仅仅是拷贝“文件到网络”的简单操作。如果这些操作在内核中完成将变得非常高效。例如也是完成类似的功能的NFS服务器也是运行在内核中的。

    通过在内核中实现web请求处理加速,通常意义的web服务器-如apache等则可以专注于处理那些动态web请求。

注:这里Apache指代任何一个web服务器。

二、快速入门

    1) 编译并加载模块
    2) 如果需要,通过/proc/sys/net/khttpd来对模块进行配置
    3) echo 1 /proc/sys/net/khttpd/start

卸载:

echo 1 /proc/sys/net/khttpd/stopecho 1 /proc/sys/net/khttpd/unload rmmod khttpd

三、配置

1、操作模式

    这里有两种推荐操作模式:

    1) "Apache" 是主web服务器,kHTTPd是辅助web服务器

clientport - 80
serverport - 8080 (or whatever)

    2) kHTTPd是主web服务器, "Apache"是辅助web服务器
clientport - 8080 (or whatever)
serverport - 80

2、配置kHTTPd

    在启动kHTTPd之前首先需要对它进行配置。这是通过/proc文件系统来进行的,因此可以在脚本中实现自动配置。大多数参数只能在kHTTPd没有启动以前才能设置。

    一般可以配置以下参数:

    1) kHTTPd监听的服务请求端口2) "Apache"监听的端口(在"localhost"接口中)3) web文档root目录(documentroot)4) 动态内容的请求所包含的字符串(可选的)[ 默认包括"cgi-bin"]

    在这里指定的documentroot一定要保证和运行在用户空间的web服务器的documentroot相一致,因为kHTTPd可能会将任何请求重定向给用户空间的web服务器来处理。

    一个典型的脚本(第一种操作模式)

#!/bin/shmodprobe khttpdecho 80 /proc/sys/net/khttpd/clientportecho 8080 /proc/sys/net/khttpd/serverportecho /var/www /proc/sys/net/khttpd/documentrootecho php3 /proc/sys/net/khttpd/dynamicecho shtml /proc/sys/net/khttpd/dynamicecho 1 /proc/sys/net/khttpd/start

    对于第二种操作模式,其典型的脚本如下:

#!/bin/shmodprobe khttpdecho 8080 /proc/sys/net/khttpd/clientportecho 80 /proc/sys/net/khttpd/serverportecho /var/www /proc/sys/net/khttpd/documentrootecho php3 /proc/sys/net/khttpd/dynamicecho shtml /proc/sys/net/khttpd/dynamicecho 1 /proc/sys/net/khttpd/start

    在这种情况下,你首先需要修改Apache的配置:
Port 80
修改为
Port 8080

3、停止kHTTPd

    为了修改配置,首先需要停止kHTTPd:

#echo 1 /proc/sys/net/khttpd/stop

    如果希望卸载模块,停止kHTTPd以后,运行:

echo 1 /proc/sys/net/khttpd/unload

    如果你感觉对你来说这个过程太慢了(上面的命令需要等待远程连接首先关闭),可以在停止其停止以后向服务器发送HUP信号,这将导致服务器线程立即停止。

    注:如果这些进程没有被停止,它们会马上重新启动。

四、 限制

    kHTTPd的安全模式非常严格,这是因为有用户空间的web服务器可以处理复杂的情况:

    kHTTPd只有在满足下面的条件才会处理请求:

    1) URL中没有"?"2) URL以"/"开始3) URL指定的文件存在4) 该文件的权限是所有用户可读的(*)5) 文件不是一个目录,不是可执行文件,没有sticky位置位(*)6) URL不包含任何非法子串,如:".."、"cgi-bin"等(*)7) 文件的mime类型是可知的(*)。这里后面标注*的条件是可以通过/proc/sys/net/khttpd来配置的不满足上面任何条件的请求将被转交给用户空间的web服务器来处理。,

五、参数

    下面的参数可以通过/proc/sys/net/khttpd被配置:

变量名         默认值    含义
serverport     8080     kHTTPd监听的服务端口
clientport     80       用户空间的web服务器的监听端口

    threads 2 服务器线程的数量,对于小型web来说应该是每个CPU对应一个,对于大型网站来说(活动文件不是保存在RAM中),其值应该是每个CPU对应2

documentroot /var/www web内容所在目录

start 0 设置为1来启动kHTTPd (该位可能被"stop"自动设置为0)

stop 0 设置为1来停止kHTTPd (该位可能被"start"自动设置为0)

unload 0 设置为1来准备卸载kHTTPd模块

sloppymime 0 如果为1,那么为止的mime类型自动设置为text/html,如果为0则未知mimi类型的文件将由用户空间的web服务器来处理

perm_required S_IROTH 需要的最小限制(其值参考"man 2 stat")

perm_forbid dir+sticky+ 文件禁止的限制掩码(其值参考"man 2 stat")

dynamic cgi-bin .. 动态内容请求URL包含的子串

maxconnect 1000 当前最大连接数

六、更多信息

    关于kHTTPd体系结构的更多信息,参考http://www.fenrus.demon.nl

[目录]


[ 本文件由良友·收藏家自动生成 ]