基於raw smtp用php實現簡易的smtp用戶端

來源:互聯網
上載者:User

任務需求:一個會議投稿系統,在作者提交論文摘要時自動向作者發送一封確認郵件。
(註:投稿系統使用的是myreview
)
仔細考慮一下,這個任務可以有以下幾種方式完成:

  1. 在伺服器上配置sendmail服務,使用php中的的mail函數發送郵件;
  2. 在伺服器上安裝mutt + msmtp,或者在伺服器上安裝其它的smtp用戶端程式,使用smtp客戶程式發送郵件
  3. 寫一個C/S程式,每當有作者提交論文時,伺服器上的client端程式即把郵件內容組合好,然後通知在我的電腦上監聽的server端程式,server端程式於是調用我的電腦上的郵件發送指令碼來發送郵件。這個c/s程式可以用java, c, php等來寫
  4. 用nc或者telnet使用raw smtp協議來發送郵件

從用linux以來就對sendmail這個服務沒有好感,方法一放棄。也不想在伺服器上安裝附加的軟體,所以方法二放棄。對比起來,方法四比方法三更簡單一些。

方法四的思路:使用tcpdump攔截一次完整的smtp發信過程,然後寫程式類比這個過程。
首先啟動tcpdump對來往的資料包進行監控:

[root@jcwkyl review]# tcpdump -vvv -t -X -s 1500 -w data host mail.jlu.edu.cn
-vvv表示用最詳細的格式來記錄捕獲的資料包,-t表示不記錄時間戳記,-X表示用hex和ascii顯示資料包內容,-s表示顯示長度為1500而不是預設的68,-w表示輸出到data檔案中。

同時,發送一封郵件。
[whb@jcwkyl bash]$ echo "mail content" | mutt -s "test subject" jcwkyl@gmail.com

看這邊tcpdump:
[root@jcwkyl review]# tcpdump -vvv -t -X -s 1500 -w data host mail.jlu.edu.cn
tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 1500 bytes
Got 34

捕獲了34個資料包。查看記錄檔案:
[root@jcwkyl review]# tcpdump -vvv -t -X -s 1500 -r data | less

輸出很多,從其中可以看到資料往來的格式,截取其中三個資料包為例:

<!--
@page { size: 8.5in 11in; margin: 0.79in }
P { margin-bottom: 0.08in }
-->

IP (tos 0x0, ttl 
61, id 51365, offset 0, flags [DF], proto: TCP (6), length: 63)
mail.jlu.edu.cn.sm
tp > jcwkyl.gridlab.57415: P, cksum 0x9765
(correct), 1:12(11) ack 1 win 1448 <nop,nop,timestamp 203
2907544
165074058>
        0x0000: 
4500 003f c8a5 4000 3d06 577f cac6 1038 
E..?..@.=.W....8
       
0x0010:  0a3c 385a 0019 e047 c0c6 7dbd eeb9 52bd 
.<8Z...G..}...R.
       
0x0020:  8018 05a8 9765 0000 0101 080a 792b b518 
.....e......y+..
       
0x0030:  09d6 d48a 3232 3020 4553 4d54 500d 0a   
....220.ESMTP..
IP (tos 0x0, ttl  64, id 34360, offset 0,
flags [DF], proto: TCP (6), length: 52) jcwkyl.gridlab.574
15 >
mail.jlu.edu.cn.smtp: ., cksum 0xebee (correct), 1:1(0) ack 12 win 46
<nop,nop,timestamp 165074
058 2032907544>
       
0x0000:  4500 0034 8638 4000 4006 96f7 0a3c 385a 
E..4.8@.@....<8Z
       
0x0010:  cac6 1038 e047 0019 eeb9 52bd c0c6 7dc8 
...8.G....R...}.
       
0x0020:  8010 002e ebee 0000 0101 080a 09d6 d48a 
................
       
0x0030:  792b b518                               
y+..
IP (tos 0x0, ttl  64, id 34361, offset 0, flags [DF],
proto: TCP (6), length: 68) jcwkyl.gridlab.574
15 >
mail.jlu.edu.cn.smtp: P, cksum 0x1dcb (incorrect (-> 0x8015),
1:17(16) ack 12 win 46 <nop,nop,t
imestamp 165074058
2032907544>
        0x0000: 
4500 0044 8639 4000 4006 96e6 0a3c 385a 
E..D.9@.@....<8Z
       
0x0010:  cac6 1038 e047 0019 eeb9 52bd c0c6 7dc8 
...8.G....R...}.
       
0x0020:  8018 002e 1dcb 0000 0101 080a 09d6 d48a 
................
       
0x0030:  792b b518 4548 4c4f 206c 6f63 616c 686f 
y+..EHLO.localho
       
0x0040:  7374 0d0a                               
st..


第一個資料包是伺服器發給我的,內容就是:220 ESMTP,第二個資料包沒有內容不用管它,第三個資料包也是我發給伺服器的,資料內容是: EHLO/x20localhost/x0d/x0a
就這樣在這34個資料包裡找,把所有的從我發到郵件伺服器的資料內容提取出來,最後的結果如下:
EHLO localhost/r/n
AUTH LOGIN/r/n
<para1>
<para2>
MAIL FROM:<whb@jlu.edu.cn>/r/n
RCPT TO:<jcwkyl@gmail.com>/r/n
DATA/r/n
Date: Fri, 1 5 Jan 2010 17:10 :06 +0800 /r/n
From: Email Address <addr@jlu.edu.cn>/r/n
To: jcwkyl@gmail.com/r/n
Subject: test subject/r/n
Message-ID: <20100115091 006 GA12962@jcwkyl.gridlab>/r/n
Mime-Version: 1.0/r/n
Content-Type: text/plain; charset=us-ascii/r/n
Content-Disposition: inline/r/n
User-Agent: Mutt/1.4.2.2i/r/n/r/n
mail content/r/n/x2e/r/n
QUIT/r/n
以上就是整個過程。注意郵件內文開始前有兩個/r/n,郵件內文以/r/n/x2e/r/n結束。/r就是十六進位的/x0d,/n就是十六進位的/x0a。
上面的<para1>和<para2>本來是兩個字串,從上下文猜測是用來身分識別驗證的,這裡用<para1>和<para2>代表。

接下來就是寫程式類比這個過程,因為會議投稿系統是用php寫的,所以就用php寫程式類比這個郵件發送過程,最終的代碼如下:
// Encapsulate the mail function<br />function SendMail ($to, $subject, $mail,<br /> $from="", $replyTo="", $cc="")<br />{<br /> // Construct the header<br /> $header = "";<br /> if (!empty($from)) $header .= "From: $from/r/n";<br /> if (!empty($cc)) $header .= "Cc: $cc/r/n";<br /> if (!empty($cc)) $header .= "Reply-to: $replyTo/r/n";<br /> // Add the signature file<br /> /* if (file_exists("Signature"))<br /> {<br /> $mail .= readfile ("Signature");<br /> }<br /> */<br /> // Use the standard mail function<br /> // Sometimes the -f option does not work<br /> //mail ($to, $subject, $mail, $header, "-f $from");<br /> //mail ($to, $subject, $mail, $header);<br /> $protocol_data = array(<br /> 'EHLO' => 'EHLO localhost',<br /> 'AUTH' => 'AUTH LOGIN',<br /> 'PARA1' => '<para1>', // 應該替換成自己的身份認證字串<br /> 'PARA2' => '<para2>',<br /> 'MAILFROM' => 'MAIL FROM:<addr@jlu.edu.cn>',<br /> 'RCPTTO' => 'RCPT TO:<'.$to.'>',<br /> 'DATA' => 'DATA'<br /> );<br /> $mailcontent = "From: From Address <addr@jlu.edu.cn>/r/n";<br /> $mailcontent .= 'To: '.$to."/r/n";<br /> $mailcontent .= 'Subject: '.$subject."/r/n/r/n";<br /> $mailcontent .= $mail."/r/n/x2e/r/n";<br /> $mailcontent .= "QUIT/r/n";<br /> $handler = popen("/usr/bin/nc mail.jlu.edu.cn 25", "w");<br /> if(!$handler) {<br /> echo '<mce:script type="text/javascript"><!--<br />alert("error");<br />// --></mce:script>';<br /> }<br /> foreach($protocol_data as $value) {<br /> fwrite($handler, $value."/r/n");<br /> }<br /> fwrite($handler, $mailcontent."/r/n");<br /> pclose($handler);<br />}<br />
被注釋掉的mail函數是投稿系統以前的代碼,mail函數之後是自己寫的類比郵件發送過程的代碼。同樣的功能很容易用其他語言實現。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.