<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
<channel>
<title><![CDATA[Snooda]]></title> 
<link>http://www.snooda.com/index</link> 
<description><![CDATA[Snooda's Blog]]></description> 
<language>zh-cn</language> 
<copyright><![CDATA[Snooda]]></copyright>
<item>
<link>http://www.snooda.com/read/153</link>
<title><![CDATA[sendmail与mail工作原理及代码分析]]></title> 
<author>snooda &lt;admin@snooda.com&gt;</author>
<category><![CDATA[默认分类]]></category>
<pubDate>Sun, 19 Sep 2010 12:24:56 +0000</pubDate> 
<guid>http://www.snooda.com/read/153</guid> 
<description>
<![CDATA[ 
	    前几天突然想做一个信件分析并自动分发的程序。先给服务器设置了域名MX记录，开放了端口，设置了收信专用账户。然后准备开始分析邮件。<br />
<br />
	发现邮件在服务器上是在一个文件中连续存储的。打开/var/spool/mail/username可以发现里面顺序存储了很多邮件。那么把这些邮件拆开就成了问题。Mail命令可以分析并分拆邮件，但是mail命令有自己的命令行，用shell的方法对其操作很麻烦。<br />
	<br />
	然后决定看一下邮件的协议，看看邮件的规范是什么，从中找出特征值来分隔。后来发现各个邮件服务器发送的邮件形状各异，很难找到一些共性。而邮件规范RFC822并没有定义太严格的邮件格式，只是定义邮件头和邮件正文由换行隔开。（<br />
)。qq，163的邮件都是中规中矩的，很好识别。但是学校的邮件服务器发送的邮件就惨不忍睹。想了一天，也没有想出比较好的方法。<br />
	<br />
	上面只是问题一，还有一个问题就是文件同步互斥问题。如何保证读到的文件的完整性、sendmail在写入文件的策略是什么，都不太清楚。<br />
	<br />
	由于有了上面的问题，想到系统内置的mail命令应该是比较完美解决这些问题的，于是决定看一下mail的实现方法。    <br />
	首先查到mail命令从属的软件包是mailx。版本是：mailx-8.1.1-44.2.2。从网上找到mailx-8.1.1-44.2.2.src.rpm 下载下来。运行：rpm -ivh mailx-8.1.1-44.2.2.src.rpm。这样，源码就被解压到了/usr/src/redhat/下。该文件夹下有两个目录，一个是SOURCE，一个是SPECS。其中SOURCE下就是补丁和源码。SPECS里是打包rpm的一个工具文件。<br />
	<br />
	到SOURCE目录下，发现有一个mailx-8.1.1.tar.gz文件，一堆补丁，和一个.c文件。注意要先打补丁，然后再编译、查看源码。我刚开始的时候直接解开tar.gz开始弄，发现里面的Makefile都没法通过编译。自己写Makefile编译后还有bug。这是因为没有打补丁的缘故。<br />
	<br />
	下面说一下打补丁。这个时候要借助SPECS里面的文件。首先把tar.gz解开。解出的mailx-8.1.1文件夹和补丁们放在同一个目录下。然后去mailx.spec里看，有一句：Source1: flock.c。说明SOURCE下的flock.c文件是后来补的源文件，需要放到mailx-8.1.1里面。下面接着是：<br />
	Patch0: mailx-8.1.1.debian.patch<br />
	Patch1: mailx-8.1.1.security.patch<br />
	Patch2: mailx-8.1.1.nolock.patch<br />
	Patch3: mailx-8.1.1.debian2.patch<br />
	Patch4: mailx-noroot.patch<br />
	Patch5: mailx-nopanic.patch<br />
	Patch6: mailx-nullchar.patch<br />
	Patch7: mailx-8.1.1-fhs.patch<br />
	Patch8: mailx-8.1.1-environ.patch<br />
	Patch9: mailx-8.1.1-siglj.patch<br />
	Patch10: mailx-8.1.1-bug15728.patch<br />
	Patch11: mailx-8.1.1-bug10074.patch<br />
	Patch12: mailx-8.1.1-uidcheck.patch<br />
	Patch13: mailx-8.1.1-flock.patch<br />
	Patch14: mailx-8.1.1-nostrip.patch<br />
	Patch15: mailx-8.1.1-ctime.patch<br />
	Patch16: mailx-8.1.1-bug134837.patch<br />
	Patch17: mailx-8.1.1-manpage-fix.patch<br />
	Patch18: mailx-8.1.1-manfix.patch<br />
	Patch19: mailx-8.1.1-display.patch<br />
	Patch20: mailx-8.1.1-bug44798.patch<br />
	Patch21: mailx-8.1.1-bug58672.patch<br />
	Patch22: mailx-8.1.1-reproblem.patch<br />
	Patch23: mailx-8.1.1-mbproblem.patch<br />
	Patch24: mailx-8.1.1-unread.patch<br />
	这个是打补丁的顺序。需要用：patch -p0 &lt; *****.patch 挨个执行一遍。（-p0具体含义可查patch用法，和目录层级有关系。假如把补丁文件和源文件放到一起就是-p1）<br />
	这么多补丁要是手动去打的话很累。我直接写了个脚本，按列表挨个执行一遍打补丁操作。<br />
	打完补丁后再去Makefile里看，会发现现在Makefile已经完整了，直接make就可以编译。这时要注意，通过打补丁，有的源文件已经不再被需要。dotlock.c被no_dot_lock.c替换了。在读源码的时候要注意这一点。假如想使用dotlock.c的话可以修改Makefile，不过还要注意dotlock.c在包含头文件的时候#include &quot;extern.h&quot;和#include &quot;rcv.h&quot;这两句反了，要对调一下才能通过编译。<br />
	<br />
	现在开始读源码，从main.c开始，先是进行一些校验活动，临时文件的创建，参数读取。在265行if (setfile(ef) &lt; 0)调用setfile开始对系统邮箱进行处理。<br />
	<br />
	去lex.c找到setfile，也是一些初始化、校验过程后。156行setptr(ibuf);调用setptr函数，这个函数是分析系统邮箱的过程。<br />
	<br />
	去fio.c，找到setptr函数。115到151行是重点之所在。判断逻辑是：假如一行的开头是From并且后面是一个空格，那么这是一封邮件的开始。向下移动到第一个空行。到达邮件正文段。直到遇到下一封邮件头。<br />
	<br />
	在mail处理系统邮箱的时候会用flock/fcntl把文件加锁，解决了互斥问题。<br />
	<br />
	这里要注意，邮件开头是From 空格 其他值，而不是邮件头里的From字段，两者相差一个冒号。我刚开始以为mail规定From字段必须在第一行，跑去查RFC822，结果那里说不强制次序。分析收到的邮件发现确实都是From开头的。过了好一会才发现并不是From字段。。。<br />
	<br />
	下面又会疑惑了，这个From开头的行是哪里来的？邮件的发送方并没有发送这个字段。况且规范并没有规定这个，即使有的邮件服务器这样发送，一定会有不太规范的邮件服务器不这样发送。这样会出问题。经过分析有可能是sendmail在收邮件的时候给加上的。<br />
	<br />
	下载sendmail的源码。阅读README，里面讲到mail.local文件夹下是负责向用户系统邮箱投递邮件的模块。进去后发现mail.local.c文件。在784行，(void) fprintf(fp, &quot;From %s %s&quot;, from, ctime(&amp;tval)); 显然，sendmail在投递邮件的时候会先写一行，结构是：From 空格 寄信人 空格 时间。这就保证了每封邮件都是From 空格开始了。我们可以放心的用这一特征来分析了。<br />
	<br />
	mail.local.c里还可以发现，在写入文件前也是加了锁的。所以我们在读邮箱时也要加锁，假如加锁成功，那么可以保证我们分析文件与sendmail投递互斥进行。<br />
	<br />
	不过需要注意一点。假如分析邮件的过程比较长，那么需要把邮件先移动到一个临时文件里然后分析。避免长时间加锁邮箱导致sendmail发信队列积压。并且移动完后一定不要忘了给邮箱解锁。<br />
	<br />
	以上就是这几天阅读代码的一个小总结，欢迎拍砖。网上关于sendmail和mail的原理说的很少，大部分都是讲配置的。期待有深度分析的文章出现~~
]]>
</description>
</item><item>
<link>http://www.snooda.com/read/153#blogcomment89</link>
<title><![CDATA[[评论] sendmail与mail工作原理及代码分析]]></title> 
<author>Anoymous &lt;user@domain.com&gt;</author>
<category><![CDATA[评论]]></category>
<pubDate>Sat, 04 Jun 2011 22:42:23 +0000</pubDate> 
<guid>http://www.snooda.com/read/153#blogcomment89</guid> 
<description>
<![CDATA[ 
	看不到
]]>
</description>
</item>
</channel>
</rss>