跳到主要内容

6. 问题检测和处理 (Problem Detection and Handling)

本节说明 SMTP 实现应如何处理邮件传输期间出现的各种问题.

6.1. 可靠投递和通过电子邮件的响应 (Reliable Delivery and Replies by Email)

投递状态通知 (Delivery Status Notifications, DSN)

当邮件无法投递时, 发送系统应生成投递状态通知 (bounce message), 通知发送者.

DSN 要求:

  • 必须发送到 reverse-path (MAIL FROM 地址)
  • 必须使用 null reverse-path (MAIL FROM:<>) 以防止循环
  • 应包含原始消息或头部
  • 应说明失败原因

Bounce 消息示例:

From: [email protected]
To: [email protected]
Subject: Undelivered Mail Returned to Sender

Your message to [email protected] could not be delivered:

550 5.1.1 Mailbox does not exist

--- Original message headers follow ---
From: [email protected]
To: [email protected]
Subject: Important message
...

成功通知

通常不发送投递成功通知, 因为它们会产生额外流量. 不过, DSN 扩展允许显式请求正向投递确认.

超时和重试

重试计划:

  • 初始失败: 30 分钟后重试
  • 后续失败: 指数退避 (1 小时, 2 小时, 4 小时等)
  • 最大重试期间: 至少 4-5 天
  • 最终失败: 向发送者发送 bounce

警告消息:

  • 4-6 小时后向发送者发送延迟警告
  • 包含延迟原因
  • 告知发送者投递尝试仍在继续

6.2. 不想要,未请求和“攻击”消息 (Unwanted, Unsolicited, and "Attack" Messages)

SMTP 服务器应实现防滥用保护:

垃圾邮件防护

技术:

  1. 接收者验证: 接受邮件前验证接收者存在
  2. 发送者验证: 检查 SPF, DKIM, DMARC
  3. 速率限制: 限制每连接/每小时消息数
  4. Greylisting: 临时拒绝首次出现的发送者
  5. 内容过滤: 扫描消息内容中的垃圾邮件模式
  6. 信誉系统: 跟踪发送者信誉

拒绝服务 (DoS) 保护

防御措施:

  1. 连接限制: 限制单个 IP 的并发连接数
  2. 速率限制: 限制每秒命令数
  3. Tarpit: 放慢可疑发送者
  4. 资源限制: 限制每连接 CPU 和内存使用
  5. 超时强制: 断开空闲连接

目录收集攻击 (Directory Harvest Attacks, DHA)

攻击者发送带有猜测地址的 RCPT TO 命令, 以查找有效邮箱.

对策:

Vulnerable:
C: RCPT TO:<[email protected]>
S: 550 No such user (reveals invalid)
C: RCPT TO:<[email protected]>
S: 250 Ok (reveals valid)

Protected:
- 延迟对 RCPT 命令的响应
- 对有效和无效地址使用相同响应 (例如总是 "250 Ok")
- 仅在 DATA 期间或之后验证接收者
- 对 RCPT 命令进行速率限制

Backscatter 防止

Backscatter 是指发送到伪造发送者地址的 bounce, 会产生附带垃圾邮件.

防止方式:

  1. 在 SMTP 时拒绝: 在 SMTP 事务期间拒绝无效接收者 (DATA 之前)
  2. 验证发送者: 不接受来自伪造发送者的邮件
  3. 不生成 Bounce: 对来自未知/可疑来源的邮件不生成 bounce
Good Practice:
C: RCPT TO:<[email protected]>
S: 550 5.1.1 User unknown (reject immediately)

Bad Practice:
C: RCPT TO:<[email protected]>
S: 250 Ok (accept mail)
(later generate bounce to forged sender)

6.3. 循环检测 (Loop Detection)

邮件循环发生在消息反复通过相同服务器转发时.

检测方法

方法 1: Received 头部计数

Count Received headers in message
If count > 100:
Reject with "554 5.4.6 Too many hops"

方法 2: Received 头部分析

Parse Received headers
Check if current server appears multiple times
If yes:
Detect loop and reject

方法 3: Message-ID 跟踪

Remember Message-ID of recently processed messages
If same Message-ID seen again within short time:
Likely a loop, reject

循环防止

最佳实践:

  1. 限制 Hop: 超过 50-100 个 Received 头部后拒绝
  2. 检测模式: 识别 trace 中重复的服务器名称
  3. 打破循环: 不转发看起来正在循环的邮件
  4. 配置审查: 避免创建循环的转发规则

循环示例:

mail.a.example -> mail.b.example ->
mail.c.example -> mail.a.example (loop!)

6.4. 补偿不规则性 (Compensating for Irregularities)

现实中的 SMTP 实现有时会偏离标准. 健壮实现应优雅处理常见不规则性.

常见不规则性

1. 额外空白

某些实现发送额外空格:

Non-standard:
C: MAIL FROM: <[email protected]> (extra spaces)

Robust handling:
Accept and parse correctly

2. 混合大小写命令

Non-standard:
C: Mail From:<[email protected]>

Robust handling:
Treat as case-insensitive

3. 缺少尖括号

某些旧系统省略地址周围的 <>:

Non-standard:
C: MAIL FROM:[email protected]

Robust handling:
Accept both with and without brackets

4. 只有 CR 或 LF (不是 CRLF)

标准要求 CRLF (\r\n), 但某些实现只发送 LF (\n) 或 CR (\r):

Robust handling:
Accept CR, LF, or CRLF as line terminator
(but always send CRLF)

5. 长行

某些实现发送超过限制的行:

Robust handling:
- Accept lines longer than 512 octets
- Split internally if needed
- Don't break during SMTP transaction

健壮性原则

"发送时保守, 接收时宽容"

  • 发送: 严格符合标准
  • 接收: 优雅接受轻微偏差
  • 记录: 记录非标准行为
  • 不要: 悄悄修正并转发, 从而维持互操作性问题

错误恢复

遇到不规则性时:

选项 1: 接受并继续

Log warning
Process message normally

选项 2: 带通知地接受

Log warning
Process message
Send DSN to postmaster about issue

选项 3: 拒绝

Only for severe violations
Provide clear error message
Log for analysis

合规测试

实现者应针对以下对象进行测试:

  • 符合标准的服务器: 验证正确操作
  • 已知有缺陷的实现: 确保互操作性
  • 畸形输入: 测试错误处理
  • 边界情况: 边界条件和异常场景