RFC822:电子邮件的基本框架

11 电子邮件的结构

如同传统信件,电子邮件也是由信封和信件内容两部分所构成。但是电子邮件不是写在纸上的东西,而是由计算机编辑、传送和显示的一串文字字符。一封电子邮件如同我们在Windows“记事本”中看到的纯文本内容,但是其中的字符是机器“写”上去的,而不是用户输入、编辑的。

 

释疑:你可能不同意这种说法,因为邮件内容,包括收件人地址、转发地址,肯定必须由用户输入。但是我们这里讨论的是在机器中传送、存储的电子邮件,用户一般通过某种电子邮件应用程序(如Outlook ExpressFoxMail等)收发、编写电子邮件,这些程序按照用户输入的信息自动产生符合RFC822MIME标准的邮件,然后才发送出去。

 

一封电子邮件的内容是纯文本的,其中开头若干行是信件“信封”,然后是一个空行(即只有回车换行字符的一行),其后是信件正文。“信封”、信件正文都是一行行的文字,邮件中的第一个空行是“信封”部分的终结标志,其后是邮件正文,邮件正文可以包含空行,也可以没有空行,这无关紧要。RFC822规定,所有的字符都必须是ASCII字符,或者更准确地说,必须是ASCII编码的字符。注意RFC822是指定字符编码的,符合RFC822标准的电子邮件,其中所有的字符只能使用ASCII编码,不能是其它编码。

RFC822对于信件正文没有什么规定,只是要求必须由ASCII字符组成,而“信封”上的信息则是要由机器处理的,因而有着严格的语法规定。“信封”的正式名称是邮件头(header),邮件头由若干“头段”(header field)组成,每一头段的格式为:

    field-name : [field-body] CRLF

(注:在本章中,当描述语法格式时,我们用斜体字表示一个语法成分的名称,用方括号表示可选项,即,包含在方括号中的语法成分可用也可不用,用粗体表示必须照样书写的字符。)

其中field-name是段名,RFC822定义了常用的段名及其语义,应用程序也可以定义自己的段名及其语义,但不得与已定义的段名同名。field-body是段体,即段的具体内容。段名和段体用一个冒号分隔,最后用CRLF(回车换行)结束整个段。

    以下是一些邮件头段的例子:

    From: George Jones <Group@Host>
    Sender: Secy@Host

    Reply-To: Secy@Host

其中每一行为一个头段,其中“From”、“Sender”和“Reply-To”是段名,段名不区分大小写,每行冒号右边的文字是段体,段体的语法、含义是有规定的。

    一个头段逻辑上就是一行,但是当文字较长时,也允许换行。换行可在任何一个空格位置进行,但是要求换行之后在新行开头至少必须有一个空格或Tab,表示本行是接续上一行的。这种换行称为“折叠”。一个头段可以折叠为若干行,应用程序进行语法分析时,应去掉折叠,将每一段都整理为一行,以便分析。

    例如:Received: from m15-40.126.com (220.181.15.40)CRLF by localhost with SMTP;

    文本编辑窗口显示出来的效果:

    Received: from m15-40.126.com (220.181.15.40)

 by localhost with SMTP;

    RFC822定义的头段按其功能可分为源地址、目的地址、日期和可选段四大部分,以下分别介绍各个部分。

12 源地址

描述信件来源的段名有FromSenderReturn-pathReceivedReply-To,它们的语法如下:

    From: mailbox

    Sender: mailbox

    Return-path: < [route] local-part@domain >

    Reply-To: mailbox

    Received[from domain][by domain][via atom][with atom]

[id msg-id][for local-part@domain]

; date-time

From用来指出发信人是谁,段体mailbox发信人的邮箱地址,mailbox可以采用两种形式之一,一种是local-part@domain,其中local-part是用户帐号名称,domain是邮件服务器的域名。另一种形式是发信人名称后面再加上尖括号括起来的邮件地址,例如

  From:  George Jones <Group@Host>

    邮箱地址中的local-part一般不能使用圆括号、尖括号、方括号、逗号、分号、冒号、@、斜杠和空格这些字符,如果要使用这些字符,则整个local-part部分必须用双引号括起来。

    如果某些程序使得信件不是从写信人的邮箱寄出,而是从另一个邮箱寄出,则必须用Sender再加上From,这时Sender的段体是寄出的邮箱地址,而From的段体是写信人的邮箱地址

    一封电子邮件可能要经过若干次转发才能到达目的地。最后的转发站(转发邮件的计算机)可以用Return-path指出回送邮件给发信人的路由Return-path段体中的可选项route指出路由,其形式为

    @domain_1, @domain_2, ... ..., @domian_n

其中各个domain_i是邮件服务器的域名。段体中最后的local-part@domain则是发信人的邮箱地址。

    Reply-To则是由最初发出邮件的机器填写的头段,它直接指出信件的回复地址

    当邮件经过多次中转转发时,每一个转发站都必须在邮件开头加上Received头段,说明邮件何时从何地来,往何处去。Received段体中的可选项用来记录这些信息:

from domain说明邮件从何处来,domain发来邮件的主机IP地址、域名或主机名

by domain指出邮件接收者,domain接收邮件的主机IP地址、域名或主机名

via atom指出邮件的物理传输路径,例如通过互联网或电话网,atom是这种物理路径的名称。

with atom指出邮件的传输协议,例如SMTPESMTPatom是协议名称。

id msg-id一些转发站可能会将邮件排队,msg-id指出邮件的内部ID

for local-part@domain指出邮件的目的地local-part@domain是邮箱地址

Received段最后要加上分号和时间戳date-time记录邮件的接收时间。时间戳的格式将在1.4节详述。

    一些邮件系统允许邮件接收者将接收到的邮件转发给他人,并保留原有的邮件头。这种情况下为了与原来邮件头中的头段相区别,新加上的头段的段名前要加上Resent-,即Resent-From Resent-Sender Resent-Reply-To,它们的作用与FromSenderReply-To相同。

    在邮件头中各段的段体中,可以插入注释。注释用一对圆括号括起来,内容可以是任何字符串。注释是给人看的,在语法上没有其它意义。

    1:以下是一封电子邮件邮件头中的片断:

Received: from unknown (HELO mail.scnu.edu.cn) (unknown@202.116.32.11)

 by 202.116.32.4 with SMTP; Fri, 09 Feb 2007 14:22:51 +0800

Received: from m15-40.126.com (220.181.15.40)

  by localhost with SMTP; 9 Feb 2007 06:32:51 -0000

Received: from 192.168.208.33 by webmail-app40 (Coremail) ;

Fri, 9 Feb 2007 14:30:58 +0800 (CST)

From: someone@126.com

从这些头段可以知道邮件来自someone@126.com,首先被主机webmail-app40收到,时间是200729,星期五,当地时间143058秒,当时邮件来自主机192.168.208.33

然后邮件被主机localhost接收到,邮件来自主机m15-40.126.com,邮件通过SMTP协议接收,接收到邮件的时间是200729,格林威治标准时间63251秒。

最后邮件被主机202.116.32.4收到,邮件来自未知主机unknown,邮件通过SMTP协议接收,接收到邮件的时间是200729,当地时间142251秒。

13目的地址

描述邮件目的地址的头段有Toccbcc,它们的语法如下:

    To: address

    cc: address

    bcc: address

其中address是一个或多个邮箱地址,在多个邮箱地址的情况下,邮箱地址彼此用逗号分隔。

To用来指出邮件的第一接收者,cc用来指出邮件的第二接收者。当邮件同时抄送给多人时,需要用到cc,其段体指出抄送的收件人。bcc是英文Blind Carbon Copy的缩写,意思是隐藏地将邮件同时抄送给其他人,而不让收件人知道。当邮件头包含bcc段时,该段的内容将不会传送给第一和第二收件人。至于bcc段中的收件人,他们可能看到bcc段的内容,也可能看不到,这取决于传送邮件的系统。

当邮件被转发时,可用Resent-ToResent-ccResent-bcc指出邮件的接收者和抄送接收者,它们的语法语义与Toccbcc分别对应。

 

释疑:注意抄送和转发的区别。抄送是在发送邮件时同时发送给多人,转发是收信人收到邮件后,再传送给其他人。

14 日期

Date头段用来指出邮件的创建日期,其语法是

     Date: date-time

其中date-time指出日期时间,其语法为

         [day , ] date time

其中可选项day表示星期几,这一项后面要加逗号。date表示日期(年、月、日),time表示时间(时、分、秒)。

    星期几用英文缩写表示,即MonTueWedThuFriSatSun

    日期的格式为dd month yyyy,其中dd是表示“日”的两位数字,month是月份,用3个字母的英文缩写,即JanFebMarApr等等,yyyy是年份,用4位数字表示。

    时间的格式是时分秒加时差。时分秒的格式是hh:mm:ss,分别用两位数字表示时、分和秒,采用24小时记时制。日期时间必须用当地时间,但邮件接收者和沿途的转发站未必在同一时区,因此最后加上“+hhmm或“-hhmm的时差部分,说明当地时间与格林威治标准时间相差(增加或减少)几小时几分钟。

例如,Fri, 02 Mar 2007 22:13:00 +0800表示200732星期五22130秒,当地时间比格林威治标准时间多8小时0分。

    以上介绍的日期事件格式其实是RFC1123制定的格式。RFC822所制定的日期格式中,年份只用两位数字,这显然将导致“千年虫”问题。RFC1123修正了RFC822的规定,年份改用4位数字。另外,RFC822的时间格式可以不用数字时差,而改用北美地区的时区符号说明当地时间,例如“EST”、“EDT”等,RFC1123建议采用数字时差。

    除了Date段,Received段最后的时间戳也使用这种日期时间格式。

当邮件被转发时,可添加Resent-Date以说明日期时间,其用法与Date相同。

15 可选头段

     可选头段包括Message-IDResent-Message-IDIn-Reply-ToReferences Keywords SubjectCommentsEncrypted

Message-ID用来为邮件指定一个唯一标识,其语法为

         Message-ID: msg-id

其中msg-id形式为<local-part@domian>,此处local-part是一串作为邮件ID的字符串domian是指定这个ID的主机域名ID的唯一性由指定ID的主机负责。当一封邮件经过若干邮件服务器传送时,这些邮件服务器可以各自为这封邮件指定不同的ID,以便服务器内部管理邮件。

    当邮件被转发时,可用Resent-Message-ID指定ID,其用法和Message-ID相同。

    In-Reply-To用来指出当前邮件是对哪一封邮件的答复,其语法为

         In-Reply-To:  *( phrase / msg-id )

    (注: 语法中的斜杠“/”表示“或者”,“*”表示可重复任意次)

其中phrase是一串字符串,msg-id是邮件ID,两者可以单独使用,也可以混合使用,目的是标识所答复的邮件。例如:

    In-Reply-to: <000601c87ead$elcd28a0$0200a8c0@c220>

    References头段用来指出与当前邮件相关的有哪些邮件。例如,当我们利用电子邮件讨论某个问题时,可能来来往往会有很多封邮件,这时References可用来指出当前邮件涉及到哪些邮件。References的语法是:

         References:  *( phrase / msg-id )

例如:

    References: <000601c87ead$elcd28a0$0200a8c0@c220>

      <15662676.1412391204620030868.JavaMail.coremail@bj163app125.163.com>

    Keywords头段用来指出当前邮件中的关键词,其语法是:

         Keywords: #phrase

    (注: “#”表示其后的语法项可多次出现,彼此之间用逗号分隔)

其中phrase是关键词,可以列出多个关键词,彼此之间用逗号分隔。

     Subject头段用来指出邮件的标题,其语法是

         Subject: text

其中text是任意的文字。

     Comments头段是注释,其语法为

         Comments: text

注意Comments是整个段作为注释,在其它段中也可以插入注释,插入的注释内容只要用圆括号括起来即可。

    Encrypted头段用来通知邮件接收者如何解密。邮件正文可以加密,接收方为了解密,可能需要知道发送方使用何种加密手段。Encrypted的语法是

         Encryptedwd1, wd2

其中wd1指出加密程序的名称,wd2用来帮助接收者选择密钥,例如,指出内部密钥表的索引。

    邮件正文可以加密,但是邮件头不能加密,因为邮件头中的信息是传送邮件的主机必须知道的,如果加密,邮件将无法传送。

    Encrypted已经被PEMPrivacy Enhanced Messaging)淘汰,有兴趣的读者可查阅RFC1421RFC1422RFC1423RFC1424

16 用户自定义段

    以上介绍了RFC822所定义的头段,以后还可能有新的RFC文件来定义一些新头段,新头段的段名必须在NIC(Network Information Center, SRI International, Menlo Park, California)登记。登记的新头段段名不允许以“X-”或“x-”开头。

    用户可以自由使用自己定义的头段,但这种段的段名不能与RFC822所定义的段名相同,也不能与将来登记的新头段段名相同,否则可能被排斥。为此,用户自定义的段名可用“X-”或“x-”开头,因为RFC822已承诺新增加的段名不会以这两个字母开头。

17 例子和解释

2:以下是一封垃圾邮件的全文,略去信件正文。虽然内容是垃圾,但语法格式是正确的(否则无法传播),废物利用,权当示例:

Received: (eyou send program); Mon, 26 Feb 2007 03:35:50 +0800

Message-ID: <372432150.27375@scnu.edu.cn>

Received: from unknown (HELO downboy.com) (unknown@83.7.213.189)

 by 202.116.32.4 with SMTP; Mon, 26 Feb 2007 03:35:50 +0800

Received: from domy5ly6i69dod ([89.131.200.82])

        by 83.7.213.189 (7.81.9/7.81.9) with SMTP id 8K7gQ4BTGPRFCJ;

        Sun, 25 Feb 2007 20:44:10 +0100

Message-ID: <001801c7591d$b39b46d0$00deed84@domy5ly6i69dod>

From: "Belinda May" <teatomic@downboy.com>

To: "somebody" <somebody@scnu.edu.cn>

Subject: expenditure

Date: Sun, 25 Feb 2007 20:42:34 +0100

 

Some garbage text here ... ..., omitted.

从邮件头可以看到,邮件创建于2007-2-2520:42:34,当地时间比格林威治时间多1小时。邮件标题是“expenditure”,收件人地址是somebody@scnu.edu.cn,发信人地址是teatomic@downboy.com。主机domy5ly6i69dod为邮件指定了一个ID001801c7591d$b39b46d0$00deed84

邮件最先被IP地址为83.7.213.189的主机收到,时间是2007-2-2520:44:10当地时间比格林威治时间多1小时。信件来自名称为domy5ly6i69dod的主机,通过SMTP协议接收。信件在该主机上的id8K7gQ4BTGPRFCJ

随后邮件被IP地址为202.116.32.4的主机接收到,时间是2007-2-2603:35:50,当地时间比格林威治时间多8小时。信件来自未知的主机,通过SMTP协议接收。主机scnu.edu.cn为邮件指定了ID372432150.27375

最后信件被最终转发程序接收,时间是2007-2-2603:35:50,当地时间比格林威治时间多8小时。

    信件中有一个空行,空行之前为邮件头,之后是信件正文。