Tao's Blog

Python运维学习之域名系统(NDS)

域名系统(Domain Name System)是域名与ip地址相互映射的一种分布式数据库,用来将主机名缓缓成ip地址。DNS协议运行在UDP协议之上,使用53端口。
简单点来说,在web中是使用IP地址来实现互联的,然而使用纯数字作为访问网站的方式显然有些反人类,DNS就应运而生,使用容易记住的域名与IP地址一一对应起来,即DNS就是将一个域名翻译为一个IP地址。

使用python内置的socket模块实现访问DNS

查找服务器地址:getaddrinfo()

该方法可以这样定义:

1
2
3
4
import socket
socket.getaddrinfo(host, port [, family, socktype, proto, flags])
# 返回的是像这样一个列表
# (family, sockeype, proto, canonname, sockaddr)

其中:
host: 主机/域名
prot: 端口号或者服务名
family: 使用的协议簇。常见的协议簇AF_UNIX(UNIX域,是一种POSIX兼容系统上的进程见通信协议);AF_INET(IPv4);AF_INET6(IPv6);其中在scoket模块中AF_UNIX(1), AF_INET(2), AF_INET6(10), 特别的, family为0时,表示支持所有family
socktype: socket的类型。常见类型: SOCK_DGRAM(UDP), SOCK_STREAM(TCP) SOCK_RAW(原始套接字),在该方法中分别用1, 2, 3表示
proto: 协议通常为0,一般忽略该参数
flags: 以AI开头,会影响函数的返回值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/bin/python
# -*- coding: utf-8 -*-
"""
get_address_info.py
"""
import sys
import socket
res = socket.getaddrinfo(sys.argv[1], None)
# 由于getaddrinfo方法返回是一个有5个元素的元祖
# 而我们需要的服务器地址最后一个元素
# 其中一个域名下可能有多个IP做负载均衡等
for i in res:
print i[-1]

# python get_address_info.py www.baidu.com

1
2
3
4
5
6
('61.135.169.121', 0)
('61.135.169.121', 0)
('61.135.169.121', 0)
('61.135.169.125', 0)
('61.135.169.125', 0)
('61.135.169.125', 0)

注意: 这是因为getaddrinfo()方法会根据它所支持的不同协议返回一个结果(windows默认只支持一种协议)
当然在Linux下也可以限制返回结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/bin/python
# -*- coding: utf-8 -*-
"""
get_address_info_1.py
"""
import sys
import socket
res = socket.getaddrinfo(sys.argv[1], None, 0, socket.SOCK_STREAM)
for i in res:
print i[-1]

# python get_address_info_1.py www.baidu.com

1
2
('61.135.169.121', 0)
('61.135.169.125', 0)

反向查询gethostbyaddr()

就是只知道ip的情况下,确定该ip对应的主机名.
但是并不是每一个ip都对应一个域名.所以在反向查找中需要捕捉和处理socket.herror异常
先看一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/bin/python
# -*- coding: utf-8 -*-
"""
get_host_by_address.py
"""
import sys
import socket
try:
res = socket.gethostbyaddr(sys.argv[1])
# ('localhost', ['localhost.localdomain', 'localhost4', 'localhost4.localdomain4'], ['127.0.0.1'])
print "Primary hostname: "
pirnt "\t%s" % res[0]
print "\nAddresses: "
for item in res[2]:
print "\t%s" % item
except socket.herror as e:
print "Couldn't get name: %s" % e

使用第三方插件PyDNS高级查询

有关DNS records的几个记录说明

记录(records) 说明
A 给出一个主机名的IPV4地址
AAAA 给出一个主机名的IPV6地址
CNAME 定义别名
MX 指定优先选择的邮件交换服务器的顺序
PTR 为一个IP地址提供主机名(用于反向解析)
NS 为一个域定义名称服务器
TXT 存储主机的一些信息
SOA 存储起始授权机构(Start Of Authority)有关信息

PyDNS安装

# pip install pydns
测试:
# python -c "import DNS"
若无消息提示,则安装成功

简单的PyDNS查询
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
#!/bin/python
# -*- coding: utf-8 -*-
"""
pydns_basic.py
"""
import sys
import DNS
# 初始化一个名称服务器
DNS.DiscoverNameServers()
# 创建一个请求对象(request object),该对象可以发出任何DNS查询请求
request_object = DNS.Request()
# 该请求对象下的req(name, qtype)方法用来执行实际的查询, 其中name为实际查询的名称,qtype为指定查询的何种记录类型. 该方法会返回一个应答对象(answer object),应答对象有个answer属性, 返回一个应答列表
# 此处的ANY记录表示对所有记录的请求
answer_object = request_object.req(name=sys.argv[1], qtype=DNS.Type.ANY)
res = answer_object.answers
if not len(res):
print "Not found."
for item in res:
print "%s\t%s" % (item['typename'], item['data'])

# python pydns_basic.py qq.com

1
2
3
4
5
6
7
8
9
10
TXT ['v=spf1 include:spf.mail.qq.com ~all']
MX (30, 'mx1.qq.com')
MX (10, 'mx3.qq.com')
MX (20, 'mx2.qq.com')
NS ns3.qq.com
NS ns1.qq.com
NS ns4.qq.com
NS ns2.qq.com
A 125.39.240.113
A 61.135.157.156

# python pydns_basic.py www.qq.com

1
2
AAAA $á(
A 175.155.116.108

查询特殊的名称服务器

我们可以直接向该域张权威的名称服务器发送查询, 所以我们余姚使用系统默认的名称服务器来查找权威名称服务器.就是通过查找接近当前域的NS records来实现.例如,查询www.server.example.com,它首先查找该名字的NS records,接着通过server.example.com向上查找直到.com

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
#!/bin/python
# -*- coding: utf-8 -*-
"""
pydns_any.py
"""
import sys
import DNS
# 给定要查询的域名name,记录类型qtype
def hierarchy_query(name, qtype):
request_object = DNS.Request()
try:
answer_object = request_object.req(name=name, qtype=qtype)
answers = [x['data'] for x in answer_object.answers if x['type']==qtype]
except DNS.Base.DNSError:
answers = []
if len(answers):
return answers
else:
remainder = name.split(".", 1)
if len(remainder) == 1:
return None
else:
return hierarchy_query(remainder[1], qtype)
def find_nameservers(hostname):
return hierarchy_query(hostname, DNS.Type.NS)
def get_records_from_nameserver(name, qtype, ns_list):
for ns in ns_list:
request_object = DNS.Request(server=ns)
try:
answers = request_object.req(name=name, qtype=qtype).answers
if len(answers):
return answers
except DNS.Base.DNSError:
pass
return []
def ns_lookup(name, qtype, verbose=1):
ns_list = find_nameservers(name)
if ns_list == None:
raise RuntimeError, "Could not find nameserver to use"
if verbose:
print "Using nameservers:", ", ".join(ns_list)
return get_records_from_nameserver(name, qtype, ns_list)
if __name__ == "__main__":
query = sys.argv[1]
DNS.DiscoverNameServers()
answers = ns_lookup(query, DNS.Type.ANY)
if not len(answers):
print "Not found."
for item in answers:
print "%s\t%s" % (item['typename'], item['data'])

#python pydns_any.py sina.com

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Using nameservers: ns1.sina.com.cn, ns2.sina.com.cn, ns3.sina.com.cn, ns4.sina.com, ns3.sina.com, ns4.sina.com.cn, ns2.sina.com, ns1.sina.com
A 66.102.251.33
TXT ['v=spf1 include:spf.sinamail.sina.com.cn -all']
SOA ('ns1.sina.com.cn', 'zhihao.staff.sina.com.cn', ('serial', 2005042601), ('refresh ', 900, '15 minutes'), ('retry', 300, '5 minutes'), ('expire', 604800, '1 weeks'), ('minimum', 300, '5 minutes'))
NS ns1.sina.com
NS ns2.sina.com.cn
NS ns2.sina.com
NS ns3.sina.com.cn
NS ns3.sina.com
NS ns4.sina.com
NS ns1.sina.com.cn
NS ns4.sina.com.cn
MX (5, 'freemx1.sinamail.sina.com.cn')
MX (10, 'freemx2.sinamail.sina.com.cn')
MX (10, 'freemx3.sinamail.sina.com.cn')
分解查询结果

有些records,特别是NS PTR CNAME MX—返回的数据中包含另一个主机名,为了得到最终的IP地址,需要解析返回的信息.在下面的代码中需要import上一个程序的代码

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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#!/bin/python
# -*- coding: utf-8 -*-
import sys
import re
import DNS
import pydns_any
def get_reverse(query):
if re.search('^\d+\.\d+\.\d+\.\d+$', query):
octets = query.split('.')
octets.reverse()
return '.'.join(octets) + '.IN-ADDR.ARPA'
return None
def format_line(index, typename, descr, data):
retval = "%-2s %-5s" % (index, typename)
data = data.replace('\n', '\n ')
if descr != None and len(descr):
retval += "%-12s" % (descr + ":")
return retval + " " + data
if __name__ == "__main__":
DNS.DiscoverNameServers()
queries = [(sys.argv[1], DNS.Type.ANY)]
done_queries = []
descriptions = {
'A': 'IP address',
'TXT': 'Data',
'PTR': 'Host name',
'CNAME': 'Alias for',
'NS': 'Name server'
}
while len(queries):
(query, qtype) = queries.pop(0)
if query in done_queries:
continue
done_queries.append(query)
print "-" * 77
print "Results for %s (lookup type %s)" % (query, DNS.Type.typestr(qtype))
print
rev = get_reverse(query)
if rev:
print "IP address given; dong reverse lookup using", rev
query = rev
answers = pydns_any.ns_lookup(query, qtype, verbose=0)
if not len(answers):
print "Not found."
count = 0
for answer in answers:
count += 1
if answer['typename'] == "MX":
print format_line(count,
answer['typename'],
"Mail server",
"%s, priority %d" % (answer['data'][1], answer['data'][0])
)
queries.append((answer['data'][1], DNS.Type.A))
elif answer['typename'] == 'SOA':
data = "\n" + "\n".join([str(x) for x in answer['data']])
print format_line(count, 'SOA', 'Start of authority', data)
elif answer['typename'] in descriptions:
print format_line(count, answer['typename'], descriptions[answer['typename']], str(answer['data']))
else:
print format_line(count, answer['typename'], None, str(answer['data']))
if answer['typename'] in ['CNAME', 'PTR']:
queries.append((answer['data'], DNS.Type.ANY))
if answer['typename'] == 'NS':
queries.append((answer['data'], DNS.Type.A))

  1. 正向查询
    # python pydns_query.py sina.com
    结果如下:

    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
    65
    66
    67
    68
    69
    -----------------------------------------------------------------------------
    Results for sina.com (lookup type ANY)
    1 A IP address: 66.102.251.33
    2 TXT Data: ['v=spf1 include:spf.sinamail.sina.com.cn -all']
    3 SOA Start of authority:
    ns1.sina.com.cn
    zhihao.staff.sina.com.cn
    ('serial', 2005042601)
    ('refresh ', 900, '15 minutes')
    ('retry', 300, '5 minutes')
    ('expire', 604800, '1 weeks')
    ('minimum', 300, '5 minutes')
    4 NS Name server: ns3.sina.com.cn
    5 NS Name server: ns4.sina.com
    6 NS Name server: ns2.sina.com
    7 NS Name server: ns3.sina.com
    8 NS Name server: ns1.sina.com
    9 NS Name server: ns2.sina.com.cn
    10 NS Name server: ns1.sina.com.cn
    11 NS Name server: ns4.sina.com.cn
    12 MX Mail server: freemx2.sinamail.sina.com.cn, priority 10
    13 MX Mail server: freemx3.sinamail.sina.com.cn, priority 10
    14 MX Mail server: freemx1.sinamail.sina.com.cn, priority 5
    -----------------------------------------------------------------------------
    Results for ns3.sina.com.cn (lookup type A)
    1 A IP address: 123.125.29.99
    -----------------------------------------------------------------------------
    Results for ns4.sina.com (lookup type A)
    1 A IP address: 123.125.29.99
    -----------------------------------------------------------------------------
    Results for ns2.sina.com (lookup type A)
    1 A IP address: 114.134.80.145
    -----------------------------------------------------------------------------
    Results for ns3.sina.com (lookup type A)
    1 A IP address: 61.172.201.254
    -----------------------------------------------------------------------------
    Results for ns1.sina.com (lookup type A)
    1 A IP address: 114.134.80.144
    -----------------------------------------------------------------------------
    Results for ns2.sina.com.cn (lookup type A)
    1 A IP address: 61.172.201.254
    -----------------------------------------------------------------------------
    Results for ns1.sina.com.cn (lookup type A)
    1 A IP address: 202.106.184.166
    -----------------------------------------------------------------------------
    Results for ns4.sina.com.cn (lookup type A)
    1 A IP address: 121.14.1.22
    -----------------------------------------------------------------------------
    Results for freemx2.sinamail.sina.com.cn (lookup type A)
    1 A IP address: 180.149.134.158
    -----------------------------------------------------------------------------
    Results for freemx3.sinamail.sina.com.cn (lookup type A)
    1 A IP address: 60.28.113.250
    -----------------------------------------------------------------------------
    Results for freemx1.sinamail.sina.com.cn (lookup type A)
    1 A IP address: 202.108.35.47
  2. 反向查询:
    python pydns_query.py 65.215.221.149
    结果如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    -----------------------------------------------------------------------------
    Results for 65.215.221.149 (lookup type ANY)
    IP address given; dong reverse lookup using 149.221.215.65.IN-ADDR.ARPA
    1 PTR Host name: www.apress.com
    -----------------------------------------------------------------------------
    Results for www.apress.com (lookup type ANY)
    1 CNAMEAlias for: prod.springer.map.fastlylb.net
    -----------------------------------------------------------------------------
    Results for prod.springer.map.fastlylb.net (lookup type ANY)
    1 A IP address: 151.101.100.250

总结

DNS用于在文字名称和底层通信的IP地址之间转换.
正向查询就是把文字域名翻译成数字的IP地址,它负责连接远程服务。
反向查询就是把IP地址翻译为远程主机的主机名。

本文链接: http://taolichuan.com/2016/11/16/python网络编程基础笔记:域名系统/  转载请注明转载自: 陶之夭夭