Skip to main content

4. The SMTP Specifications (SMTP规范)

4.1. SMTP Commands (SMTP命令)

4.1.1. Command Semantics and Syntax (命令语义和语法)

SMTP命令定义了用户请求的邮件传输或邮件系统功能。SMTP命令是由<CRLF>终止的字符串。命令本身是字母字符,如果后面跟随参数则由<SP>终止,否则由<CRLF>终止。(为了改善互操作性,SMTP接收方应该 (SHOULD) 容忍终止<CRLF>之前的尾随空白。) 邮箱的local-part的语法必须 (MUST) 符合接收方站点约定和第4.1.2节中指定的语法。下面讨论SMTP命令。SMTP应答在第4.2节中讨论。

邮件事务涉及几个作为不同命令参数传递的数据对象。反向路径 (reverse-path) 是MAIL命令的参数,正向路径 (forward-path) 是RCPT命令的参数,邮件数据是DATA命令的参数。这些参数或数据对象必须 (MUST) 被传输和保存,等待由邮件数据结束指示传达的确认,该指示最终确定事务。其模型是提供不同的缓冲区来保存数据对象的类型; 也就是说,有一个反向路径缓冲区、一个正向路径缓冲区和一个邮件数据缓冲区。特定命令导致信息被追加到特定缓冲区,或导致清除一个或多个缓冲区。

几个命令 (RSET, DATA, QUIT) 被指定为不允许参数。在服务器提供并被客户端接受的特定扩展不存在的情况下,客户端禁止 (MUST NOT) 发送此类参数,服务器应该 (SHOULD) 拒绝包含它们的命令,认为其语法无效。

4.1.1.1. Extended HELLO (EHLO) or HELLO (HELO) (扩展HELLO或HELLO)

这些命令用于向SMTP服务器标识SMTP客户端。参数子句包含SMTP客户端的完全限定域名 (fully-qualified domain name),如果有的话。在SMTP客户端系统没有有意义的域名的情况下 (例如,当其地址是动态分配的且没有可用的反向映射记录时),客户端应该 (SHOULD) 发送地址文字 (address literal) (参见第4.1.3节)。

RFC 2821和一些早期的非正式实践鼓励在文字后面跟随有助于标识客户端系统的信息。该约定并未得到广泛支持,许多SMTP服务器将其视为错误。为了互操作性,服务器最好准备好处理此字符串的出现,但SMTP客户端不应该 (SHOULD NOT) 发送它。

SMTP服务器在连接问候应答和对此命令的响应中向SMTP客户端标识自己。

客户端SMTP应该 (SHOULD) 通过发出EHLO命令来启动SMTP会话。如果SMTP服务器支持SMTP服务扩展,它将给出成功响应、失败响应或错误响应。如果SMTP服务器违反本规范而不支持任何SMTP服务扩展,它将生成错误响应。如上所述,较旧的客户端SMTP系统可以 (MAY) 使用HELO (如RFC 821中指定) 而不是EHLO,服务器必须 (MUST) 支持HELO命令并正确响应。无论如何,客户端必须 (MUST) 在启动邮件事务之前发出HELO或EHLO。

这些命令以及对其中一个命令的"250 OK"应答确认SMTP客户端和SMTP服务器都处于初始状态,即没有正在进行的事务,并且所有状态表和缓冲区都已清除。

语法:

ehlo           = "EHLO" SP ( Domain / address-literal ) CRLF

helo = "HELO" SP Domain CRLF

通常,对EHLO的响应将是多行应答。应答的每一行包含一个关键字以及可选的一个或多个参数。遵循多行应答的正常语法,这些关键字在除最后一行之外的所有行中跟随代码 (250) 和连字符,在最后一行中跟随代码和空格。使用RFC 5234 [7] 的ABNF表示法和终端符号,肯定响应的语法为:

ehlo-ok-rsp    = ( "250" SP Domain [ SP ehlo-greet ] CRLF )
/ ( "250-" Domain [ SP ehlo-greet ] CRLF
*( "250-" ehlo-line CRLF )
"250" SP ehlo-line CRLF )

ehlo-greet = 1*(%d0-9 / %d11-12 / %d14-127)
; string of any characters other than CR or LF

ehlo-line = ehlo-keyword *( SP ehlo-param )

ehlo-keyword = (ALPHA / DIGIT) *(ALPHA / DIGIT / "-")
; additional syntax of ehlo-params depends on
; ehlo-keyword

ehlo-param = 1*(%d33-126)
; any CHAR excluding <SP> and all
; control characters (US-ASCII 0-31 and 127
; inclusive)

虽然EHLO关键字可以以大写、小写或混合大小写指定,但它们必须 (MUST) 始终以不区分大小写的方式识别和处理。这只是RFC 821和第2.4节中指定的实践的扩展。

EHLO响应必须 (MUST) 包含第4.5.1节中未列为"必需"的所有命令的关键字 (以及所需的相关参数),仅排除第4.1.5节中描述的专用命令。专用命令可以 (MAY) 列出。

4.1.1.2. MAIL (MAIL) (邮件)

此命令用于启动邮件事务,在该事务中,邮件数据被传递到SMTP服务器,该服务器可能依次将其传递到一个或多个邮箱或将其传递到另一个系统 (可能使用SMTP)。参数子句包含反向路径,并可能包含可选参数。通常,只有在没有正在进行的邮件事务时才能发送MAIL命令,参见第4.1.4节。

反向路径由发件人邮箱组成。从历史上看,该邮箱可能可选地以主机列表开头,但该行为现已弃用 (参见附录C)。在某些类型的报告消息中,应答可能导致邮件循环 (例如,邮件传递和未传递通知),反向路径可以为空 (参见第3.6节)。

此命令清除反向路径缓冲区、正向路径缓冲区和邮件数据缓冲区,并将其参数子句中的反向路径信息插入反向路径缓冲区。

如果协商了服务扩展,MAIL命令还可以携带与特定服务扩展相关联的参数。

语法:

mail = "MAIL FROM:" Reverse-path
[SP Mail-parameters] CRLF

4.1.1.3. RECIPIENT (RCPT) (收件人)

此命令用于标识邮件数据的单个收件人; 通过多次使用此命令来指定多个收件人。参数子句包含正向路径,并可能包含可选参数。

正向路径通常由必需的目标邮箱组成。发送系统不应该 (SHOULD NOT) 生成称为源路由 (source route) 的可选主机列表。接收系统必须 (MUST) 识别源路由语法,但应该 (SHOULD) 剥离源路由规范,并使用与邮箱关联的域名,就好像未提供源路由一样。

类似地,中继主机应该 (SHOULD) 剥离或忽略源路由,并且名称禁止 (MUST NOT) 被复制到反向路径中。当邮件到达其最终目的地 (正向路径仅包含目标邮箱) 时,SMTP服务器根据其主机邮件约定将其插入目标邮箱。

此命令将其正向路径参数追加到正向路径缓冲区; 它不更改反向路径缓冲区或邮件数据缓冲区。

例如,在中继主机xyz.com接收到的带有信封命令的邮件

MAIL FROM:<[email protected]>
RCPT TO:<@hosta.int,@jkl.org:[email protected]>

通常将使用信封命令直接发送到主机d.bar.org

如附录C中所述,xyz.com也可以 (MAY) 选择使用信封命令将消息中继到hosta.int

MAIL FROM:<[email protected]>
RCPT TO:<@hosta.int,@jkl.org:[email protected]>

或使用信封命令中继到jkl.org

MAIL FROM:<[email protected]>
RCPT TO:<@jkl.org:[email protected]>

现在强烈不鼓励尝试以这种方式使用中继。由于主机根本不需要中继邮件,xyz.com也可以 (MAY) 在收到RCPT命令时完全拒绝消息,使用550代码 (因为这是"策略原因")。

如果协商了服务扩展,RCPT命令还可以携带与服务器提供的特定服务扩展相关联的参数。客户端禁止 (MUST NOT) 传输除服务器在其EHLO响应中提供的服务扩展相关联的参数之外的参数。

语法:

rcpt = "RCPT TO:" ( "<Postmaster@" Domain ">" / "<Postmaster>" /
Forward-path ) [SP Rcpt-parameters] CRLF

注意,与通常的local-part规则不同,上面显示的"Postmaster"字符串被视为不区分大小写。

4.1.1.4. DATA (DATA) (数据)

接收方通常向DATA发送354响应,然后将命令后面的行 (以<CRLF>序列结尾的字符串,如第2.3.7节所述) 视为来自发件人的邮件数据。此命令导致邮件数据被追加到邮件数据缓冲区。邮件数据可以包含任何128个ASCII字符代码,尽管经验表明使用除SP, HT, CR和LF之外的控制字符可能会导致问题,并且应该 (SHOULD) 在可能的情况下避免。

邮件数据由仅包含句点的行终止,即字符序列"<CRLF>.<CRLF>",其中第一个<CRLF>实际上是前一行的终止符 (参见第4.5.2节)。这是邮件数据结束指示。此终止序列的第一个<CRLF>也是结束数据 (消息文本) 最后一行的<CRLF>,或者,如果没有邮件数据,则结束DATA命令本身 ("无邮件数据"情况不符合本规范,因为它要求既不传输本规范要求的跟踪头字段,也不传输RFC 5322 [4] 要求的消息头部分)。禁止 (MUST NOT) 添加额外的<CRLF>,因为那会导致将空行添加到消息中。此规则的唯一例外是,如果消息正文传递给发起SMTP发件人时,最后一"行"不以<CRLF>结尾; 在这种情况下,发起SMTP系统必须 (MUST) 要么拒绝消息为无效,要么添加<CRLF>以使接收SMTP服务器识别"数据结束"条件。

接受仅以<LF>结尾的行的惯例,作为对某些UNIX系统不符合行为的让步,已被证明会导致的互操作性问题多于其解决的问题,SMTP服务器系统禁止 (MUST NOT) 这样做,即使是以改进鲁棒性的名义。特别是,序列"<LF>.<LF>" (裸换行符,没有回车符) 禁止 (MUST NOT) 被视为等同于<CRLF>.<CRLF>作为邮件数据结束指示。

收到邮件数据结束指示要求服务器处理存储的邮件事务信息。此处理消耗反向路径缓冲区、正向路径缓冲区和邮件数据缓冲区中的信息,并且在完成此命令时清除这些缓冲区。如果处理成功,接收方必须 (MUST) 发送OK应答。如果处理失败,接收方必须 (MUST) 发送失败应答。SMTP模型在此时不允许部分失败: 消息要么被服务器接受以进行传递并返回肯定响应,要么不被接受并返回失败应答。在向数据结束指示发送肯定的"250 OK"完成应答时,接收方承担消息的全部责任 (参见第6.1节)。随后诊断的错误必须 (MUST) 在邮件消息中报告,如第4.4节所述。

当SMTP服务器接受用于中继或最终传递的消息时,它在邮件数据的顶部插入跟踪记录 (也可互换地称为"时间戳行"或"Received"行)。此跟踪记录指示发送消息的主机的身份、接收消息 (并插入此时间戳) 的主机的身份以及接收消息的日期和时间。中继的消息将有多个时间戳行。这些行的形成细节,包括其语法,在第4.5节中指定。

关于DATA命令操作的其他讨论出现在第3.3节中。

语法:

data = "DATA" CRLF

4.1.1.5. RESET (RSET) (重置)

此命令指定将中止当前邮件事务。任何存储的发件人、收件人和邮件数据必须 (MUST) 被丢弃,并且所有缓冲区和状态表都被清除。接收方必须 (MUST) 向不带参数的RSET命令发送"250 OK"应答。客户端可以 (MAY) 在任何时候发出重置命令。如果在EHLO之后立即发出、在会话中发出EHLO之前、在发送并确认数据结束指示之后或在QUIT之前立即发出,它实际上等同于NOOP (即,没有效果)。SMTP服务器禁止 (MUST NOT) 因接收到RSET而关闭连接; 该操作保留给QUIT (参见第4.1.1.10节)。

由于EHLO意味着服务器的一些额外处理和响应,即使正式语义相同,RSET通常也比重新发出该命令更有效。

在违反本规范意图的情况下,SMTP服务器可能会收到底层TCP连接已关闭或重置的指示。为了保持邮件系统的鲁棒性,SMTP服务器应该 (SHOULD) 准备好处理这种情况,并且应该 (SHOULD) 将其视为在连接消失之前收到了QUIT。

语法:

rset = "RSET" CRLF

4.1.1.6. VERIFY (VRFY) (验证)

此命令要求接收方确认参数标识了用户或邮箱。如果它是用户名,则按照第3.5节中指定的方式返回信息。

此命令对反向路径缓冲区、正向路径缓冲区或邮件数据缓冲区没有影响。

语法:

vrfy = "VRFY" SP String CRLF

4.1.1.7. EXPAND (EXPN) (扩展)

此命令要求接收方确认参数标识了邮件列表,如果是,则返回该列表的成员。如果命令成功,将返回包含第3.5节中描述的信息的应答。除了一个成员列表的简单情况外,此应答将有多行。

此命令对反向路径缓冲区、正向路径缓冲区或邮件数据缓冲区没有影响,并且可以 (MAY) 在任何时候发出。

语法:

expn = "EXPN" SP String CRLF

4.1.1.8. HELP (HELP) (帮助)

此命令导致服务器向客户端发送有用的信息。该命令可以 (MAY) 接受参数 (例如,任何命令名称) 并返回更具体的信息作为响应。

此命令对反向路径缓冲区、正向路径缓冲区或邮件数据缓冲区没有影响,并且可以 (MAY) 在任何时候发出。

SMTP服务器应该 (SHOULD) 支持不带参数的HELP,并且可以 (MAY) 支持带参数的HELP。

语法:

help = "HELP" [ SP String ] CRLF

4.1.1.9. NOOP (NOOP) (无操作)

此命令不影响任何参数或先前输入的命令。它除了接收方发送"250 OK"应答外不指定任何操作。

此命令对反向路径缓冲区、正向路径缓冲区或邮件数据缓冲区没有影响,并且可以 (MAY) 在任何时候发出。如果指定了参数字符串,服务器应该 (SHOULD) 忽略它。

语法:

noop = "NOOP" [ SP String ] CRLF

4.1.1.10. QUIT (QUIT) (退出)

此命令指定接收方必须 (MUST) 发送"221 OK"应答,然后关闭传输通道。

接收方禁止 (MUST NOT) 故意关闭传输通道,直到它接收并应答QUIT命令 (即使有错误)。发送方禁止 (MUST NOT) 故意关闭传输通道,直到它发送QUIT命令,并且应该 (SHOULD) 等待接收应答 (即使对先前命令有错误响应)。如果由于违反上述规定或系统或网络故障而过早关闭连接,服务器必须 (MUST) 取消任何待处理的事务,但不能撤销任何先前完成的事务,并且通常必须 (MUST) 表现得就像正在进行的命令或事务收到了临时错误 (即4yz响应) 一样。

QUIT命令可以 (MAY) 在任何时候发出。任何当前未完成的邮件事务将被中止。

语法:

quit = "QUIT" CRLF

4.1.1.11. Mail-Parameter and Rcpt-Parameter Error Responses (Mail参数和Rcpt参数错误响应)

如果服务器SMTP不识别或无法实现与特定MAIL FROM或RCPT TO命令相关联的一个或多个参数,它将返回代码555。

如果由于某种原因,服务器暂时无法容纳与MAIL FROM或RCPT TO命令相关联的一个或多个参数,并且如果特定参数的定义没有强制使用另一个代码,它应该 (SHOULD) 返回代码455。

特定于特定参数及其值的错误将在参数定义的RFC中指定。

4.1.2. Command Argument Syntax (命令参数语法)

上述命令的参数子句的语法 (在适用的情况下使用RFC 5234 [7] 中指定的语法) 如下所示。下面给出的一些产生式仅与附录C中描述的源路由结合使用。本文档中未定义的终端符号,如ALPHA, DIGIT, SP, CR, LF, CRLF,如RFC 5234 [7] 第6节中的"核心"语法或RFC 5322 [4] 中的消息格式语法中定义的那样。

Reverse-path   = Path / "<>"

Forward-path = Path

Path = "<" [ A-d-l ":" ] Mailbox ">"

A-d-l = At-domain *( "," At-domain )
; Note that this form, the so-called "source
; route", MUST BE accepted, SHOULD NOT be
; generated, and SHOULD be ignored.

At-domain = "@" Domain

Mail-parameters = esmtp-param *(SP esmtp-param)

Rcpt-parameters = esmtp-param *(SP esmtp-param)

esmtp-param = esmtp-keyword ["=" esmtp-value]

esmtp-keyword = (ALPHA / DIGIT) *(ALPHA / DIGIT / "-")

esmtp-value = 1*(%d33-60 / %d62-126)
; any CHAR excluding "=", SP, and control
; characters. If this string is an email address,
; i.e., a Mailbox, then the "xtext" syntax [32]
; SHOULD be used.

Keyword = Ldh-str

Argument = Atom

Domain = sub-domain *("." sub-domain)

sub-domain = Let-dig [Ldh-str]

Let-dig = ALPHA / DIGIT

Ldh-str = *( ALPHA / DIGIT / "-" ) Let-dig

address-literal = "[" ( IPv4-address-literal /
IPv6-address-literal /
General-address-literal ) "]"
; See Section 4.1.3

Mailbox = Local-part "@" ( Domain / address-literal )

Local-part = Dot-string / Quoted-string
; MAY be case-sensitive

Dot-string = Atom *("." Atom)

Atom = 1*atext

Quoted-string = DQUOTE *QcontentSMTP DQUOTE

QcontentSMTP = qtextSMTP / quoted-pairSMTP

quoted-pairSMTP = %d92 %d32-126
; i.e., backslash followed by any ASCII
; graphic (including itself) or SPace

qtextSMTP = %d32-33 / %d35-91 / %d93-126
; i.e., within a quoted string, any
; ASCII graphic or space is permitted
; without blackslash-quoting except
; double-quote and the backslash itself.

String = Atom / Quoted-string

虽然上面对Local-part的定义相对宽松,但为了最大的互操作性,期望接收邮件的主机应该 (SHOULD) 避免定义Local-part需要 (或使用) Quoted-string形式或Local-part区分大小写的邮箱。对于需要生成或比较Local-part (例如,与特定邮箱名称) 的任何目的,所有引用形式必须 (MUST) 被视为等效,并且发送系统应该 (SHOULD) 传输使用最少引用的形式。

系统禁止 (MUST NOT) 以需要在SMTP中使用非ASCII字符 (高位设置为1的八位组) 或其他八位组的方式定义邮箱,但服务扩展允许的情况除外 (例如,第2.2节)。

(第4章内容继续...)