Python的dnspython庫使用指南
標簽(空格分隔): Python DNS 測試
因為平時在測試DNS的時候有些操作手動完成不方便,所以需要用到腳本,而在Python里dnspython這個用於DNS操作的庫十分強大,但是無奈網上大部分資料只列舉了少部分的用法,所以記錄一下我平時使用到的功能,基本上已經能應付大部分的使用場景了。想具體了解dnspython可以登錄官方網站閱讀使用文檔
常用工具
最常用的用法是調用默認的resolver發送解析請求,如:
from dns import resolver
ans = resolver.query("www.baidu.com", "A")
print("qname:",ans.qname)
print ("reclass:",ans.rdclass)
print ("rdtype:",ans.rdtype)
print ("rrset:",ans.rrset)
print ("response:",ans.response)
結果為:
('qname:', <DNS name www.baidu.com.>)
('reclass:', 1)
('rdtype:', 1)
('rrset:', <DNS www.a.shifen.com. IN A RRset>)
('response:', <DNS message, ID 64940>)
在這里解析任務默認發送給系統默認的dns服務器,其中比較重要的是response,在dnspython的官方文檔里,response屬於類dns.message.Message,這個類也是許多DNS query請求的返回結果,下面詳細介紹下這個類。
類的主要成員變量有:
int flags #The DNS flags of the message.
int id #The query id; the default is a randomly chosen id.
list of RRset addictional
list of RRset answer
list of RRset authority
flags屬於返回DNS報文的標志位(詳見《TCP/IP詳解(卷一)》關於DNS的部分),可以利用以下代碼打印DNS報文的各個標志位:
#!/bin/env python2.7
ans = resolver.query("www.baidu.com", "A")
def FlagCount(flags, pos):
if (flags/(2**pos))%2 == 1:
return True
else:
return False
def GetFlags(flags):
QR_pos = 15
AA_pos = 10
TC_pos = 9
RD_pos = 8
RA_pos = 7
QR_flag = FlagCount(flags, QR_pos)
AA_flag = FlagCount(flags, AA_pos)
TC_flag = FlagCount(flags, TC_pos)
RD_flag = FlagCount(flags, RD_pos)
RA_flag = FlagCount(flags, RA_pos)
flag_dic = {"QR":QR_flag, "AA":AA_flag, "TC":TC_flag, "RD":RD_flag, "RA":RA_flag}
print "flag:",
for flag in flag_dic:
if flag_dic[flag]:
print flag,
flags = ans.response.flags
GetFlags(flags)
返回結果為:
flag: AA RD QR RA
另外一個比較重要的類就是RRset,通常返回的三個section信息都使用這個類封裝,常用的用法是使用類函數to_text()令解析結果以字符串形式顯示。如:
ans = resolver.query("www.baidu.com", "A")
for i in ans.response.answer:
print i.to_text()
返回結果為:
www.baidu.com. 1200 IN CNAME www.a.shifen.com.
www.a.shifen.com. 119 IN A 220.181.112.244
www.a.shifen.com. 119 IN A 220.181.111.188
對自己搭建的DNS服務器發送請求
當需要對自己搭建的DNS服務器發送解析請求的時候,可以使用dns.query這個類,使用這個類可以對指定的地址、端口發送自定義的DNS解析請求,下面對主要的幾個成員函數貼出官方文檔的說明:
##使用udp發送解析命令
udp(q, where, timeout=None, port=53, af=None, source=None, source_port=0, ignore_unexpected=False, one_rr_per_rrset=False)
Return the response obtained after sending a query via UDP.
Return dns.message.Message object
##使用tcp發送解析命令
tcp(q, where, timeout=None, port=53, af=None, source=None, source_port=0, one_rr_per_rrset=False)
Return the response obtained after sending a query via TCP.
Return dns.message.Message object
其中,形參where對應DNS服務器IP地址,q對應類dns.message.Message。返回的結果是dns.message.Message,上面已經介紹如何使用這個類。
我們可以通過dns.message.make_query()來構造一個解析請求。看一下make_query()函數的原型:
make_query(qname, rdtype, rdclass=1, use_edns=None, want_dnssec=False, ednsflags=None, payload=None, request_payload=None, options=None)
基本上設置前兩個數值就夠了。因此,對自己搭建的DNS服務器發送解析請求可以簡要按照以下步驟:
import dns
SERVER = "1.1.1.1"#your DNS server
PORT = 53#DNS server port
dns_query = dns.message.make_query("www.baidu.com", "A")
response = dns.query.udp(dns_query, SERVER, port = PORT)
for i in response.answer:
print i.to_text()
操作自己搭建的DNS服務器
也可以通過dnspython對DNS進行動態更新。比如在bind服務器中可以使用rndc工具來對bind進行動態更新,但是操作rndc工具始終不大方便,而我們也可以選擇使用dns.update對bind進行動態更新。
一個簡單的例子:已知一個zone的tsig(主輔同步加密)key-value為{"default":"werasdfasdfweffs==",},我們就可以使用這個事務簽名對這個zone進行更新操作。如,我要對zone testqa.com添加100個rdata為"1.1.1.1"的主機記錄,分別是www1~www100,可以:
import dns.tsigkeyring
import dns.update
import dns.query
ZONE = "testqa.com"
keyring = dns.tsigkeyring.from_text({'default.any': 'xxxxxxxxxxxxxxx'})
update_query = dns.update.Update(ZONE, keyring=keyring)
for i in range(1,101):
update_query.add("testqa" + str(i), 60, "1.1.1.1")
還是那句話,想了解更多的用法就去看官方文檔,或者自己看源碼。
