Python网络编程——DNS

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

无论我们在哪里,我们对任何属于 python.org 的主机名的 DNS 请求都会被发到里面的 DNS 服务器当中的一个,他们会直接返回查询结果,这样DNS 服务器就会向浏览器返回一个包含 www.python.org 的 IP 地址的 UDP 包。根据不同的配置,DNS 服务器往往还需要进行查询的次数也不同,有可能在进行一次查询就够用了(第三次查询),但如果机构较为庞大,很多部门都有自己的子 DNS 服务器,就可能把请求分配到各个部门的自己的 DNS 服务器,这就需要更多次查询。

一个解析邮箱域名的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
import argparse
import dns.resolver


# pip install dnspython3

def resolve_hostname(hostname, indent=""):
"""
print an A or AAAA for `hostname`; follow CNAMEs if necessary.
:param hostname:
:param indent:
:return:
"""

indent = indent + " "
answer = dns.resolver.query(hostname, 'A') # A 代表 ipv4地址
if answer.rrset is not None:
for record in answer:
print(indent, hostname, "has A address ", record.address)
return
answer = dns.resolver.query(hostname, "AAAA") # AAAA 代表 ipv6 地址
if answer.rrset is not None:
for record in answer:
print(indent, hostname, "has AAAA address ", record.address)
return

answer = dns.resolver.query(hostname, "CNMAE")
if answer.rrset is not None:
record = answer[0]
cname = record.address
print(indent, hostname, "is a CNAME alias for ", cname)
resolve_hostname(cname, indent) # 递归查询
return
print(indent, "ERROR: no A, AAAA, or a CNMAE alias for ", hostname)


def resolve_email_domain(domain):
"""
For an email address `name@domain` find its mail server IP address
:param domain:
:return:
"""

try:
answer = dns.resolver.query(domain, "MX", raise_on_no_answer=False)
except dns.resolver.NXDOMAIN:
print("ERROR: No such domain ", domain)
return
if answer.rrset is not None:
records = sorted(answer, key=lambda record: record.perference)
for record in records:
name = record.exchange.to_text(omit_final_dot=True)
print("Priority ", record.perference)
resolve_hostname(name)
else:
print("This domain has no explicit MX records")
print("Attempting to resolve it as an A, AAAA, or CNAME")
resolve_hostname(domain)


if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Find mail-server IP address")
parser.add_argument("domain", help="domain that you want to send mail to")
resolve_email_domain(parser.parse_args().domain)

简要的来说,解析邮箱域名的规则是这样的:

如果存在 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() 而不是自己尝试解析服务器的主机名。

Author

Ctwo

Posted on

2020-10-09

Updated on

2020-10-25

Licensed under

Comments