Python网络编程——DNS
DNS 协议
域名系统(DNS, Domain Name Service)是成千上万互联网主机相互协作,对主机名与 IP 地址映射关系查询做出响应的一种机制。
DNS协议
目的: 解析主机名,返回 IP 地址
标准: RFC 1034与 RFC 1035
传输层协议: UDP/IP与 TCP/IP
端口号: 53
以 www.python.org 这一域名为例。如果网络浏览器需要解析该域名,那么浏览器就会运行一个类似于 getaddrinfo() 的调用,请求操作系统对该域名进行解析。系统本身知道其是否运行了自己的名称服务器,以及连接的网络是否会提供名称服务。如今,我们的机器常常会在连接网络的同时通过 DHCP 自动配置名称服务器信息。可以通过公司办公室或者教育机构的 LAN,也可以通过无线网络,还可以通过家庭电缆或 DSL 连接到网络。在其余情况下,系统管理员设置机器时会手动配置 DNS 服务器的 IP 地址。有时,由于人们对于 ISP 提供的 DNS 及其性能并不满意,他们会选择自己配置一个第三方的DNS服务器,比如说谷歌运行的8.8.8.8或8.8.4.4。即使不查询域名服务,计算机也知道一些主机名对应的 IP 地址。当我们调用内置函数 getaddrinfo() 时,操作系统做的第一件事并非是去向 DNS 服务器查询。如果使用的是 POSIX 系统,那么要查询的文件取决于 /tc/nsswitch.conf
文件的 hosts 条目,如果是 Windows,那么会取决于控制面板的选项。如 Linux 系统会先查询 /etc/conf
。 然后会尽可能地使用一种叫做多播 DNS 的专用协议。
假如我们的电脑里面并没有定义 www.python.org 这一域名,也没有在足够短的时间内访问过该域名(意味着并没有缓存),这时计算机就会去查询真正的域名解析服务器,它通常会发一个基于 UDP 的 DNS 数据查询包。DNS 服务器它会首先检查自己最近查询域名的缓存,如果有就可以马上返回 IP 地址。如果没有,就会从世界上 DNS 服务器层级结构的最顶层开始递归查询 www.python.org。根节点的名称服务器可以识别所有的顶级域名(TLD),如.com, .net 并且存储了负责相应顶级域名的服务群信息。为了能够在实际连接至域名系统之前找到域名服务器,名称服务器软件通常内置了这些顶级服务器的 IP 地址。这样经过一次 UDP 往返之后 DNS 服务器就能够获取保存完整 .org 域名索引的服务器了。
现在将发送第二个 DNS 请求,这次是发给某一个 .org 服务器,用来询问保存 python.org 域名信息的服务器,可以使用 whois 工具来获取相关信息(电脑里没这个命令的话可以在web上查)
类似这个样子:
无论我们在哪里,我们对任何属于 python.org 的主机名的 DNS 请求都会被发到里面的 DNS 服务器当中的一个,他们会直接返回查询结果,这样DNS 服务器就会向浏览器返回一个包含 www.python.org 的 IP 地址的 UDP 包。根据不同的配置,DNS 服务器往往还需要进行查询的次数也不同,有可能在进行一次查询就够用了(第三次查询),但如果机构较为庞大,很多部门都有自己的子 DNS 服务器,就可能把请求分配到各个部门的自己的 DNS 服务器,这就需要更多次查询。
一个解析邮箱域名的例子:
1 | import argparse |
简要的来说,解析邮箱域名的规则是这样的:
如果存在 MX 记录,就必须尝试与这些 SMTP 服务器进行通信。如果没有任何 SMTP 服务接收消息,那么必须向用户返回一个错误(或者将该消息放入重发队列里)。如果优先级不同,那就按照优先级序号从大到小尝试这些服务器。如果不存在 MX 记录,但是域名提供了 A 或 AAAA 记录,那么可以尝试向这些记录发起连接,如果域名没有提供上述任何一记录但给出了 CNAME,那么应该使用相同的规则搜索该 CNAME 对应的 MX 记录或 A 记录。
MX: 邮件交换记录 (MX record)是域名系统(DNS)中的一种资源记录类型,用于指定负责处理发往收件人域名的邮件服务器。MX记录允许设置一个优先级,当多个邮件服务器可用时,会根据该值决定投递邮件的服务器。简单邮件传输协议(SMTP)会根据MX记录的值来决定邮件的路由过程。
CNAME: CNAME 被称为规范名字。这种记录允许您将多个名字映射到同一台计算机。 通常用于同时提供 WWW 和 MAIL 服务的计算机。例如,有一台计算机名为"r0WSPFSx58."(A记录)。 它同时提供 WWW 和 MAI L服务,为了便于用户访问服务。可以为该计算机设置两个别名(CNAME):WWW 和 MAIL
更好的方法当然是使用我们的 getsockaddr() 而不是自己尝试解析服务器的主机名。
Python网络编程——DNS