一条投递会话的五个阶段
SMTP 是纯文本命令与三位应答码交替的协议,一次投递固定走五段:服务端问候(220)→ 客户端问候与能力协商(EHLO)→ 加密升级与身份认证(STARTTLS、AUTH)→ 信封声明(MAIL FROM、RCPT TO)→ 数据投递(DATA 与结束标志)。理解这条顺序,抓包时就能一眼判断会话卡在哪一段。
基本信息
| 服务端口 | 中继与投递用 25;提交用 587(要求 STARTTLS);加密提交 465(隐式 TLS) |
|---|---|
| 传输 | TCP,命令与应答默认按 CRLF 分行 |
| 认证时机 | 提交类连接必须先 AUTH 再声明信封,25 端口一般不做认证 |
| 行长度限制 | 命令行不超过 512 字节,正文行不超过 998 字节(RFC 5321) |
典型使用场景
- 邮件发不出去时逐段核对应答码,判断是 DNS、连接、认证还是策略拒收
- 写发信程序时确认库是否正确处理 STARTTLS 升级后的 EHLO 重发
- 排查「正文被截断」:多半是 DATA 阶段遇到单独一行的点,或正文里有超长行
- 用 telnet 或 openssl s_client 手写会话,验证凭据与退信路径是否正常
完整投递(含 STARTTLS 与认证)
完整投递(含 STARTTLS 与认证)
S: 220 mail.example.com ESMTP ready
C: EHLO client.local
S: 250-mail.example.com
S: 250-SIZE 10485760
S: 250-AUTH PLAIN LOGIN
C: STARTTLS
S: 220 Ready to start TLS
(完成 TLS 握手,之后所有内容加密)
C: EHLO client.local
C: AUTH PLAIN AHVzZXIAcGFzcw==
S: 235 2.7.0 Authentication successful
C: MAIL FROM:<me@example.com>
S: 250 2.1.0 Ok
C: RCPT TO:<you@example.net>
S: 250 2.1.5 Ok
C: DATA
S: 354 End data with <CR><LF>.<CR><LF>
C: Subject: test
C: (空行)
C: hello
C: .
S: 250 2.0.0 Ok: queued as 4XyZ123
C: QUIT
S: 221 2.0.0 Bye应答码分段读法
| 码段 | 含义 | 会话位置 |
|---|---|---|
| 220 / 250 | 服务就绪 / 命令成功 | 问候与信封阶段 |
| 235 | 认证成功 | AUTH 之后 |
| 354 | 可以开始送正文 | DATA 之后 |
| 421 / 450 | 服务不可用 / 临时失败 | 可重试,不必放弃 |
| 550 / 554 | 永久失败 / 事务被拒 | 策略或地址问题,重试无用 |
正文里若出现以点开头的行(例如 .hidden),发送方要在行首再加一个点做转义,接收方去掉一个点还原;在 DATA 阶段直接粘贴含单独一行的点,会提前结束正文并投出一封被截断的邮件。
STARTTLS 之后必须重新发一次 EHLO:加密通道里的能力列表与明文阶段可能不同,老代码只在升级前发一次 EHLO 会出现「认证命令不被识别」的报错。
多位收件人时 RCPT TO 可重复出现,服务器对每个地址单独应答;只要有一个地址被 550 拒,就要在正文里跑一遍投递结果,不能因为整体返回 250 就认为全部成功。