第1章. SMTP概述
1.1. SMTP在郵件通信中的位置
SMTP,即簡單郵件傳送協議,所對應RFC文檔為RFC821。同http等多數應用層協議一樣,它工作在C/S模式下,用來實現因特網上的郵件傳送。SMTP在整個電子郵件通信中所處的位置如圖 1所示。
圖 1電子郵件的通信過程
可以看出,SMTP是用來將客戶機上的郵件傳送到服務器上。這里的客戶機是指某次連接中的發送方,服務器是指相應的接收方。在講解發送郵件的整個通信過程前,先解釋一下面幾個術語。
1.2. 幾個術語
1.2.1. 郵件
郵件是一種消息的格式,由信封、首部和正文組成。
信封上最重要的是收信人的地址。郵件服務器用這個地址將郵件發送到收信人所在的郵件服務器上。
首部是由用戶代理或郵件服務器添加的一些信息。包括Received、Message-ID、From、Data、Reply-To、X-Phone、X-Mailer、To和Subject等字段。
正文是是發送用戶發給接收用戶報文的內容。RFC 822 規定正文為NVT ASCII文字行。
更為詳細的說明,請參考RFC821和RFC822等協議。
1.2.2. 用戶代理
用戶代理UA(User Agent)是用戶與電子郵件系統的交互接口,一般來說它就是我們PC機上的一個程序。Windows上常見的用戶代理是Foxmail和Outlook Express。
用戶代理提供一個好的用戶界面,它提取用戶在其界面填寫的各項信息,生成一封符合SMTP等郵件標准的郵件,然后采用SMTP協議將郵件發送到發送端郵件服務器。
1.2.3. 郵件服務器
郵件服務器是電子郵件系統的核心,它用來發送和接收郵件。郵件服務器不同於普通PC的是它幾乎是全天工作的,所以它可以在任何時候為用戶提供服務,后面將提到這正是為什么需要郵件服務器的一個重要原因。很多ISP都提供免費的郵件服務器,如126提供smtp.126.com郵件服務器。
郵件服務器向其它郵件服務器轉發郵件也是采用SMTP協議。
1.3. 郵件的收發過程
一般情況下,一封郵件的發送和接收過程如下。
1) 發信人在用戶代理里編輯郵件,包括填寫發信人郵箱、收信人郵箱和郵件標題等等。
2) 用戶代理提取發信人編輯的信息,生成一封符合郵件格式標准(RFC822)的郵件。
3) 用戶代理用SMTP將郵件發送到發送端郵件服務器(即發信人郵箱所對應的郵件服務器)。
4) 發送端郵件服務器用SMTP將郵件發送到接收端郵件服務器(即收信人郵箱所對應的郵件服務器)。
5) 收信人調用用戶代理。用戶代理用POP3協議從接收端郵件服務器取回郵件。
6) 用戶代理解析收到的郵件,以適當的形式呈現在收信人面前。
第2章. SMTP詳解
2.1. 通信過程
一個具體的SMTP通信(如發送端郵件服務器與接收端服務器的通信)的過程如下。
1) 發送端郵件服務器(以下簡稱客戶端)與接收端郵件服務器(以下簡稱服務器)的25號端口建立TCP連接。
2) 客戶端向服務器發送各種命令,來請求各種服務(如認證、指定發送人和接收人)。
3) 服務器解析用戶的命令,做出相應動作並返回給客戶端一個響應。
4) 2)和3)交替進行,直到所有郵件都發送完或兩者的連接被意外中斷。
從這個過程看出,命令和響應是SMTP協議的重點,下面將予以重點講述。
2.2. 命令和響應
2.2.1. 格式
SMTP的命令不多(14個),它的一般形式是:COMMAND [Parameter] <CRLF>。其中COMMAND是ASCII形式的命令名,Parameter是相應的命令參數,<CRLF>是回車換行符(0DH, 0AH)。
SMTP的響應也不復雜,它的一般形式是:XXX Readable Illustration。XXX是三位十進制數;Readable Illustration是可讀的解釋說明,用來表明命令是否成功等。XXX具有如下的規律:以2開頭的表示成功,以4和5開頭的表示失敗,以3開頭的表示未完成(進行中)。
2.2.2. 一個例子
命令和響應的格式是語法,各命令和響應的意思則是語義,各命令和各響應在時間上的關系則是同步。下面將通過一個簡單的SMTP通信過程來說明協議的這三個要素。(每條命令結尾的<CRLF>省略)
C:telnet smtp.126.com 25 /* 以telnet方式連接126郵件服務器 */
S:220 126.com Anti-spam GT for Coremail System (126com[071018]) /* 220為響應數字,其后的為歡迎信息,會應服務器不同而不同*/
C:HELO smtp.126.com /* HELO 后用來填寫返回域名(具體含義請參閱RFC821),但該命令並不檢查后面的參數 */
S:250 OK
C: MAIL FROM: bripengandre@126.com /* 發送者郵箱 */
S:250 … ./* “…”代表省略了一些可讀信息 */
C:RCPT TO: bripengandre@smail.hust.edu.cn /* 接收者郵箱 */
S:250 … ./* “…”代表省略了一些可讀信息 */
C:DATA /* 請求發送數據 */
S:354 Enter mail, end with "." on a line by itself
C:Enjoy Protocol Studing
C:.
S:250 Message sent
C:QUIT /* 退出連接 */
S:221 Bye
分析上面的過程可參考注釋進行,這里要補充如下幾點。
1) “C:”開頭的行(不包括"C:")是客戶端的輸入,而以“S:”開頭的行(不包括"S:")則是服務器的輸出。
2) 上述的命令並不一定會一次性成功,服務器會返回錯誤響應,客戶端應該按照協議規定的時序,來輸入后續的命令(或重復執行失敗的命令,或重置會話,或退出會話等等)。
2.2.3. 常用命令
SMTP命令不區分大小寫,但參數區分大小寫,有關這方面的詳細說明請參考RFC821。常用的命令如下。
HELO <domain> <CRLF>。向服務器標識用戶身份發送者能欺騙,說謊,但一般情況下服務器都能檢測到。
MAIL FROM: <reverse-path> <CRLF>。<reverse-path>為發送者地址,此命令用來初始化郵件傳輸,即用來對所有的狀態和緩沖區進行初始化。
RCPT TO:<forward-path> <CRLF>。 <forward-path>用來標志郵件接收者的地址,常用在MAIL FROM后,可以有多個RCPT TO。
DATA <CRLF>。將之后的數據作為數據發送,以<CRLF>.<CRLF>標志數據的結尾。
REST <CRLF>。重置會話,當前傳輸被取消。
NOOP <CRLF>。要求服務器返回OK應答,一般用作測試。
QUIT <CRLF>。結束會話。
VRFY <string> <CRLF>。驗證指定的郵箱是否存在,由於安全方面的原因,服務器大多禁止此命令。
EXPN <string> <CRLF>。驗證給定的郵箱列表是否存在,由於安全方面的原因,服務器大多禁止此命令。
HELP <CRLF>。查詢服務器支持什么命令。
2.2.4. 常用響應
常用的響應如下所示,數字后的說明是從英文譯過來的。更詳細的說明請參考RFC821。
501參數格式錯誤
502命令不可實現
503錯誤的命令序列
504命令參數不可實現
211系統狀態或系統幫助響應
214幫助信息
220<domain>服務就緒
221<domain>服務關閉
421<domain>服務未就緒,關閉傳輸信道
250要求的郵件操作完成
251用戶非本地,將轉發向<forward-path>
450要求的郵件操作未完成,郵箱不可用
550要求的郵件操作未完成,郵箱不可用
451放棄要求的操作;處理過程中出錯
551用戶非本地,請嘗試<forward-path>
452系統存儲不足,要求的操作未執行
552過量的存儲分配,要求的操作未執行
553郵箱名不可用,要求的操作未執行
354開始郵件輸入,以"."結束
554操作失敗
第3章. SMTP的擴充
3.1. SMTP的缺點
從2.2.2的例子可以看出,SMTP至少還有如下缺點。
1) 命令過於簡單,沒提供認證等功能。
2) 只傳送7位的ASCII碼,不能傳送二進制文件。
針對缺點1),標准化組織制定了擴充的SMTP(即ESMTP),對應的RFC文檔為RFC1425。針對缺點2),標准化組織在兼容SMTP的前提下,提出了傳送非7位ASCII碼的方法,對應的RFC文檔有兩個:郵件首部的擴充對應於RFC1522,郵件正文的擴充對應與RFC1521(即MIME)。
3.2. ESMTP
ESMTP最顯著的地方是添加了用戶認證功能。如果用戶想使用ESMTP提供的新命令,則在初次與服務器交互時,發送的命令應該是EHLO而不是HELO。先來看一個例子。(注意每行數據末尾的\r\n)
- C:telnet smtp.126.com 25//以telnet方式連接126郵件服務器
- S:220 126.com Anti-spam GT for Coremail System (126com[071018])//220為響應數字,其后的為歡迎信息,會應服務器不同而不同
- C:EHLO smtp.126.com \r\n//除了HELO所具有的功能外,EHLO主要用來查詢服務器支持的擴充功能
- S:250-mail
- S:250-AUTH LOGIN PLAIN
- S:250-AUTH=LOGIN PLAIN
- S:250 8BITMIME//最后一個響應數字應答碼之后跟的是一個空格,而不是'-'
- C:AUTH LOGIN\r\n//請求認證
- S:334 dxNlcm5hbWU6//服務器的響應——經過base64編碼了的“Username”
- C:Y29zdGFAYW1heGl0Lm5ldA==\r\n//發送經過BASE64編碼了的用戶名
- S:334 UGFzc3dvcmQ6//經過BASE64編碼了的"Password:"
- C:MTk4MjIxNA==\r\n//客戶端發送的經過BASE64編碼了的密碼
- S:235 auth successfully//認證成功
- C: MAIL FROM: bripengandre@126.com\r\n//發送者郵箱
- S:250 … .//“…”代表省略了一些可讀信息
- C:RCPT TO: bripengandre@smail.hust.edu.cn\r\n//接收者郵箱
- S:250 … .//“…”代表省略了一些可讀信息
- C:DATA\r\n//請求發送數據
- S:354 Enter mail, end with "." on a line by itself
- C:Enjoy Protocol Studing
- C:.
- S:250 Message sent
- C:QUIT //退出連接
- S:221 Bye
C:telnet smtp.126.com 25//以telnet方式連接126郵件服務器 S:220 126.com Anti-spam GT for Coremail System (126com[071018])//220為響應數字,其后的為歡迎信息,會應服務器不同而不同 C:EHLO smtp.126.com \r\n//除了HELO所具有的功能外,EHLO主要用來查詢服務器支持的擴充功能 S:250-mail S:250-AUTH LOGIN PLAIN S:250-AUTH=LOGIN PLAIN S:250 8BITMIME//最后一個響應數字應答碼之后跟的是一個空格,而不是'-' C:AUTH LOGIN\r\n//請求認證 S:334 dxNlcm5hbWU6//服務器的響應——經過base64編碼了的“Username” C:Y29zdGFAYW1heGl0Lm5ldA==\r\n//發送經過BASE64編碼了的用戶名 S:334 UGFzc3dvcmQ6//經過BASE64編碼了的"Password:" C:MTk4MjIxNA==\r\n//客戶端發送的經過BASE64編碼了的密碼 S:235 auth successfully//認證成功 C: MAIL FROM: bripengandre@126.com\r\n//發送者郵箱 S:250 … .//“…”代表省略了一些可讀信息 C:RCPT TO: bripengandre@smail.hust.edu.cn\r\n//接收者郵箱 S:250 … .//“…”代表省略了一些可讀信息 C:DATA\r\n//請求發送數據 S:354 Enter mail, end with "." on a line by itself C:Enjoy Protocol Studing C:. S:250 Message sent C:QUIT //退出連接 S:221 Bye
對於這個例子有如下幾點說明。
1) 只是一個示意性的過程,再輸入用戶名、密碼時需采用base64編碼,這需要專門的計算,所以在telnet終端上模擬比較麻煩。
2) 認證過程有很多種,有基於明文的認證,也有基於MD5加密的認證,這里給出的只是一個示意性的過程。
3) EHLO對於具體服務器,響應會不同,關鍵字“8BITMIME”用來說明服務器是否支持正文中傳送8位ASCII碼,而以“X”開頭的關鍵字都是指服務器自定義的擴充(還沒納入RFC標准)
更詳細的說明,請參看RFC1425。
3.3. 郵件首部的擴充
首部通過兩種編碼方式來支持傳送非7位ASCII碼。它首先通過一個如下格式的編碼字來表明所用的編碼方式。
=?charset?encoding?encoded-text?text
charset是字符集規范。有效值是兩個字符串us-ascii和iso-8859-x,其中x 是一個單個數字,例如iso-8859-1中的數字為“ 1”。
encoding是一個單個字符用來指定編碼方法,支持兩個值。
Q代表quoted-printable(可打印)編碼。任何要發送的字符若其第8比特置1則被作為3個字符發送:第1個是字符是“=”,后面的兩個字符對應於字符的十六進制表示。例如對於二進制碼11111111,其對應的十六進制表示為“FF”,所以對應的編碼位“=FF”。為了能夠傳輸“=”,“=”的編碼方式與第8比特置1的字符相同,因為其二進制代碼為00111101,所以對應的編碼為“=3D”。可以看出這種編碼方式的開銷達200%,所以只適合傳送只含有少量非7位ASCII碼的文本。
B代表base64編碼。它的編碼方法是先將二進制代碼划分為一個24bit長的單元,然后將這24 bit單元划分為4個6 bit組。每個組按圖 2所示的方法轉換成ASCII碼。
圖 2 base64映射表
可以看出這種映射方法是這樣的:0-25依次映射成A-Z,26-51依次映射成a-z,52-61依次映射成數字0-9,然后62映射成+,63映射成/。
對於二進制代碼01001001 00110001 01111001,先將其划分成4個6 bit組,即010010 0100011 000101 111001。接着按圖 2所示的映射表,可得到base64編碼為:STF5。可以看出,這種編碼方式的開銷是25%,相對quoted-printable編碼來說,它更適合用來傳送含大量非7位ASCII碼的二進制文件。
3.4. 正文的擴充
正文的擴充主要是使正文不僅可以傳輸NVT ASCII字符,而且可以傳輸任意字符,對應的文檔為RFC1511(即MIME)。
MIME全稱為“Multiple Internet Mail Extensions”, 比較確切的中文名稱為“多用途互聯網郵件擴展”。它通過新增一些郵件首部字段、郵件內容格式和傳送編碼,使得其成為一種應用很廣泛的可以傳輸多媒體的電子郵件規范。
更詳細的說明請參看另一篇文章《MIME協議分析》和RFC1511。
第4章. 常見的疑問
4.1. 為什么需要SMTP服務器
一般的PC資源不夠,處理能力不夠,不可能全天候地連接在因特網上來收發郵件。所以使用SMTP服務器,可以讓多個用戶共用服務器,有效地降低了成本。
4.2. SMTP和郵件格式的關系
如前所述,SMTP是客戶機向服務器發送郵件時所使用的協議,其核心是2.2中所述的命令和響應,至於它命令和響應中所帶的參數采用什么格式,則是依賴於其他標准的。例如DATA后所帶的參數,則應遵循郵件格式標准RFC822.
SMTP和郵件格式的關系可用這么一個例子來說明。甲與乙書信往來,甲通過郵局向乙發信,郵局間轉交郵件可看成使用了SMTP協議,至於書信的格式則會因為地區習慣等的不同而不同(中國人的書信格式和美國人的書信格式不同),這個書信格式則可看成是郵件格式標准。
應當認識到不能孤立地看待協議,各個協議之間往往存在着耦合關系,但為了分析方便,我們在具體敘述某個協議時,只能抓住主要矛盾——主要闡述單個協議。
4.3. 瀏覽器發送郵件用的什么協議
瀏覽器如IE、Maxthon可通過登陸用戶郵箱,來收發郵件,這是怎樣實現的?例如bripengandre@126.com可通過登陸www.126.com來收發郵件。
這個過程是這樣的:bripengandre@126.com在www.126.com提供的郵件頁面上填寫的相應信息(如發信人郵箱、收信人郵箱等),通過http協議被提交給126服務器;126服務器根據這些信息組裝一封符合郵件規范的郵件(就像用戶代理一樣);然后smtp.126.com通過SMTP協議將這封郵件發送到接收端郵件服務器。
可以看出,瀏覽器發送郵件只是用戶代理的功能直接放到郵件服務器上去做了,至於郵件服務器間發送郵件還是采用的SMTP協議。我們看問題,如果有必要還是要適當地透過現象看本質。
4.4. 如何用實驗驗證SMTP的通信過程
1) 可以通過ethereal等協議分析軟件來抓包分析協議。
2) 可以利用socket編程實現SMTP的通信過程。
3) 可以利用用戶代理來查看一封郵件的原始編碼。例如在Foxmail中,可以選擇郵件列表右鍵菜單的“原始信息”進行查看。
第5章. 分析方案
ID |
Protocol |
Captured contents |
||||||
user name |
password |
sender |
receiver |
subject |
contents |
attachments |
||
4 |
smtp |
√ |
√ |
√ |
√ |
√ |
√ |
|
表 1給出了協議分析要求。容易看出,獲取各個字段是比較容易的。我們可以抓取客戶端與服務器端的交互信息,然后根據各命令字或響應字來提取出我們想要的字段。例如,要獲取user name,我們只需檢測到服務器端要求客戶端發送用戶名這個時候,然后提取這之后客戶端的發送信息即可。需要說明的是,雖然客戶端與服務端交互的信息可能經過了編碼或加密,但我們仍能夠通過解碼或解密來獲得所需要的信息。