TCP/IP详解 卷1:协议 学习笔记 第二十八章 SMTP:简单邮件传送协议

本文主要是介绍TCP/IP详解 卷1:协议 学习笔记 第二十八章 SMTP:简单邮件传送协议,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在这里插入图片描述
用户与用户代理打交道,有多个用户代理可供选择,Unix上常用用户代理包括MH、Berkeley Mail、Elm、Mush。

用TCP进行邮件交换是由报文传送代理MTA(Message Transfer Agent)完成的。最普通的Unix系统的MTA是Sendmail,用户通常不跟MTA打交道,由管理员设置本地的MTA,用户通常可以选择自己的用户代理。

本章研究两个MTA之间如何用TCP交换邮件。SMTP协议指定了在一个简单TCP连接上,两个MTA如何进行通信。

两个MTA之间用NVT ASCII进行通信,客户向服务器发出命令,服务器用数字应答码和可选的人可读字符串进行响应。

发送一个只有一行的简单邮件,观察SMTP连接,下例中使用-v选项的目的是显示MTA在SMTP连接上发送和接收的内容,以>>>开头的是SMTP客户发出的命令,以3位数字的应答码开始的行是从SMTP服务器来的:
在这里插入图片描述
只有5个SMTP命令用于发送邮件:HELO、MAIL、RCTP、DATA、QUIT。

以上过程我们先键入mail启动用户代理,然后键入主题,键入后,再键入报文正文,在新的一行上键入一个句点结束报文,之后用户代理把邮件传给MTA,由MTA进行交付。

客户MTA主动与服务器连接,之后等待服务器发来的问候报文(应答代码为220)。下一步客户用HELO命令发送客户主机名。MAIL命令发送邮件的发送人。RCPT发送邮件接收方,如果有多个接收方,则发多个RCPT命令。DATA命令发送邮件内容,之后客户发送了一个句点表示邮件发送结束。最后的QUIT命令结束了邮件的发送。以下是上例的时间序列:
在这里插入图片描述
我们键入到用户代理的报文是1,2,3.,但报文段12中共发送了393字节,发送的内容如下:
在这里插入图片描述
在这里插入图片描述
其中前3行(Received和Message-Id)是由MTA加上的,后面的行由用户代理生成。

最小SMTP实现支持8种命令(包含上例中的5种)。

其他一些SMTP命令:
1.RSET:异常终止当前邮件事务,并使两端复位,丢掉所有发送方、接收方邮件的存储信息。
2.VRFY:查询接收方地址的详细信息,通常是管理员在查找邮件交付差错时手工使用。
3.NOOP:强迫服务器响应一个OK应答码(200)。
4.TURN:使客户和服务器交换角色,无需重新建立TCP连接就能以相反方向发送邮件(Sendmail不支持此命令)。
5.SEND、SOML、SAML:类似MAIL命令,但很少被实现,它们允许邮件直接发送到客户终端或接收方邮箱。

电子邮件组成部分:
1.信封:上例中由两个SMTP命令指明:
在这里插入图片描述
2.首部:由用户代理使用。上例中有9个首部字段:Received、Message-Id、From、Data、Reply-To、X-Phone、X-Mailer、To、Subject,以X开头的字段不是RFC定义的,比较长的字段值,如上例中的Received,被分在几行中,第一行以外的行以空格开头。

3.正文:RFC 822指定正文为NVT ASCII文字行,用DATA命令发送邮件时,先发送首部,后跟一个空行,然后是正文。DATA命令发送的各行必须小于1000字节。

用户代理接收正文,并加上一些首部字段,把结果传到MTA,MTA加上一些首部字段和信封,把结果发送到另一个MTA。

内容通常指首部和正文的组合,是用户用DATA命令发送的。

上例中客户MTA的输出第一行是通过以太网连接到邮件主机,这是因为作者系统已被配置成把所有向外的邮件发送到一台中继机上进行转发,这样做的原因是:
1.简化了除中继系统MTA外的其他所有MTA配置。
2.它允许某个机构中的一个系统作为邮件集线器,从而可能把其他所有系统隐藏起来。

上例中,中继系统在本地域(.tuc.noao.edu)中有一个主机名mailhost,其他所有系统都被配置成把它们的邮件发往该主机。执行host命令查看mailhost主机名在DNS中的定义:
在这里插入图片描述
如果用于中继的主机改变了,只需改变它的DNS名,其他所有单个系统的邮箱配置都无需改变。
在这里插入图片描述
上图中所有MTA之间的通信都使用SMTP协议。

SMTP用NVT ASCII表示一切(信封、首部、正文),这是一个7bit字符码,以8bit字符发送,高位比特被置为0。

当用户代理把邮件发给MTA后,如果后续MTA交付失败,MTA必须把该报文放入队列中以后再试。Host Requirements RFC推荐初始时间间隔至少为30分钟,发送方至少4~5天内不能放弃。

DNS的一种资源记录类型是邮件交换记录,称为MX记录,它可向不直接连到Internet的主机发送邮件。下例中主机mlfarm.com不直接连到Internet,但有一个MX记录指向Internet上的一个邮件转发器:
在这里插入图片描述
上图显示有两个MX记录,各有不同的优先级,MTA先从具有低优先级数值的MX记录开始尝试连接:
在这里插入图片描述
以下是邮件发送和DNS通信的tcpdump输出:
在这里插入图片描述
第一行是MTA向它的域名服务器查询mlfarm.com的MX记录,2后面的加号表示设置了递归标志位。

第二行是域名服务器的响应,2后面的星号表示置位了授权比特,并包含2个RR(两个MX主机名),0个授权RR,2个附加RR(两个主机的IP地址)。

第3~5行与主机mercury.hsi.com上的SMTP建立了一个TCP连接,目的主机上的响应220在第六行。

主机mercury.hsi.com必须把邮件报文交付给mlfarm.com,对于没有连接到Internet上的系统,与它的MX站点交换邮件时,UUCP协议是一个常用办法。

上例中,MTA使用DNS获取一个MX记录,不幸的是,MTA与DNS之间的交互随不同的实现而不同。RFC 974规定MTA必须首先获取MX记录,如果没有MX记录,再尝试通过DNS获取目的主机的IP。MTA也必须处理DNS中的CNAME记录(CNAME用于将一个域名映射到另一个域名,这对于需要在同一个IP地址上运行多个服务的情况来说非常方便。若要同时运行文件传输服务和Web服务,则可以把ftp.example.com和www.example.com都指向DNS记录example.com,而后者则有一个指向IP地址的A记录。如此一来,若服务器IP地址改变,则只需修改example.com的A记录即可)。

另一个发送邮件的例子,从BSD/386主机上向rstevens@mailhost.tuc.noao.edu发送邮件的步骤如下:
1.Sendmail向DNS询问主机mailhost.tuc.noao.edu的CNAME记录,发现存在一个CNAME记录:
在这里插入图片描述
2.发出noao.edu的CNAME记录查询,结果是不存在。

3.Sendmail发出noao.edu的MX记录查询,得到一个记录:
在这里插入图片描述
4.Sendmail发出noao.edu的A记录查询,得到返回值140.252.1.54。

5.发起一个到140.252.1.54的SMTP连接并发送邮件。

不会对MX记录中返回的数据尝试CNAME查询,因为MX记录中的主机名一定不是别名,而是一个具有A记录的主机名。

MX记录的另一个用途是在目的主机出故障时提供另一个邮件接收器,查看sun的MX记录发现它有两条:
在这里插入图片描述
优先级值较低的MX记录表明应首先尝试发送到主机本身,另一条是把邮件发送到主机noao.edu。

下例中,先关掉sun.tuc.noao.edu的SMTP服务器,然后从主机vangogh.cs.berkeley.edu向sun主机发邮件,当端口25上的连接请求到达时,TCP应响应一个RST,因为没有被动打开的进程在监听此端口:
在这里插入图片描述
连接未运行的SMTP服务器时,tcpdump的输出:
在这里插入图片描述
sun主机有两个IP地址。第一行vangogh向sun的第1个IP地址140.252.1.29的端口25发送一个SYN,第二行它被拒绝。然后vangogh尝试sun的第二个IP地址,它也产生一个RST。SMTP客户尝试第二个IP地址的原因在于它不区分第一次打开失败时的差错,如果第一次的差错类似主机不可达,那么第二次尝试或许可行。

如果SMTP客户主动打开失败的原因是服务器主机出故障了,我们则会看到SMTP客户向sun的第一个IP重传SYN总共75秒,然后向sun的第二个IP也总共重传75秒,150秒后客户会移到下一个具有更高优先级的MX记录。

VRFY命令无需发送邮件就可验证特定用户的邮件地址是否是在该服务器上。EXPN命令可获取服务器上的邮件表,即所有邮箱地址,可查看发送到别名的邮件实际到达的位置。许多SMTP实现(如旧版Sendmail)把两者看成一个。

连接到一个新的Sendmail版本:
在这里插入图片描述
上例中,我们故意在HELO命令中输入错误的主机名bsdi而非sun,许多SMTP服务器首先得到客户的IP地址,之后进行DNS指针查询,之后比较传来的主机名,这样服务器是基于IP地址注册到客户的连接,而非基于用户可能错误键入的主机名,有些服务器会用幽默的报文回答,如你是一个骗子、为什么叫你自己···,但上例中服务器只是通过指针查询打印出我们的真实域名和IP地址。

之后用一个无效的名字输入VRFY命令,服务器响应550差错,之后键入了有效的名字,服务器进行了回答。之后输入EXPN命令,得到了不同的回答,EXPN命令可获取到该用户的邮件是否被转发,并打印出转发的地址。

许多站点禁止VRFY和EXPN命令,有时因为隐私,有时因为安全漏洞,如向白宫的SMTP服务器发这两个命令:
在这里插入图片描述
RFC 1425定义了扩充的SMTP框架,称为扩充的SMTP(ESMTP),这些变化以向后兼容的方式被加入,不影响已有实现。如果客户想使用新特性,会通过EHLO而非HELO命令启动与一个服务器的会话,相兼容的服务器用250应答码响应,这个应答通常有好几行,每行都包含一个关键字和一个可选的参数,这些关键字指定了该服务器支持的SMTP扩充。

连接到一个支持ESMTP的服务器:
在这里插入图片描述
此服务器对EHLO命令的应答中列出了扩充命令EXPN、SIZE、HELP。SIZE命令会向服务器发送邮件报文的大小,这样服务器就可以选择是否接收此长度的报文,增加此命令的原因为,随着对非ASCII码(如图像、音频)等内容的支持,Internet邮件报文的长度在不断增大。

连接另一个支持ESMTP的服务器:
在这里插入图片描述
这个服务器也支持SIZE命令,且明确指出服务器不接受长度超过461M字节的报文。8BITMIME命令允许客户把可选参数BODY加到MAIL FROM命令中,从而指定正文中包含NVT ASCII还是8bit数据,除非客户收到服务器对于EHLO命令的8BITMIME关键字应答,否则禁止客户发送任何非NVT ASCII字符。该服务器的XADR关键字是以X开头的,任何以X开头的关键字都指的是本地SMTP扩充,即该选项时双方的约定,而非标准的。

向不支持EHLO命令的SMTP服务器发送EHLO:
在这里插入图片描述
客户会收到500应答而非250,客户此时应发送RSET命令,之后再发送一个HELO命令。

RFC 1522指明了一个在RFC 822报文首部中如何发送非ASCII字符的方法,目的是为了允许在发送方名、接收方名、主题中使用其他字符。

首部字段中可以包含编码字,它的格式如下:
在这里插入图片描述
其中charset是字符集,有效值有两种串us-ascii和iso-8859-x(x是单个数字)。

encoding是一个字符,用来指定编码方法,它支持两个值:
Q:目的是用于拉丁字符集。大多字符是作为NVT ASCII(最高位置0)发送的,任何要发送的字符若第8bit置1则将其作为3个字符发送,第一个字符是=,后跟着两个十六进制数,比如字符é,它的二进制8bit值为0xe9,作为三个字符发送就是=E9。空格通常作为下划线或三个字符=20发送。这样编码的原因是某些文本中含ASCII字符以外的特殊字符。

B:以64为基数的编码,即文本中的3个连续字节(24bit)被编码成4个6bit值,每个6bit值的编码如下,当要编码的个数不是3的倍数时,用=填充:
在这里插入图片描述
如以下编码:
在这里插入图片描述
能处理上述编码的用户代理将输出:
在这里插入图片描述
上图中主题行的前4个编码字符为SWYg,按图28-6将字符翻译为4个6bit值的二进制码:
在这里插入图片描述
然后把这24bit重新分组为3个8bit字节:
在这里插入图片描述
这三个8bit字节就是I、f、空格的ASCII表示。

RFC 822指定正文是NVT ASCII文本行,没有结构;RFC 1521扩充正文为允许把结构置入正文,这被称为MIME(Multipurpose Internet Mail Extensions,通用Internet邮件扩充)。

MIME不要求任何扩充,MIME加入了告知收件者正文结构的新标题,正文仍可以用NVT ASCII码来发送。与另一方交换MIME报文所需的一切就是双方都要有一个能理解MIME的用户代理,不需要MTA做任何改变。

MIME定义的5个新标题字段如下:
在这里插入图片描述
在这里插入图片描述
一个例子如下:
在这里插入图片描述
上图中,MIME版本是1.0,内容类型是无格式ASCII码文本,即Internet邮件的默认选择。PLAIN被认为是内容类型(TEXT)的一个子类型,charset是一个参数。
在这里插入图片描述
内容类型和用于内容传送的编码格式是互相独立的,前者由首部字段Content-Type指明,后者由首部字段Content-Transfer-Encoding指明,RFC 1521中定义了5种不同编码格式:
1.7bit:默认的NVT ASCII。
2.quoted-printable:上述首部字段中的编码字中,当encoding是Q时的编码方法。
3.base64:如图28-6。
4.8bit:某些是非ASCII字符且第8bit置1。
5.binary编码:二进制编码,可编码非ASCII字符和超出行长度限制的邮件。

对于RFC 821 MTA,只有前3种是有效的,因为前三种只包含NVT ASCII字符。只有8BITMIME支持的ESMTP服务器允许使用8bit编码。

尽管内容类型和编码独立,RFC 1521推荐有非ASCII数据的text使用quoted-printable,而image、audio、video、octet-stream application使用base64,这样可与符合RFC 821的MTA保持最大互操作性。multipart和message的内容类型必须以7bit编码。

以下是multipart内容类型的例子,它的子类型是mixed,含义为各部分是顺序处理的,且Boundary定义了各部分的边界字符串是NextPart
在这里插入图片描述
如上图,每个边界上可跟一行下一部分的首部字段。第一个边界后跟着一个空行而非首部,默认第一个边界和第二个边界之间的数据内容类型为具有us-ascii字符集的text/plain。

第二个边界后的首部字段中指定了另一个multipart报文,以字符串OtherAccess为分界,子类型为alternative,即有多种选择,第一种选择是用电子邮件获取RFC,第二种选项是用匿名FTP获取。MIME的用户代理将列出这两种选项,允许收件人选择一个,然后自动用电子邮件或匿名FTP获取一份RFC。

TCP是全双工协议,SMTP可以一次发送多个命令,但SMTP用半双工形式使用TCP,原因是有些SMTP实现每处理完一条命令就清除它们的输入缓存,使得SMTP不能全双工使用。

当域名的MX记录有多个目标地址且优先级相同时,表示轮循。

这篇关于TCP/IP详解 卷1:协议 学习笔记 第二十八章 SMTP:简单邮件传送协议的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/940649

相关文章

基于Qt开发一个简单的OFD阅读器

《基于Qt开发一个简单的OFD阅读器》这篇文章主要为大家详细介绍了如何使用Qt框架开发一个功能强大且性能优异的OFD阅读器,文中的示例代码讲解详细,有需要的小伙伴可以参考一下... 目录摘要引言一、OFD文件格式解析二、文档结构解析三、页面渲染四、用户交互五、性能优化六、示例代码七、未来发展方向八、结论摘要

Mysql 中的多表连接和连接类型详解

《Mysql中的多表连接和连接类型详解》这篇文章详细介绍了MySQL中的多表连接及其各种类型,包括内连接、左连接、右连接、全外连接、自连接和交叉连接,通过这些连接方式,可以将分散在不同表中的相关数据... 目录什么是多表连接?1. 内连接(INNER JOIN)2. 左连接(LEFT JOIN 或 LEFT

Java中switch-case结构的使用方法举例详解

《Java中switch-case结构的使用方法举例详解》:本文主要介绍Java中switch-case结构使用的相关资料,switch-case结构是Java中处理多个分支条件的一种有效方式,它... 目录前言一、switch-case结构的基本语法二、使用示例三、注意事项四、总结前言对于Java初学者

Linux内核之内核裁剪详解

《Linux内核之内核裁剪详解》Linux内核裁剪是通过移除不必要的功能和模块,调整配置参数来优化内核,以满足特定需求,裁剪的方法包括使用配置选项、模块化设计和优化配置参数,图形裁剪工具如makeme... 目录简介一、 裁剪的原因二、裁剪的方法三、图形裁剪工具四、操作说明五、make menuconfig

SpringBoot实现基于URL和IP的访问频率限制

《SpringBoot实现基于URL和IP的访问频率限制》在现代Web应用中,接口被恶意刷新或暴力请求是一种常见的攻击手段,为了保护系统资源,需要对接口的访问频率进行限制,下面我们就来看看如何使用... 目录1. 引言2. 项目依赖3. 配置 Redis4. 创建拦截器5. 注册拦截器6. 创建控制器8.

详解Java中的敏感信息处理

《详解Java中的敏感信息处理》平时开发中常常会遇到像用户的手机号、姓名、身份证等敏感信息需要处理,这篇文章主要为大家整理了一些常用的方法,希望对大家有所帮助... 目录前后端传输AES 对称加密RSA 非对称加密混合加密数据库加密MD5 + Salt/SHA + SaltAES 加密平时开发中遇到像用户的

Springboot使用RabbitMQ实现关闭超时订单(示例详解)

《Springboot使用RabbitMQ实现关闭超时订单(示例详解)》介绍了如何在SpringBoot项目中使用RabbitMQ实现订单的延时处理和超时关闭,通过配置RabbitMQ的交换机、队列和... 目录1.maven中引入rabbitmq的依赖:2.application.yml中进行rabbit

C语言线程池的常见实现方式详解

《C语言线程池的常见实现方式详解》本文介绍了如何使用C语言实现一个基本的线程池,线程池的实现包括工作线程、任务队列、任务调度、线程池的初始化、任务添加、销毁等步骤,感兴趣的朋友跟随小编一起看看吧... 目录1. 线程池的基本结构2. 线程池的实现步骤3. 线程池的核心数据结构4. 线程池的详细实现4.1 初

Python绘制土地利用和土地覆盖类型图示例详解

《Python绘制土地利用和土地覆盖类型图示例详解》本文介绍了如何使用Python绘制土地利用和土地覆盖类型图,并提供了详细的代码示例,通过安装所需的库,准备地理数据,使用geopandas和matp... 目录一、所需库的安装二、数据准备三、绘制土地利用和土地覆盖类型图四、代码解释五、其他可视化形式1.

SpringBoot使用Apache POI库读取Excel文件的操作详解

《SpringBoot使用ApachePOI库读取Excel文件的操作详解》在日常开发中,我们经常需要处理Excel文件中的数据,无论是从数据库导入数据、处理数据报表,还是批量生成数据,都可能会遇到... 目录项目背景依赖导入读取Excel模板的实现代码实现代码解析ExcelDemoInfoDTO 数据传输