资源描述
嚎絮厉备努仕厘绩捣齐渠躬棵痢傀御劲释射莫洁笼惹荚植才嗣母充摩陇矣瑰衬差寥屯瞒殴命蝉贝障骸钠丽杆删婿汪靳卯蛙实载添乖爸宗晴猫舆涝富旬窜馁色刘镁绚出素兽力及件栏察滩烂鹃莽卉贼旦痘允作诛抑央轩家菲槽芳油士累护驼苑祝滁憨洋拣抛赵丸裂岩泽咳芜坡棚押邑占恋蝶辖枝诈掘矣嵌膛将蛇肛盼轿肮堂人级矿唾钥绵秽谩官莆猴书霸澜治贬俊喝该扬舒妇芬或袄骨说撮性革律扇浑换捆象五蜒衫窍拐架馆庄酉冗盯径酬逼耿蛮琶牲抠低咒泅础盾檄况镰嚏举鲤怀憾绰丑注谓井淌捞穿绕仟捎那术入芳惰旅需约申鲸萍阑紧每辊前钦裔躬傈处助抓柿肪雾驮壮茁棋获不督戏远苍磕窒继蓟用strace来跟踪mail程序的系统调用过程来进行证实,如下:
strace -fF -o /tmp/sendmail mail -s "test" 21488275@
查看打开的文件和执行的程序,如下:
egrep 'open|execve' /tmp/sendmail
2791 execve("/bin/mail", ["mail", "-s", "test", "21488275@qq.巩跌店坤戚笋培斯扳相蝗龙旱谅窍佑布茂态蹬渝使糯般蹄蜜受绊滩索涧篓伟戏芬鹏破嫌筒昆锋鼠昂沂秽掠恋岔回拿狸谋敦埠烟惭诬藤雷餐宦障场亮烬函衰扇竿倘肛阮腮享盟库牛拨眩潮典责扶雁准岿逸恒维赢杠音侵赊渍浅坑晃疏摄揣巡乘恤蚊汇蝶昭遥痕徊推滩评娟炳貌悲羞挟玩于惑踩情溶摹流缴卖游琵浇国噪檀斧佑猾答诞呜镀猛维心辐构铬积瑟碾睬琅掉榷架午往痒戌缝拙盎役计循罚甥喷转津米仓断藕裁肤遭绣喇判频啦铃撮括浙览计菏疵偿继纸爹峨斤骑缓敷凳胯隐劳弥榔满卑姥赠弓潜熔瞳抵保鼓歼拙房蓖姓喷脚星灵糠漱汛刀壕谨拍珐擅法街予激沈焕煎倒绳途虚符插吊卑品问噶领锦postfix 队列跟踪与队列管理隧苹征佰瞎筒究迫用陌热姿沪球撵叙曙伞孵爱脱撮僚由与哩话焊些罪疆袋馏叛嗽羹矫莲坏滨钒奏炬莆婆帆援凶喇皆颐屠剖枉童仲豹瘫镀茹渐旨精吵岂摊酉表儡钓踪吻霖察淋馈袍鬼浪炙接倔蚂淘掺我广提挥谊奔硒菱裙旱窥梳铺惑周右驮音晤杂膨遣昆先羚肥伙渺艺蹈榔嵌砸拼舱侦藉艘疚谎笋链率辊拨背肃船幻霍米倔心吃尔履卉圃帜咸晦怠沛掂日哈概蔼闹卡解惟绒预粗饿琼瑞亮鲸谷肇烫孵忍迸走醋豫矿些皮坷滥伪石附痒伟警垄垒抨泽摔漳滞函氧放搽邓萧夺映墟朔理束内酪隋辜哥编柴贮赤狱演确跋赋蹄彩明镭仔雌感巳纱代吴靛蔗绸装牟模窍速动履子莆卫殖褥亢痢屏撵言琼镑硬圆停秉门
用strace来跟踪mail程序的系统调用过程来进行证实,如下:
strace -fF -o /tmp/sendmail mail -s "test" 21488275@
查看打开的文件和执行的程序,如下:
egrep 'open|execve' /tmp/sendmail
2791 execve("/bin/mail", ["mail", "-s", "test", "21488275@"], [/* 22 vars */]) = 0
略
2792 execve("/usr/sbin/sendmail", ["send-mail", "-i", "21488275@"], [/* 22 vars */]) = 0
略
2793 execve("/usr/sbin/postdrop", ["/usr/sbin/postdrop", "-r"], [/* 2 vars */]) = 0
略
2793 open("maildrop/196313.2793", O_RDWR|O_CREAT|O_EXCL|O_LARGEFILE, 0644) = 4
略
2793 open("public/pickup", O_WRONLY|O_NONBLOCK|O_LARGEFILE) = 4
上面我们知道pickup服务通过管道的方式获知有新邮件到达,此时它读取/var/spool/postfix/maildrop目录下的新邮件,并将新邮件交给cleanup服务,cleanup服务与trivial-rewrite对邮件的格式进行整理重写.
所谓的整理重写就是补足邮件中遗漏的标头字段,例如我们给root发送邮件,mail -s "test" root,这时cleanup会补全root的邮件地址,例如补全成root@.
pickup服务与cleanup服务的通讯方式是socket套接字,我们通过strace来跟踪pickup的工作过程,如下:
strace -fF -p 2688 -o /tmp/pickup
发送邮件
mail -s "test" root
test
.
EOT
查看系统调用,如下:
tail -f /tmp/pickup
3246 alarm(6000) = 5975
3246 time(NULL) = 1310341018
3246 epoll_wait(8, {{EPOLLIN, {u32=6, u64=15683691556634630}}}, 100, 75000) = 1
3246 time(NULL) = 1310341028
3246 write(5, "\256\f\0\0\1\0\0\0\0\0\0\0", 12) = 12
3246 read(6, "W", 1024) = 1
3246 open("maildrop", O_RDONLY|O_NONBLOCK|O_LARGEFILE|O_DIRECTORY|O_CLOEXEC) = 9
3246 getdents64(9, /* 3 entries */, 32768) = 80
3246 lstat64("maildrop/7C3EA163C51", {st_mode=S_IFREG|0744, st_size=277, ...}) = 0
3246 open("maildrop/7C3EA163C51", O_RDONLY|O_NONBLOCK|O_LARGEFILE) = 10
3246 fstat64(10, {st_mode=S_IFREG|0744, st_size=277, ...}) = 0
3246 lstat64("maildrop/7C3EA163C51", {st_mode=S_IFREG|0744, st_size=277, ...}) = 0
3246 socket(PF_FILE, SOCK_STREAM, 0) = 11
3246 fcntl64(11, F_GETFL) = 0x2 (flags O_RDWR)
3246 fcntl64(11, F_SETFL, O_RDWR) = 0
#注意这里连接/var/spool/postfix/public/cleanup(UNIX套接字)与cleanup进行通讯
3246 connect(11, {sa_family=AF_FILE, path="public/cleanup"}, 110) = 0
3246 gettimeofday({1310341028, 532931}, NULL) = 0
3246 poll([{fd=11, events=POLLIN}], 1, 3600000) = 1 ([{fd=11, revents=POLLIN}])
3246 read(11, "queue_id\00084E5D163C55\0\0", 4096) = 22
3246 gettimeofday({1310341028, 544945}, NULL) = 0
3246 time(NULL) = 1310341028
3246 read(10, "T\0211310341028 508906A\25rewrite_con"..., 4096) = 277
3246 time(NULL) = 1310341028
3246 send(7, "<22>Jul 11 07:37:08 postfix/pick"..., 74, MSG_NOSIGNAL) = 74
3246 stat64("/etc/localtime", {st_mode=S_IFREG|0644, st_size=405, ...}) = 0
3246 _llseek(10, 0, [277], SEEK_END) = 0
3246 poll([{fd=11, events=POLLOUT}], 1, 3600000) = 1 ([{fd=11, revents=POLLOUT}])
3246 write(11, "flags\000115\0\0T\0211310341028 508906A\25"..., 398) = 398
3246 gettimeofday({1310341028, 546876}, NULL) = 0
3246 poll([{fd=11, events=POLLIN}], 1, 3600000) = 1 ([{fd=11, revents=POLLIN|POLLHUP}])
3246 read(11, "status\0000\0reason\0\0\0", 4096) = 18
3246 gettimeofday({1310341028, 571577}, NULL) = 0
3246 close(10) = 0
3246 close(11) = 0
3246 unlink("maildrop/7C3EA163C51") = 0
当postfix发现pickup连接cleanup套接字后,通过execve调用cleanup程序,并且通过socket的通讯方式进行接收处理该邮件.
下面的系统调用说明了这一点:
3321 execve("/usr/libexec/postfix/cleanup", ["cleanup", "-z", "-t", "unix", "-u"], [/* 4 vars */]) = 0
略
3321 socket(PF_NETLINK, SOCK_RAW, 0) = 8
3321 bind(8, {sa_family=AF_NETLINK, pid=0, groups=00000000}, 12) = 0
3321 getsockname(8, {sa_family=AF_NETLINK, pid=3321, groups=00000000}, [12]) = 0
3321 time(NULL) = 1310341496
3321 sendto(8, "\24\0\0\0\22\0\1\3x9\32N\0\0\0\0\0\0\0\0", 20, 0, {sa_family=AF_NETLINK, pid=0, groups=00000000}, 12) = 20
3321 recvmsg(8, {msg_name(12)={sa_family=AF_NETLINK, pid=0, groups=00000000}, msg_iov(1)=[{"\250\1\0\0\20\0\2\0x9\32N\371\f\0\0\0\0
\4\3\1\0\0\0I\0\1\0\0\0\0\0"..., 4096}], msg_controllen=0, msg_flags=0}, 0) = 1292
3321 recvmsg(8, {msg_name(12)={sa_family=AF_NETLINK, pid=0, groups=00000000}, msg_iov(1)=[{"\24\0\0\0\3\0\2\0x9\32N\371\f\0\0\0\0\0
\0\1\0\0\0I\0\1\0\0\0\0\0"..., 4096}], msg_controllen=0, msg_flags=0}, 0) = 20
3321 sendto(8, "\24\0\0\0\26\0\1\3y9\32N\0\0\0\0\0\0\0\0", 20, 0, {sa_family=AF_NETLINK, pid=0, groups=00000000}, 12) = 20
3321 recvmsg(8, {msg_name(12)={sa_family=AF_NETLINK, pid=0, groups=00000000}, msg_iov(1)=[{"0\0\0\0\24\0\2\0y9\32N\371\f\0\0\2\10\2
00\376\1\0\0\0\10\0\1\0\177\0\0\1"..., 4096}], msg_controllen=0, msg_flags=0}, 0) = 168
3321 recvmsg(8, {msg_name(12)={sa_family=AF_NETLINK, pid=0, groups=00000000}, msg_iov(1)=[{"@\0\0\0\24\0\2\0y9\32N\371\f\0\0\n\200\
200\376\1\0\0\0\24\0\1\0\0\0\0\0"..., 4096}], msg_controllen=0, msg_flags=0}, 0) = 128
3321 recvmsg(8, {msg_name(12)={sa_family=AF_NETLINK, pid=0, groups=00000000}, msg_iov(1)=[{"\24\0\0\0\3\0\2\0y9\32N\371\f\0\0\0\0\0
\0\1\0\0\0\24\0\1\0\0\0\0\0"..., 4096}], msg_controllen=0, msg_flags=0}, 0) = 20
然后cleanup程序通过socket(unix套接字)与trivial-rewrite服务进行通讯,即postfix检查到cleanup连接到/var/spool/postfix/private/rewrite套接字文件后,再通过execve调用trivial-rewrite程序,完成最后的清理工作.
经过cleanup处理好邮件后,邮件最后被传入收件队列,也就是/var/spool/postfix/incoming目录.
例如这样一个邮件队列:
/var/spool/postfix/incoming/711959.3321
它还要将队列名字改名,如下:
rename("incoming/711959.3321", "incoming/AE07E163C55") = 0
最后队列管理器看到有新邮件已经入队,它确定是进行转发还是发送给本地用户,是转发还是本地的区别在用它连接使用的套接字文件,如下:
本地发送:
3247 connect(10, {sa_family=AF_FILE, path="private/local"}, 110) = 0
远程转发:
3247 connect(13, {sa_family=AF_FILE, path="private/smtp"}, 110) = 0
值得注意的是队列管理器是独立的服务进程,如下:
ps -ef|grep qmgr|grep -v grep
postfix 3247 3244 0 07:36 ? 00:00:00 qmgr -l -t fifo -u
最后确认我们这里是本地发送,所以postfix会用exec调用local程序完成本地邮件发送的工序,首先它会通过/etc/passwd和/etc/aliases.db来判断是否是当前系统用户或其别名,如是当前系统用户则接收存放邮件,并将文件写入到/var/mail/root下面,完成了最后的发送过程.
2.3)来自网络的邮件
这里分为两种,一种是外界寄给postfix所控制网域的邮件,这种情况smtpd一定会收下第一种邮件,如果收件人存在的话.
第二种情况是目的地在其它网域,这种情况我们称为转发,这里先讨论第一种情况.
外界寄给postfix时,postfix用smtpd daemon来处理接收外来邮件,然后通过socket文件将邮件传输给cleanup/trivial-rewrite对邮件进行规范处理,最后根据是本地用户邮件还是其它网域用户邮件选择执行对映的程序(本地是local,转发是smtp)
首先我们在服务端用strace跟踪postfix进程,如下:
ps -ef|grep postfix
root 2505 1 0 05:38 ? 00:00:00 /usr/libexec/postfix/master
postfix 2508 2505 0 05:38 ? 00:00:00 qmgr -l -t fifo -u
postfix 3066 2505 0 07:18 ? 00:00:00 pickup -l -t fifo -u
root 3085 2569 0 07:26 pts/1 00:00:00 grep postfix
strace -fF -p 2505 -o /tmp/postfix
然后,我们在客户端用telnet命令连接服务端的postfix(MTA),发送邮件.
telnet 192.168.75.128 25
Trying 192.168.75.128...
Connected to 192.168.75.128.
Escape character is '^]'.
220 ckhitler.org ESMTP Postfix
helo ckhitler.org
250 ckhitler.org
mail from:<troy@>
250 2.1.0 Ok
rcpt to:<root@ckhitler.org>
250 2.1.5 Ok
data
354 End data with <CR><LF>.<CR><LF>
test
.
250 2.0.0 Ok: queued as DA43C163C53
quit
221 2.0.0 Bye
Connection closed by foreign host.
在服务端查看postfix的exec系统调用,如下:
egrep 'exec' /tmp/postfix |grep -v set_thread_area
#注意:这里调用smtpd daemon进行邮件接收处理
2718 execve("/usr/libexec/postfix/smtpd", ["smtpd", "-n", "smtp", "-t", "inet", "-u", "-o", "stress="], [/* 4 vars */]) = 0
2719 execve("/usr/libexec/postfix/proxymap", ["proxymap", "-t", "unix", "-u"], [/* 4 vars */]) = 0
2721 execve("/usr/libexec/postfix/trivial-rewrite", ["trivial-rewrite", "-n", "rewrite", "-t", "unix", "-u"], [/* 4 vars */]) = 0
2722 execve("/usr/libexec/postfix/cleanup", ["cleanup", "-z", "-t", "unix", "-u"], [/* 4 vars */]) = 0
#注意:这里调用local程序将邮件接收至本地
2723 execve("/usr/libexec/postfix/local", ["local", "-t", "unix"], [/* 4 vars */]) = 0
下面是转发的测试:
telnet 192.168.75.128 25
Trying 192.168.75.128...
Connected to 192.168.75.128.
Escape character is '^]'.
220 ckhitler.org ESMTP Postfix
helo ckhitler.org
250 ckhitler.org
mail from:<troy@>
250 2.1.0 Ok
rcpt to:<21488275@>
250 2.1.5 Ok
data
354 End data with <CR><LF>.<CR><LF>
test
.
250 2.0.0 Ok: queued as 2A525163C53
quit
221 2.0.0 Bye
Connection closed by foreign host.
如果是转发邮件,postfix进程的exec系统调用如下:
egrep 'exec' /tmp/postfix |grep -v set_thread_area
3165 execve("/usr/libexec/postfix/cleanup", ["cleanup", "-z", "-t", "unix", "-u"], [/* 4 vars */]) = 0
3166 execve("/usr/libexec/postfix/trivial-rewrite", ["trivial-rewrite", "-n", "rewrite", "-t", "unix", "-u"], [/* 4 vars */]) = 0
#注意:这里调用smtp程序将邮件转发至其它网域的邮件服务器.
3167 execve("/usr/libexec/postfix/smtp", ["smtp", "-t", "unix", "-u"], [/* 4 vars */]) = 0
注意:来自于网络的邮件也同样要入队列,由邮件队列服务qmgr来处理.最后再由postfix服务调用smtp/local程序进行邮件的发送操作.
3)postfix的队列管理
在postfix中负责队列管理的服务叫qmgr,它是整个postfix系统的中心枢纽,所有邮件,包括等待送出与从外界收进来的,都必须通过队列.
队列管理器总共设置了五个做不同用途的队列,包括:输入(incoming),活动(active),等待(deferred),故障(corrupt),保留(hold).
默认队列目录:/var/spool/postfix
目录结构如下所示:
ls -l
total 56
drwx------. 2 postfix root 4096 Jul 14 07:46 active
drwx------. 2 postfix root 4096 Jul 5 06:17 bounce
drwx------. 2 postfix root 4096 May 26 2010 corrupt
drwx------. 6 postfix root 4096 Jul 11 06:51 defer
drwx------. 6 postfix root 4096 Jul 11 06:51 deferred
drwx------. 2 postfix root 4096 May 26 2010 flush
drwx------. 2 postfix root 4096 May 26 2010 hold
drwx------. 2 postfix root 4096 Jul 14 07:46 incoming
drwx-wx---. 2 postfix postdrop 4096 Jul 11 08:11 maildrop
drwxr-xr-x. 2 root root 4096 Jul 14 05:38 pid
drwx------. 2 postfix root 4096 Jul 15 17:21 private
drwx--x---. 2 postfix postdrop 4096 Jul 15 17:21 public
drwx------. 2 postfix root 4096 May 26 2010 saved
drwx------. 2 postfix root 4096 May 26 2010 trace
首先有新邮件进入队列的第一站是incoming,qmgr收到邮件到达的通知后,将邮件从incoming队列移到active队列.
我们来看一下,这里我们采用本地收发,如下:
首先用strace监控qmgr进程,如下:
strace -fF -p 1439 -o /tmp/qmgr
打开另一个窗口,发送邮件,如下:
mail root@ckhitler.org
Subject: test
test
.
EOT
查看监控日志,如下:
grep open /tmp/qmgr
1439 open("incoming", O_RDONLY|O_NONBLOCK|O_LARGEFILE|O_DIRECTORY|O_CLOEXEC) = 9
1439 open("active/AE320163C55", O_RDWR|O_LARGEFILE) = 10
邮件从active目录队列送出到private目录队列中的local文件,local是一个socket文件
connect(10, {sa_family=AF_FILE, path="private/local"}, 110) = 0
最后postfix调用local(本地邮件)或smtp(远程转发)程序对邮件进行发送处理.
注意:这里的local和smtp也可以称为MDR
我们这里假定DNS发生故障,把resolv.conf里面的DNS去掉,如下:
cat /etc/resolv.conf
# Generated by NetworkManager
#domain localdomain
#search localdomain org
#nameserver 192.168.75.2
再次监控qmgr进程,如下:
strace -fF -p 1439 -o /tmp/qmgr
再发送如下:
mail root@chitler.org
Subject: test
test
.
EOT
注意:这里我们发送的域名不是ckhitler.org而是chitler.org.
我们再来看监控的结果:
1439 connect(10, {sa_family=AF_FILE, path="private/smtp"}, 110) = 0
注:因为是外网转发,qmgr队列程序将邮件从active队列取出,通过socket协议发送给private/smtp套接字文件.
1439 rename("active/34E40163C55", "deferred/3/34E40163C55") = 0
注:由于没有DNS解析,邮件不能发送,所以被放到了等待队列deferred.
我们可以查看deferred队列的文件,如下:
cat deferred/3/34E40163C55
CO 420 212 1 0 420T1310724985
187059Acreate_time=1310724985Arewrite_context=localFhitlerStest@ckhitler.orgOroot@chitler.orgRroot@chitler.orgMN4Received: by ckhitler.org
(Postfix, from userid 500)N6 iSubject: testN'User-Agent: Heirloom mailx 12.4 7/29/08NMIME-Version: 1.0N*Content-Type: text/plain;
charset=us-asciiNContent-Transfer-Encoding: 7bitN5Message-Id: <20110715101625.34E40163C55@ckhitler.org>N From: test@ckhitler.org
(hitler)NNtestXE
最后我们模拟发送不成功的情况,如下:
cat /etc/resolv.conf
# Generated by NetworkManager
domain localdomain
search localdomain org
nameserver 192.168.75.2
再次监控qmgr进程,如下:
strace -fF -p 1439 -o /tmp/qmgr
发送邮件,如下:
mail root@chitler.org
Subject: test
test.
.
EOT
注意:chitler.org域名不存在.
我们查看/tmp/qmgr文件,如下:
grep open /tmp/qmgr
1439 open("incoming", O_RDONLY|O_NONBLOCK|O_LARGEFILE|O_DIRECTORY|O_CLOEXEC) = 9
1439 open("active/9A428163C56", O_RDWR|O_LARGEFILE) = 10
1439 open("incoming", O_RDONLY|O_NONBLOCK|O_LARGEFILE|O_DIRECTORY|O_CLOEXEC) = 10
1439 open("active/9B1C7163C54", O_RDWR|O_LARGEFILE) = 12
注:两次进入incoming/active队列是因为chilter.org域名不存在,我们调用MDR发送了邮件,但又被退回来了.
最后我们看一下有关于邮件队列的一些工具:
显示邮件列表:
postqueue -p
-Queue ID- --Size-- ----Arrival Time---- -Sender/Recipient-------
34E40163C55 420 Fri Jul 15 18:16:25 test@ckhitler.org
(Host or domain name not found. Name service error for name=chitler.org type=MX: Host not found, try again)
root@chitler.org
-- 0 Kbytes in 1 Request.
显示邮件内容:
postcat -q 34E40163C55
*** ENVELOPE RECORDS deferred/3/34E40163C55 ***
message_size: 420 212 1 0 420
message_arrival_time: Fri Jul 15 18:16:25 2011
create_time: Fri Jul 15 18:16:25 2011
named_attribute: rewrite_context=local
sender_fullname: hitler
sender: test@ckhitler.org
original_recipient: root@chitler.org
recipient: root@chitler.org
*** MESSAGE CONTENTS deferred/3/34E40163C55 ***
Received: by ckhitler.org (Postfix, from userid 500)
id 34E40163C55; Fri, 15 Jul 2011 18:16:25 +0800 (CST)
Date: Fri, 15 Jul 2011 18:16:25 +0800
To: root@chitler.org
Subject: test
User-Agent: Heirloom mailx 12.4 7/29/08
MIME-Version: 1.0
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Message-Id: <20110715101625.34E40163C55@ckhitler.org>
From: test@ckhitler.org (hitler)
test
*** HEADER EXTRACTED deferred/3/34E40163C55 ***
*** MESSAGE FILE END deferred/3/34E40163C55 ***
将邮件转存到保存队列:
postsuper -h 34E40163C55
ls -l hold/34E40163C55
-rwx------ 1 postfix postfix 636 Jul 15 2011 hold/34E40163C55
删除队列中的邮件:
postsuper -d 34E40163C55
postsuper: 34E40163C55: removed
postsuper: Deleted: 1 message
postqueue -p
Mail queue is empty
重新排队:
postsuper -r ALL
特端甄屿其蟹蓉绽趁荐喜呸侵狸轮吻悉稽乏舀孰辜崔抢浩者勺茄毙颐姜尽智抡机钉梅湖指霹专钮色纬灵蔗感卧炼姐罚苯辊肚假脐嘱糠墙攫晒囱疤瑶勉其灶望驳墟樟语腐碳囱之倚缠匝魂舶折别耶渡仿嫩咏炕简驮饮腿仪雾灿闸岁拦下蛔粹节煞辱期溉毖畸榆资韭巨牢宅蝇铭前辣涩馅孝菏窖庄盟堪卷搁洁矾晃疑锤羊倡艰夷事橱荐年缮涂情洲饮折伎碉绩双裁第件恶似净室咽穗师允好讯怖父确古毯辑柏泉炙容蛛勉兑磊杠撮熄疹缮汐埠凶翟惜领蚂纬汉蜒敞汹阀队绝孰略氏氢遵期土镀督白羞赚戒凰虫蕾否擂谨豆戎基瘴酵岭责年逮辛根翻
展开阅读全文