dnspythonでDNSサーバのzone fileを扱う
BINDやNSDといったDNSサーバのゾーンファイルをpythonで扱うライブラリは幾つかある.その中でもdnspythonはある程度メンテされていて,かつ使いやすそう.
インストール
pip
でインストール可能.ただしPython 2.xと3.xとでパッケージが分かれているので注意が必要.
Python 2.xの場合
$ pip install dnspython
Python 3.xの場合
$ pip install dnspython3
Zone fileの読み込み & レコードの参照
下のようなzone fileが/path/to/example.com.zone
に置いてある場合を考える.
$ORIGIN example.com. $TTL 3600 @ IN SOA ns1.example.com. root.example.com. ( 2014060801 ; Serial 28800 ; Refresh 14400 ; Retry 3600000 ; Expire 3600 ; Minimum TTL ) IN NS ns1.example.com. ns1 IN A 192.168.1.2 ;;; Network gateway IN A 192.168.1.1 IN AAAA 2001:db8::1 ;;; Server svr1 IN A 192.168.1.101 svr2 IN A 192.168.1.102 svr3 IN A 192.168.2.201 wiki IN CNAME svr3
これを読み込んだ上で,全てのレコードを表示する.
import dns from dns import (zone, rdataclass, rdatatype) zone_example = zone.from_file("/path/to/example.com.zone") print("Origin: {}".format(zone_example.origin)) for name, node in zone_example.nodes.items(): for rdataset in node.rdatasets: for rdata in rdataset: print("{}: {} {} {}".format(name, rdataclass.to_text(rdataset.rdclass), rdatatype.to_text(rdataset.rdtype), rdataset.ttl)) rdata_property_names = set(dir(rdata)) - set(dir(dns.rdata.Rdata)) for property_name in rdata_property_names: if not property_name.startswith("_"): print(" {}: {}".format(property_name, getattr(rdata, property_name)))
この場合の出力はこのような感じ:
Origin: example.com. @: IN SOA 3600 mname: ns1 minimum: 3600 serial: 2014060800 rname: root expire: 3600000 refresh: 28800 retry: 14400 @: IN NS 3600 target: ns1 svr1: IN A 3600 address: 192.168.1.101 svr2: IN A 3600 address: 192.168.1.102 svr3: IN A 3600 address: 192.168.2.201 wiki: IN CNAME 3600 target: svr3 ns1: IN A 3600 address: 192.168.1.2 gateway: IN A 3600 address: 192.168.1.1 gateway: IN AAAA 3600 address: 2001:db8::1
取得したいレコードのドメイン名が既知である場合,find_node()
/find_rdataset()
を使う.似た名前のメソッドにfind_rrset()
があるが,こちらは返ってくるオブジェクトにドメイン名の情報が含まれているかの違いがある.
node_gateway = zone_example.find_node("gateway") rdataset_gateway_a = zone_example.find_rdataset("gateway", "A") rrset_gateway_a = zone_example.find_rrset("gateway", "A") print(node_gateway) print(node_gateway.rdatasets) print(rdataset_gateway_a.__repr__(), rdataset_gateway_a, hasattr(rdataset_gateway_a, "name")) print(rrset_gateway_a.__repr__(), rrset_gateway_a, hasattr(rrset_gateway_a, "name"))
この場合の出力はこうなる:
<DNS node 140259498981032> [<DNS IN A rdataset>, <DNS IN AAAA rdataset>] <DNS IN A rdataset> 3600 IN A 192.168.1.1 False <DNS gateway IN A RRset> gateway 3600 IN A 192.168.1.1 True
指定した名前のレコードが存在しない場合,KeyError
が投げられる.None
を返してほしい場合,get_node()
/get_rdataset()
/get_rrset()
を使う.
読み込んだzone fileはレコードの新規追加や編集・削除もできる.このあたりは参考元のページにサンプルコード付きでかなり詳しく書かれているので割愛.
Zone fileの編集の半自動化に使ったり,テストのようなものを書くのに使えそう.