python網(wǎng)絡(luò)編程學(xué)習(xí)筆記(四):域名系統(tǒng)
一、什么是域名系統(tǒng)
DNS 計(jì)算機(jī)域名系統(tǒng) (DNS) 是由解析器以及域名服務(wù)器組成的。當(dāng)我們?cè)谏暇W(wǎng)的時(shí)候,通常輸入的是網(wǎng)址,其實(shí)這就是一個(gè)域名,而我們計(jì)算機(jī)網(wǎng)絡(luò)上的計(jì)算機(jī)彼此之間只能用IP地址才能相互識(shí)別。再如,我們?nèi)ヒ籛EB服務(wù)器中請(qǐng)求一WEB頁(yè)面,我們可以在瀏覽器中輸入網(wǎng)址或者是相應(yīng)的IP地址,例如我們要上新浪網(wǎng),我們可以在IE的地址欄中輸入網(wǎng)址,也可輸入IP地址,但是這樣子的IP地址我們記不住或說(shuō)是很難記住,所以有了域名的說(shuō)法,這樣的域名會(huì)讓我們?nèi)菀椎挠涀 ?br>
|
名稱(chēng) |
含義 |
特性 |
|
域名服務(wù)器 |
保存有該網(wǎng)絡(luò)中所有主機(jī)的域名和對(duì)應(yīng)IP地址,并具有將域名轉(zhuǎn)換為IP地址功能的服務(wù)器。 |
域名必須對(duì)應(yīng)一個(gè)IP地址,而IP地址不一定只對(duì)應(yīng)一個(gè)域名,采用類(lèi)似目錄樹(shù)的等級(jí)結(jié)構(gòu)。 |
|
域名解析服務(wù)器 |
域名與IP地址之間的轉(zhuǎn)換工作 |
域名解析過(guò)程中的查詢(xún)順序?yàn)椋罕镜鼐彺嬗涗?、區(qū)域記錄、轉(zhuǎn)發(fā)域名服務(wù)器、根域名服務(wù)器。 |
二、訪問(wèn)DNS的方法一:使用socket模塊
1、DNS查詢(xún)
以查詢(xún)www.external.example.com為例。首先,程序會(huì)和操作系統(tǒng)配置文件指定的本地名稱(chēng)服務(wù)器通信。這個(gè)服務(wù)器是一個(gè)遞歸的名稱(chēng)服務(wù)器,它收到請(qǐng)求并以適當(dāng)?shù)姆绞絺鬟f下去。遞歸服務(wù)器做的第一件事情是詢(xún)問(wèn).com域,回答是以一種指向另外一外名稱(chēng)服務(wù)器的提名形式給出的。這個(gè)名稱(chēng)服務(wù)器可以提供名稱(chēng)中包含.com的信息。查詢(xún)發(fā)送到該服務(wù)器后,該服務(wù)器將以另一個(gè)提名回答進(jìn)行回應(yīng),指向另外一臺(tái)服務(wù)器,而這個(gè)服務(wù)器可以提供example.com的名稱(chēng)信息。這個(gè)循環(huán)重復(fù)多次,直到查詢(xún)到external.example.com服務(wù)的名稱(chēng)服務(wù)器。
2、正向查詢(xún)
最基本的查詢(xún)是正向查詢(xún),即根據(jù)一個(gè)主機(jī)名來(lái)查找ip地址。Socket庫(kù)可以實(shí)現(xiàn)這種查詢(xún),主要用函數(shù)socket.getaddrinfo()。注意,該函數(shù)和ipv6不兼容。
Getaddrinfo(host,port[,family[,sockettype[,proto[,flags]]]])
參數(shù)host為域名,以字符串形式給出代表一個(gè)IPV4/IPV6地址或者None.
參數(shù)port如果字符串形式就代表一個(gè)服務(wù)名,比如“http”"ftp""email"等,或者為數(shù)字,或者為None
參數(shù)family為地主族,可以為AF_INET ,AF_INET6 ,AF_UNIX.
參數(shù)socketype可以為SOCK_STREAM(TCP)或者SOCK_DGRAM(UDP)
參數(shù)proto通常為0可以直接忽略
參數(shù)flags為AI_*的組合,比如AI_NUMERICHOST,它會(huì)影響函數(shù)的返回值
該函數(shù)返回值是一列tuple,每一個(gè)tuple如下:
(family,socktype,proto,canonname,sockaddr)
其中sockaddr實(shí)際上就是遠(yuǎn)程機(jī)器的地址和端口,也就是查詢(xún)的數(shù)據(jù)。
例如:
>>> import socket
>>> print socket.getaddrinfo('www.baidu.com',None)
[(2, 0, 0, '', ('61.135.169.125', 0)), (2, 0, 0, '', ('61.135.169.105', 0))]
>>> print socket.getaddrinfo('www.baidu.com',None)[0][4][0]
61.135.169.125
>>> print socket.getaddrinfo('www.baidu.com',None)[0][4][1]
0
注意:因?yàn)橐粋€(gè)網(wǎng)站可能有多個(gè)網(wǎng)址,所以?xún)纱尾樵?xún)時(shí),結(jié)果不同也是很正常的。這里用一段代碼將所有查詢(xún)結(jié)果列出:
##@小五義
import socket
host=raw_input('host:')
result=socket.getaddrinfo(host,None)
counter=0
for i in result:
print "%-2d:%s"%(counter,i[4])
counter+=1
運(yùn)行結(jié)果如下:
>>>
host:www.baidu.com
0 :('61.135.169.105', 0)
1 :('61.135.169.125', 0)
>>>
host:www.yahoo.com
0 :('106.10.170.118', 0)
>>>
host:www.163.com
0 :('60.210.18.169', 0)
1 :('123.132.254.15', 0)
3、反向查詢(xún)
反向查詢(xún)是指通過(guò)ip地址查詢(xún)域名。這里用到gethostbyaddr
gethostbyaddr(addr,len,type)
參數(shù)addr可以為IPv4或IPv6的IP地址,參數(shù)len為參數(shù)addr的長(zhǎng)度,參數(shù)type為AF_INET。
下面給出的例子,將反向查詢(xún)ip地址的域名。
##@小五義
import socket
hostip=raw_input('ip:')
try:
result=socket.gethostbyaddr(hostip)
print "hostname:"+result[0]
print "\n Addresses:"
for i in result[2]:
print " " +i
except socket.herror, e:
print "counld not look up name:",e
運(yùn)行結(jié)果是:
>>>
ip:127.0.0.1
hostname:localhost
Addresses:
127.0.0.1
>>>
ip:216.109.118.73
hostname:gi-2-19.bas1-1-con.ac2.yahoo.com
Addresses:
216.109.118.73
>>>
ip:123.132.254.15
counld not look up name: [Errno 11004] host not found
>>>
ip:60.210.18.169
counld not look up name: [Errno 11004] host not found
從運(yùn)行的結(jié)果看,第一次查詢(xún)到的兩個(gè)ip放進(jìn)去,卻反向查詢(xún)不到域名,這里我也沒(méi)搞明白是什么原因,有待高手解答。
三、訪問(wèn)DNS的方法二:使用PyDNS進(jìn)行高級(jí)查詢(xún)
pyDNS提供了一個(gè)功能更強(qiáng)的訪問(wèn)DNS系統(tǒng)的接口。其下載地址為http://pydns.sourceforge.net。其中py3dns是針對(duì)python3.x的,本人的學(xué)習(xí)環(huán)境是python2.6,所以就下載安裝了pydns。下載后解壓,將DNS文件夾拷貝到Python安裝文件夾下的Lib\site-packages\文件夾下即可。
1、簡(jiǎn)單的pyDNS查詢(xún)
首先調(diào)用DNS.DiscoverNameServers()查找系統(tǒng)中的名稱(chēng)服務(wù)器。然后建立一個(gè)請(qǐng)求對(duì)象DNS.Request()。DNS.Request()的req()方法用來(lái)執(zhí)行實(shí)際的查詢(xún)。通常有兩個(gè)參數(shù):name給出了實(shí)際查詢(xún)的名稱(chēng);qtype指定了record類(lèi)型。當(dāng)使用請(qǐng)求對(duì)象來(lái)發(fā)出查詢(xún)請(qǐng)求時(shí),pyDNS會(huì)返回一個(gè)包含結(jié)果的應(yīng)答對(duì)象(answer object),該對(duì)象有個(gè)屬性叫answers,包含所有返回的應(yīng)答列表。
在給出例子前,首先列出大多數(shù)的DNS records列表如下:
A Address. 網(wǎng)址記錄(定在右邊), 定義於 RFC 1035.
AAAA IPv6 Address. (第 6 代網(wǎng)址表式法). 定義於 RFC 1886.
AFSDB AFS Data Dase location. 定義於 RFC 1183.
CNAME Canonical Name (正式名稱(chēng)), 定義於 RFC 1035. 這是定別名(alias)的指標(biāo)用法. 別名設(shè)定在 LHS, 正式名稱(chēng)設(shè)定在 RHS.
GPOS Geographic POSition (地理位置)?, 定義於 RFC 1712. 過(guò)時(shí)(obsolete)用法, 不建議使用. .
HINFO Host INFOrmation (機(jī)器基本資料; OS, 硬體, ...), 定義於 RFC 1035.
ISDN ISDN (整合數(shù)位網(wǎng)路網(wǎng)址表示法), 定義於 RFC 1183.
KEY publick key (公開(kāi)金匙; DNS 資料, 透過(guò)編碼, 密碼通訊), 定義於 RFC 2065.
LOC LOCation (網(wǎng)路所在的地理區(qū)域; 表經(jīng)緯度), 定於 RFC 1876.
MB MailBox. (信箱; 已經(jīng)很少使用), 定義於 RFC 1035. --> 參考 MX 記錄項(xiàng)目.
MD 定義於 RFC 1035. 過(guò)時(shí)(obsolete)用法, 不建議使用. --> 參考 MX 記錄項(xiàng)目.
MF 定義於 RFC 1035. 過(guò)時(shí)(obsolete)用法, 不建議使用. --> 參考 MX 記錄項(xiàng)目.
MG 定義於 RFC 1035.
MINFO 定義於 RFC 1035.
MR 定義於 RFC 1035.
MX Mail eXchanger. (電子郵件, 交寄設(shè)定). 定義於 RFC 1035. 基本用法是, 當(dāng)一個(gè) email address 包含某一筆 MX 記錄的 LHS時(shí), 那麼 email 傳遞系統(tǒng)會(huì), 將該電子郵件, 轉(zhuǎn)交給該筆 MX 的 RHS 所指示的系統(tǒng), 去進(jìn)一步處理.
NULL 空記錄 ( 如空白行等; 無(wú)實(shí)際用途), 定義於 RFC 1035.
NS Name Server (表示 RHS 為一領(lǐng)域名稱(chēng)伺服機(jī)器), 定義於 RFC 1035.
NSAP Network Services Access Point address. ( ISO-OSI 的網(wǎng)路服務(wù), 網(wǎng)址表示法) 定義於 RFC 1348, 另外又分別經(jīng)過(guò) RFC 1637, 1706 兩次重新定義.
NSAP-PTR 定義於 RFC 1348. 過(guò)時(shí)用法.
NXT 定義於 RFC 2065.
PTR PoinTeR. ( 指標(biāo) ), 定義於 RFC 1035. 通常用於將 IP addr. 只回到某一個(gè)對(duì)應(yīng) 的 domain name.
下面是一個(gè)簡(jiǎn)單的例子:
##@小五義
# -*- coding: cp936 -*-
import DNS
query=raw_input('輸入DNS:')
DNS.DiscoverNameServers()
reqobj=DNS.Request()
answerobj=reqobj.req(name=query,qtype=DNS.Type.ANY)
if not len(answerobj.answers):
print "Not found"
for i in answerobj.answers:
print "%-5s %s"%(i['typename'],i['data'])
運(yùn)行結(jié)果:
輸入DNS:163.com
TXT ['v=spf1 include:spf.163.com -all']
A 123.58.180.8
A 123.58.180.5
A 123.58.180.6
A 123.58.180.7
MX (10, '163mx03.mxmail.netease.com')
MX (50, '163mx00.mxmail.netease.com')
MX (10, '163mx01.mxmail.netease.com')
MX (10, '163mx02.mxmail.netease.com')
NS ns2.nease.net
NS ns4.nease.net
NS ns3.nease.net
NS ns1.nease.net
輸入DNS:www.yahoo.com
CNAME fd-fp3.wg1.b.yahoo.com
2、查詢(xún)特殊的名稱(chēng)服務(wù)器
前面的例子中,對(duì)ANY類(lèi)型的查詢(xún),有種特殊情況,就是如果不事先請(qǐng)求,有時(shí)候MX records會(huì)丟失。因此,正常情況下,不會(huì)使用ANY。解決方法是跳過(guò)本地名稱(chēng)服務(wù)器,直接向該域中權(quán)威的名稱(chēng)服務(wù)器發(fā)送查詢(xún)。為了這么做,需要使用系統(tǒng)默認(rèn)的名稱(chēng)服務(wù)器來(lái)查找權(quán)威名稱(chēng)服務(wù)器。這是通過(guò)查找接近于當(dāng)前域的NS records來(lái)實(shí)現(xiàn)的。下面的例子:
##@小五義
# -*- coding: cp936 -*-
import DNS
def hierquery(qstring,qtype):
reqobj=DNS.Request()
try:
print query
answerobj=reqobj.req(name=query,qtype=qtype)
answers=[x['data'] for x in answerobj.answers if x['type']==qtype]
print answers
except DNS.Base.DNSError:
answers=[]
if len(answers):
return answers
else:
remainder=qstring.split(".",1)
if len(remainder)==1:
return None
else:
return hierquery(remainder[1],qtype)
def findnameservers(hostname):
return hierquery(hostname,DNS.Type.NS)
def getrecordsfromnameserver(qstring,qtype,nslist):
for ns in nslist:
reqobj=DNS.Request(server=ns)
try:
answers=reqobj.req(name=qstring,qtype=qtype).answers
if len(answers):
return answers
except DNS.Base.DNSError:
pass
return []
def nslookup(qstring,qtype,verbose=1):
nslist=findnameservers(qstring)
if nslist==None:
raise RuntimeError,'找不到服務(wù)器'
if verbose:
print "服務(wù)器:",",".join(nslist)
return getrecordsfromnameserver(qstring,qtype, nslist)
if __name__=='__main__':
query=raw_input('輸入網(wǎng)站:')
DNS.DiscoverNameServers()
answers=nslookup(query,DNS.Type.ANY)
if not len(answers):
print "未找到!"
for i in answers:
print "%-5s %s"%(i['typename'],i['data'])
運(yùn)行結(jié)果如下:
輸入網(wǎng)站:163.com
服務(wù)器: ns3.nease.net,ns1.nease.net,ns2.nease.net,ns4.nease.net
A 123.58.180.8
A 123.58.180.5
A 123.58.180.6
A 123.58.180.7
MX (10, '163mx02.mxmail.netease.com')
MX (10, '163mx03.mxmail.netease.com')
MX (50, '163mx00.mxmail.netease.com')
MX (10, '163mx01.mxmail.netease.com')
TXT ['v=spf1 include:spf.163.com -all']
NS ns4.nease.net
NS ns1.nease.net
NS ns2.nease.net
NS ns3.nease.net
SOA ('ns4.nease.net', 'admin.nease.net', ('serial', 20014505), ('refresh ', 801, '13 minutes'), ('retry', 3600, '1 hours'), ('expire', 604800, '1 weeks'), ('minimum', 18000, '5 hours'))
輸入網(wǎng)站:baidu.com
服務(wù)器: dns.baidu.com,ns4.baidu.com,ns2.baidu.com,ns3.baidu.com
SOA ('dns.baidu.com', 'sa.baidu.com', ('serial', 2012081509), ('refresh ', 300, '5 minutes'), ('retry', 300, '5 minutes'), ('expire', 2592000, '4 weeks'), ('minimum', 7200, '2 hours'))
TXT ['v=spf1 ip4:61.135.163.0/24 ip4:220.181.50.0/24 ip4:220.181.18.241 ip4:61.208.132.13 ip4:220.181.27.29 ip4:202.108.22.171 ip4:61.135.162.0/24 ip4:220.181.5.0/24 ip4:123.125.66.0/24 ip4:61.135.168.0/24 a mx ptr ~all']
A 123.125.114.144
A 220.181.111.85
A 220.181.111.86
MX (20, 'jpmx.baidu.com')
MX (20, 'mx50.baidu.com')
MX (10, 'mx.mailcdn.baidu.com')
MX (20, 'mx1.baidu.com')
NS ns4.baidu.com
NS ns2.baidu.com
NS ns3.baidu.com
NS dns.baidu.com
3、分解查詢(xún)結(jié)果
有些records,特別是NS、PTR、CNAME返回的數(shù)據(jù)中包含另一個(gè)主機(jī)名。為了得到最終的ip,需要分解返回的信息。這里用下面的代碼來(lái)完成:
##@小五義
import sys, DNS, re
def hierquery(qstring,qtype):
reqobj=DNS.Request()
try:
answerobj=reqobj.req(name=query,qtype=qtype)
answers=[x['data'] for x in answerobj.answers if x['type']==qtype]
except DNS.Base.DNSError:
answers=[]
if len(answers):
return answers
else:
remainder=qstring.split(".",1)
if len(remainder)==1:
return None
else:
return hierquery(remainder[1],qtype)
def findnameservers(hostname):
return hierquery(hostname,DNS.Type.NS)
def getrecordsfromnameserver(qstring,qtype,nslist):
for ns in nslist:
reqobj=DNS.Request(server=ns)
try:
answers=reqobj.req(name=qstring,qtype=qtype).answers
if len(answers):
return answers
except DNS.Base.DNSError:
pass
return []
def nslookup(qstring,qtype,verbose=1):
print qstring
nslist=findnameservers(qstring)
print nslist
if nslist==None:
raise RuntimeError,'找不到服務(wù)器'
if verbose:
print "服務(wù)器:",",".join(nslist)
return getrecordsfromnameserver(qstring,qtype, nslist)
def getreverse(query):
"""Given the query, returns an appropriate reverse lookup string
under IN-ADDR.ARPA if query is an IP address; otherwise, returns None.
This function is not IPv6-compatible."""
if re.search('^/d+/./d+/./d+/./d+$', query):
octets = query.split('.')
octets.reverse()
return '.'.join(octets) + '.IN-ADDR.ARPA'
return None
def formatline(index, typename, descr, data):
retval = "%-2s %-5s" % (index, typename)
if isinstance(data,list):
return retval
else:
data = data.replace("/n", "/n ")
if descr != None and len(descr):
retval += " %-12s" % (descr + ":")
return retval + " " + data
DNS.DiscoverNameServers()
query1=raw_input('輸入網(wǎng)站:')
queries = [(query1, DNS.Type.ANY)]
donequeries = []
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 donequeries:
# Don't look up the same thing twice
continue
donequeries.append(query)
print "-" * 77
print "Results for %s (lookup type %s)" %(query, DNS.Type.typestr(qtype))
print
rev = getreverse(query)
if rev:
print "IP address given; doing reverse lookup using", rev
query = rev
answers = nslookup(query, qtype, verbose = 0)
if not len(answers):
print "Not found."
count = 0
for answer in answers:
count += 1
if answer['typename'] == 'MX':
print formatline(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 data
print formatline(count, 'SOA', 'Start of authority', data)
elif answer['typename'] in descriptions:
##print answer['data']
print formatline(count, answer['typename'],
descriptions[answer['typename']], answer['data'])
else:
print formatline(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))
本人在運(yùn)行時(shí),總是報(bào)錯(cuò),沒(méi)找到原因,望高手指點(diǎn)。
- Python腳本實(shí)現(xiàn)DNSPod DNS動(dòng)態(tài)解析域名
- Python中使用scapy模擬數(shù)據(jù)包實(shí)現(xiàn)arp攻擊、dns放大攻擊例子
- python腳本實(shí)現(xiàn)分析dns日志并對(duì)受訪域名排行
- Python寫(xiě)的一個(gè)簡(jiǎn)單DNS服務(wù)器實(shí)例
- python實(shí)現(xiàn)DNS正向查詢(xún)、反向查詢(xún)的例子
- python實(shí)現(xiàn)dnspod自動(dòng)更新dns解析的方法
- Python實(shí)現(xiàn)獲取域名所用服務(wù)器的真實(shí)IP
- python使用urlparse分析網(wǎng)址中域名的方法
- Python實(shí)現(xiàn)從url中提取域名的幾種方法
- python 域名分析工具實(shí)現(xiàn)代碼
- python實(shí)現(xiàn)域名系統(tǒng)(DNS)正向查詢(xún)的方法
相關(guān)文章
python executemany的使用及注意事項(xiàng)
這篇文章主要介紹了python executemany的使用及注意事項(xiàng),非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2017-03-03
對(duì)Pandas MultiIndex(多重索引)詳解
今天小編就為大家分享一篇對(duì)Pandas MultiIndex(多重索引)詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-11-11
用pywin32實(shí)現(xiàn)windows模擬鼠標(biāo)及鍵盤(pán)動(dòng)作
這篇文章主要介紹了用pywin32實(shí)現(xiàn)windows模擬鼠標(biāo)及鍵盤(pán)動(dòng)作的示例,需要的朋友可以參考下2014-04-04
詳解Python最長(zhǎng)公共子串和最長(zhǎng)公共子序列的實(shí)現(xiàn)
這篇文章主要介紹了詳解Python最長(zhǎng)公共子串和最長(zhǎng)公共子序列的實(shí)現(xiàn)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-07-07
Python使用Yagmail庫(kù)實(shí)現(xiàn)自動(dòng)化郵件營(yíng)銷(xiāo)
在數(shù)字營(yíng)銷(xiāo)領(lǐng)域,自動(dòng)化郵件營(yíng)銷(xiāo)是一種高效、低成本的方式,能夠幫助企業(yè)與客戶(hù)保持溝通,提升品牌忠誠(chéng)度,而Yagmail是一個(gè)簡(jiǎn)潔且功能強(qiáng)大的Python庫(kù),可以大大簡(jiǎn)化郵件發(fā)送的過(guò)程,本文將詳細(xì)介紹如何使用Yagmail庫(kù)來(lái)實(shí)現(xiàn)自動(dòng)化郵件營(yíng)銷(xiāo),需要的朋友可以參考下2024-12-12
python使用tkinter包實(shí)現(xiàn)進(jìn)度條
python中的tkinter包是一種常見(jiàn)的設(shè)計(jì)程序的GUI界面用的包,本文將使用tkinter包實(shí)現(xiàn)不同風(fēng)格的進(jìn)度條,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-11-11
python pandas合并Sheet,處理列亂序和出現(xiàn)Unnamed列的解決
這篇文章主要介紹了python pandas合并Sheet,處理列亂序和出現(xiàn)Unnamed列的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-03-03

