Mail Protocol - SMTP
1. 概述
SMTP的全称是Simple Mail Transfer Protocol,即简单邮件传输协议,基于TCP/IP用于发送邮件的协议,目的旨在提供可靠和高效的邮件传输
其出现替代掉了早期同样用于用于邮件传输的FTP协议,SMTP于1981年出现,首份文档为RFC 788 Simple Mail Transfer Protocol,后续经过迭代优化,引入了系列的扩展属性以及登录认证,渐渐地演变成了如今的现代化SMTP协议
2. 架构
当存在用户A向用户B发送一封邮件时,整个发送 & 接收过程如下所示,其中可以看到SMTP主要起到了邮件发送的作用
SMTP发送邮件有两个不同的阶段 :submitting
和 relaying
submitting
客户端发送邮件给邮件服务器(如上图的User A -> User A's Mail server
),其端口号一般用的是587,一般需要先通过该端口进行认证,随后再进行发信
这个阶段发送所使用的host一般可直接从邮箱官网 - 设置中获取
relaying
一个邮件服务器投递邮件给另一个邮件服务器,有部分文档也称之为transfer(如上图的User A's Mail server -> User B's Mail server
),一般使用的端口是25,其不会进行发件人的身份认证
这个阶段发送所使用的host可以通过获取DNS的MX记录来获取,如下所示可以通过dig
来获取指定Mail Server可以发送邮件的host信息
1 | ❯ dig thoughtworks.com MX |
通过上述的ANSWER SECTION
部分中,可以看到对应的MX记录,同时该记录中包含着1, 5, 10
等三个不同的优先级,值越小则表示优先级越高,因此在上述结果中ASPMX.L.GOOGLE.COM
的优先级是最高的
什么是MX记录?
DNS的MX记录(Mail Exchanger记录)是一种DNS资源记录,用于指定接收电子邮件的邮件服务器,MX记录告诉其他邮件服务器如何将电子邮件发送到特定域名的邮件服务器,在电子邮件系统中,MX记录是非常重要的,因为它们帮助确定电子邮件的路由路径,确保邮件能够正确地投递到接收方的邮箱
什么是Dig?
用于在Linux命令行环境下获取指定域名的DNS信息,可以通过Dig快速教程来熟悉这个命令
3. 端口
- 25:该端口由RFC 821标准引入,该端口默认不支持SSL并且不用身份认证
- 465:一般默认SMTP协议是TCP明文传输,如果需要升级为SSL,则一般需要再次协商,如果期望在第一次建立TCP连接就使用SSL加密,则可以使用465端口,该端口设计的初衷就是用于SSL连接,但最终未被采纳
- 587:该端口在 RFC 2476中定义,需要身份认证
4. 命令
SMTP通过一系列的命令来完成身份认证与邮件的发送
HELO
发送方问候收件方,本质上是英文单词HELLO的缩写,用于建立一次全新的SMTP会话,一般携带的参数是邮件发送端自身的标识
EHLO
其全称是Extended Hello
,与HELO
相同,都是用于建立一次全新的SMTP会话,但是不一样的是该命令的响应会返回当前邮件接收端支持的扩展特性,如下所示:
1 | ❯ telnet smtp.163.com 25 |
可以看到在发送完HELO
后,邮件接收端返回中携带了一些特性信息,各个特性的解释如下:
250-PIPELINING
:表示服务器支持流水线模式,在流水线模式中支持一次性发送多个SMTP命令
250-AUTH LOGIN PLAIN XOAUTH2
:这部分列出了服务器支持的身份验证方法,表明服务器支持以下身份验证方法:
- “LOGIN”:基于用户名和密码的身份验证
- “PLAIN”:基于明文的身份验证
- “XOAUTH2”:OAuth 2.0协议的一种身份验证方式,通常用于Google等服务
250-AUTH=LOGIN PLAIN XOAUTH2
:与上一行类似,但使用了等号来做分割
250-STARTTLS
:服务器支持的另一项特性,它允许SMTP通信升级为加密通信(SSL),以增加安全性
250 8BITMIME
:表示服务器支持8位MIME(Multipurpose Internet Mail Extensions),8BITMIME允许电子邮件传输包含8位数据的MIME编码消息,以支持更多字符集和内容类型
其中需要注意的是,SMTP响应中,如果为最后一行,则响应格式则不是[STATUS_CODE]-[TEXT]
,而是[STATUS_CODE] [TEXT]
,最后一行不以-
作为间隔符
AUTH LOGIN
该命令主要用于登录邮箱账号获得认证,才能够进行后续的邮件发送,如下所示:
1 | ❯ telnet smtp.163.com 25 |
Tips: Linux下可以通过
echo -n "原文" | base64
对原文字符串进行base64加密,可以通过echo -n "BASE64密文" | base64 -D
进行base64解密
可以看到输入auth login
后,服务端的响应是334 dXNlcm5hbWU6
,在输入邮箱账号base64字符串回车后,服务端的响应是334 UGFzc3dvcmQ6
,对应的通过base64解密可以得知对应两个返回的字符串为:
1 | echo -n "dXNlcm5hbWU6" | base64 -D |
MAIL FROM
用于标明发信人的身份,命令的使用如下所示:
1 | MAIL FROM:<zxchengb@163.com> |
RCPT TO
用于标明收信人的身份信息,具体使用如下所示:
1 | RCPT TO:<zxchengb@qq.com> |
需要注意的是,在SMTP中无法区分BCC、CC和TO的身份,这一类信息需要在邮件正文的mime header里的不同字段进行区分,该指令同时还会检测邮箱是否存在,如果邮箱不存在,则会返回5xx的状态码
DATA
用于发送方正式发送邮件主体内容数据,如果发送结束,则需要以<CRLF>.<CRLF>
作为结束符
QUIT
用于结束当前会话,通知邮件服务器断开连接,如下所示:
1 | QUIT |
RSET
表示清除当前所有缓冲数据,包含发件人、收件人和邮件内容
VRFY
用于验证指定的用户邮箱是否存在,但出于安全问题,大部分情况下都不会提供该命令功能
EXPN
用于验证给定的邮箱列表是否存在,跟VRFY一样,该命令通常会被禁用
HELP
用于查询服务器支持的命令,大部分邮件服务器也会禁用该命令
NOOP
可以理解成是一个心跳请求,接收方如果一切正常则必须回复250 OK
利用以上命令可以在命令行中发送一封邮件,如下所示:
1 | ❯ telnet smtp.163.com 25 |
5. 状态码
每次发送命令,服务端都会返回一个状态码,以下是常见的状态码返回清单
Code | Desc |
---|---|
211 | 系统状态或系统帮助响应 |
214 | 帮助信息 |
220 | 服务就绪 |
221 | 服务关闭 |
250 | 要求的操作已完成 |
251 | 用户非当前邮件服务器,将进行转发 |
334 | 等待用户输入验证信息 |
354 | 开始邮件输入,以”.”结束 |
421 | 服务未就绪,关闭传输通道 |
450 | 要求的邮件操作未完成,邮箱不可用 |
451 | 放弃要求的操作,处理过程中出错 |
452 | 系统存储不足,要求的操作未执行 |
501 | 参数格式错误 |
502 | 命令不可实现 |
503 | 错误的命令序列 |
504 | 命令参数不可实现 |
550 | 要求的邮件操作未完成,邮箱不可用 |
551 | 用户非当前邮件服务器 |
552 | 过量的存储分配,要求的操作未完成 |
553 | 邮箱名不可用 |
554 | 操作失败 |