电子邮件的组成:信封、首部和正文
电子邮件由三部分组成,下图是 Client 发送的整个数据:
信封(envelope)
信封是 MTA 用来交付的,在上例中由两个 SMTP 命令指明;
MAIL From: [email protected]
RCPT To: [email protected]首部(header)
首部由用户代理使用,上例中可以看到 9 个首部字段:Recived、Message-Id、From、Date、Reply-To、X-Phone、X-Mailer、To 和 Subject
(以 X- 开头的是用户定义的字段,其他是由 RFC 822 定义的,详见 4.1 节)正文(body)
正文是发送用户发给接收用户报文的内容,RFC 822 指定正文为 NVT ASCII 文字行,用 DATA 命令发送的各行都必须小于 1000 字节
用户接收我们指定为正文的部分,加上一些首部字段,并把结果传到 MTA;MTA 加上一些首部字段,加上信封,并把结果发送到另一个 MTA
(更多实现细节,详见 RFC 2821)
TCP/IP 交换电子邮件示意图:
邮件基础协议
邮件基础的网络协议有以下三个,一般我们使用 SMTP 协议来发送邮件,POP3 和 IMAP 协议来接收邮件(从服务端接收邮件至客户端)
- 下表中 IMAP 协议支持发送邮件并不是邮件投递,而是通过 append 指令将邮件从客户端上传到服务端
SMTP | 25(465) | True | False |
POP3 | 110(995) | False | True |
IMAP | 143(993) | True | True |
SMTP
两个 MTA 之间通过 NVT ASCII 进行通信,客户向服务器发出命令,服务器用数字应答码和可选的人可读字符串进行响应
下图是 SMTP 的一个交互会话过程:
- 键入 mail -v 启动用户代理; 键入 subject(主题)和正文
- 主动打开 port:25 ;等待从 Server 返回的问候报文(应答码220)
- HELO:标识自己(参数必须是合格的主机名)
- MAIL:标识报文发起人
- RCPT:标识接收方(可发多次,标识多个接收方)
- DATA:邮件报文内容
- QUIT:结束邮件交换
下图是发送方 SMTP(Client)和接收方 SMTP(Server)之间的一个 SMTP 连接:
SMTP 指令
最小SMTP支持 8 个命令,除了上述 5 个命令还有:
- RSET:异常中止当前的邮件事务并使两端复位。丢掉所有有关发送方、接收方或邮件的存储信息
- VRFY:使客户询问发送方以验证接收方地址,而无需向发送方发送邮件(通常用与管理员查找邮件交付差错中使用)
- NOOP:强迫服务器响应一个OK应答码(200)
(另外还有一些附加可选命令)
SMTP 用半双工的形式使用 TCP,客户发送一个命令后停止等待应答;实际上 Client 可以一次发多个命令,称为流水线技术(pipelining)
如果使用了这种技术,Client 则不能丢弃报文直到所有的应答都已检查过,确认报文被服务器接收了
使用 Telnet 模拟 SMTP 发送邮件
POP3
POP3 协议相对 SMTP/IMAP 要简单一些,协议的指令也不多
POP3 的生命周期
POP3 命令以 CRLF 对结束,特定命令多行响应,以 CRLF.CRLF 结束
在服务器打开邮件后,它为每个消息指定一个消息号,并以八进制表示每个消息的长度。第一个消息被指定为 1,第二个消息被指定为 2,以此类推,第 N 个消息被指定为 N
在POP3命令和响应中,所以的消息号和长度以十进制表示
AUTHORIZATION
- USER :客户确认身份(参数:username)
- PASS :身份确认完成
- QUIT :终止 POP3 会话
- APOP :用于替代 USER 和 PASS 命令,它以 MD5 数字摘要的形式向POP3邮件服务器提交帐户密码(参数:用户名/密钥)(该命令实现可选)
安全性:每个 POP3 会话都以 USER/PASS 互换开始,导致了用户名和口令在网络上的显式传送,当服务连接频率变大、时间间隔小,就会加大了泄密的可能
(使用APOP:随着密钥长度的增加,解读的难度也会上升)
TRANSACTION
操作状态下的命令:
- STAT :查询邮箱中的统计信息(邮件数量和所有邮件大小)
- LIST :列出邮箱中的所有邮件信息(消息号/大小)(参数:MSG 列出对应消息号的邮件信息)
- RETR :获取某封邮件的内容(参数:MSG)
- DELE :将某封邮件标记为删除(参数:MSG)(被标记的邮件直到当前会话进入 UPDATE 状态才被删除)
- NOOP :检测连接状况
- RSET :取消删除标记
UPDATE
当客户在 AUTHORIZATION 状态下发送 QUIT 命令后,会话进入 UPDATE 状态
如果会话因为 QUIT 命令以外的原因中断,会话并不进入 UPDATE 状态,也不从服务器中删除任何信件
(更多实现细节,详见 RFC 1939)
IMAP
IMAP 协议通过 port:143 来提供电子邮件的收发服务,和 POP3 被用来提供电子邮件客户端服务(从服务器检索电子邮件)
相对于 POP3,IMAP 支持多个电子邮件客户端同时管理邮箱,并可以通过邮件的标签/状态,监测到其他用户对于邮件的操作
IMAP 还支持在线/离线两种操作模式,客户端可以获得邮件副本存储在本地
标志消息属性
与邮件关联的有一个或多个 token list,将 flag 添加至列表可以设置邮件的属性,每个 flag 可以设置为永久的或临时的(当前会话)
\Seen | 邮件已阅读 |
\Answered | 邮件已回复 |
\Flagged | 邮件被标记为紧急/特别关注 |
\Delete | 邮件被标记删除(to EXPUNGE) |
\Draft | 邮件被标记为草稿 |
\Recent | 邮件最近到达该邮箱(本次会话是首次收到当前邮件通知) |
具体的实现与服务器相关
状态和流程图
Client 和 Server 建立好连接后,IMAP 连接处于四种状态之一
最初的状态在服务器的 greeting 报文中标识,客户端在不当状态中尝试的命令服务器都将以 BAD/NO 响应(取决于实现)
- Not Authenticated State :未经认证的状态,连接启动时进入该状态,除非已进行预验证
- Authenticated State:认证状态,可选择邮箱进行访问
- Selected State:选定状态,已选择一个邮箱访问
- Logout State:退出状态,连接正在终止(LOGOUT 命令)
服务器必须发送 BYE 响应来关闭连接,同样的,客户端应该发送 LOGOUT 命令来关闭连接
如果服务器检测到客户端单方面关闭了连接,则可以省略 BYE 响应
IMAP 命令
下面罗列一些常见的 IMAP 命令
客户端命令 —— 任何状态
- CAPABILITY:查询服务器实现的功能
- NOOP:检测连接
- LOGOUT:终止当前连接
客户端命令 —— 未经认证的状态
- STARTTLS:与服务器使用 TLS 进行交互
- AUTHENTICATE:与服务器的认证机制
- LOGIN:鉴权登陆,输入用户名与密码
客户端命令 —— 认证状态
- SELECT:选择邮箱
- EXAMINE:以只读方式选择邮箱
- CREATE:创建一个邮箱
- DELETE:删除一个邮箱
- RENAME:重命名邮箱
- SUBSCRIBE:订阅指定邮箱
- UNSUBSCRIBE:取消订阅邮箱
- LSUB:返回订阅邮箱列表
- APPEND:追加一个State,例如可以保存一封新的邮件
客户端命令 —— 选定状态
- CHECK:检查当前服务器状态(例如:磁盘,内存等)
- CLOSE:永久删除所有消息
- EXPUNGE:永久删除所有消息,与 CLOSE 不同的是,将返回每个消息标识
- SEARCH:类似 find 命令,功能很强大可以按照不同条件搜索邮件
- FETCH:检索与消息相关数据,例如:获取正文
- STORE:改变与消息相关数据,例如:设置邮箱已读、删除状态等
- COPY:拷贝指定的消息
- UID:返回 UID 列表用于 FETCH