邮件从发送到接收需经多个组件:MUA、MSA、MTA、LDA 各司其职
来源标题:A Bit about Email
一封邮件送达收件人前要经过多个组件:发件人的 MUA 将邮件上传至 MSA 验证密码,再交给 MTA 与其他邮件服务器通信,送达收件方 MTA 后由 LDA 存入本地存储,等待收件人通过 IMAP 读取。这一结构源于 1980 年代的设计,当时多数机构只能负担一台服务器。各步骤未必是不同机器甚至不同程序。
以下正文由 AI 翻译自 Hacker News 最新(原文版权归原站所有),可能存在偏差,重要内容请以原文为准。
先说一些术语。
一封典型的邮件在到达收件人之前会经过许多不同的组件:
- 发件人的 MUA [通过 465 端口上的 SMTP]
- 发件方的 MSA
- 发件方的 MTA [通过 25 端口上的 SMTP]
- 收件方的 MTA
- 收件方的 LDA 与邮件存储 [通过 IMAP]
- 收件人的 MUA
(这些并不一定对应不同的机器,甚至不一定是不同的程序,但为每个步骤取一个名字会很有帮助。)
邮件用户代理(MUA)运行在用户自己的计算机上,是用户用来发送或阅读邮件的程序。
假设用户要发送一封邮件:这封邮件会被上传到提交代理(MSA),MSA 会校验用户的密码,然后把它交给邮件传输代理(MTA)。传输代理的职责是与其他邮件服务器通信,把邮件送到接收方的 MTA。一旦收到,邮件就交给本地投递代理(LDA)存储起来,直到收件人来查收。
“LDA”这个名字并不标准,我用它是为了避免出现四个只差一个字母的不同缩写。
这种结构之所以存在,是因为在设计这套系统的时候(大约 1980 年),大多数机构只负担得起一台服务器。维护是一份全职工作,升级往往需要停机好几天。一台网络条件较好的计算机可能拥有一条专用的个位数兆比特每秒链路,而且只有一半时间能正常工作。
这就迫使 MTA 和 MUA 必须分开:如果收件人的服务器在邮件发出时没有在线,发件方的 MTA 会保存这封邮件,稍后重试投递。这样一来,短暂的服务中断就不会导致邮件被退回。由于邮件客户端是用户运行的交互式程序,就需要一个单独的邮件存储,把邮件保存到用户能够阅读为止。
大多数 MTA 都被配置为对一封邮件持续重试五天,通常采用指数退避:我自己的配置是首次失败后等待 5 分钟,在长时间中断时将间隔逐步增加到一小时。
MSA 和 LDA 一直以某种形式存在,但当人们开始从自己的计算机访问邮件、而不是远程登录到服务器上时,它们变得复杂得多。
大多数通信都通过简单邮件传输协议(SMTP)进行,包括 MTA 与 MTA 之间的投递。
为了把范围缩小一点,我来假装自己是一台 MTA,要向 blog@maurycyz.com 发送邮件。
第一步是查询该域名的接收方 MTA:
dig MX maurycyz.com
[略] maurycyz.com. 300 IN MX 1 smallcrow.com. [略]
dig A smallcrow.com
[略] smallcrow.com. 300 IN A 65.109.172.162 [略]
电子邮件的历史足够悠久,所以它有自己专用的 DNS 记录,而不必借助子域名或滥用 TXT 记录之类的花招。那个数字是优先级(越小优先级越高),在指定了多台主机时会用到:大型邮件服务商会有多台服务器,以确保投递及时。
SMTP 是一种相对直白的基于文本的协议。唯一需要注意的是,大多数邮件服务器要求用 CRLF 作为数据结束标记。这通常可以用 ctrl-m 发送,但用一个能自动插入回车字节的工具会更方便:
telnet
telnet> set crlf 将把回车作为 telnet 的 <CR><LF> 发送。 telnet> open smallcrow.com 25 Trying 65.109.172.162... Connected to smallcrow.com. Escape character is '^]'. 220 smallcrow.com ESMTP Postfix HELO nowhere 250 smallcrow.com MAIL FROM:me@nowhere 250 2.1.0 Ok RCPT TO:blog@maurycyz.com 250 2.1.5 Ok DATA 354 End data with <CR><LF>.<CR><LF> From: me@nowhere To: blog@maurycyz.com Hello, world. . 250 2.0.0 Ok: queued as C870D7CD93 QUIT 221 2.0.0 Bye
发送的每一行都是一条命令,远程服务器会以一个三位数字加一段人类可读的文字作为回应。200 表示成功,300 表示“继续……”,400 是临时错误,告诉发件方稍后重试,500 是永久错误,意味着这封邮件应作为无法投递退回。
(如果这看起来与 HTTP 相似,那并非巧合。Web 的很大一部分,包括其标头语法,都是从 SMTP 借鉴来的。)
第一条命令总是 HELO(或 EHLO),后面跟发件方 MTA 的主机名。这通常与邮件地址中使用的主机名不同,但许多服务商要求它与发件人的 IP 地址匹配。我自己的服务器没有这个要求,所以如果你想试一试,随便填一个无法解析的域名就行。
接下来的两条命令 MAIL FROM 和 RCPT TO,分别指定投递地址,以及退信通知应转发到的地址。
退信消息通常是在错误发生的上一级生成的:大多数情况下是接收方 MTA 出了问题,并向发件方 MTA 发送一个错误码,但到那时 MSA 已经(完成处理),从而被迫生成一封未投递通知。这些通知本身使用空的 MAIL FROM,以避免无限循环。
地址被指定并接受之后,就会发送一条 DATA 命令,后面跟着实际的邮件内容。邮件内容至少包含 “From” 和 “To” 标头以及一些文本。邮件以一个单独的点结束,服务器据此表示邮件已交付。
作为最后一跳的 MTA 应当在返回状态码之前,先把邮件转交给 LDA。这样可以确保它们绝不会接受自己无法投递的邮件,否则就会导致错误通知。这种“退信”消息与伪造 MAIL FROM 的垃圾邮件发送者相互配合得很糟糕:它把对方明显伪造的邮件变成一封很可能被投递并造成困扰的邮件。
邮件内部的地址并不要求与 MAIL FROM 和 RCPT TO 中的地址(也称为信封)一致。最常见的原因是邮件被转发了。例如,邮件列表会把信封目标设为每个成员的邮件地址,但保留原始的 “From” 标头不变,以便回复能正常工作。信封发件人地址通常会被改为指向列表管理员会查看错误通知的某个地方。
当然,这并不是说中间系统不会修改标头:
(原文较长,此处译出前 6000 字,完整内容请点「打开原文」。)
正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。