Managing DNS zone files with dnspython

来源:互联网 发布:sql查询去掉重复行 编辑:程序博客网 时间:2024/06/08 08:57

from: http://agiletesting.blogspot.com/2005/08/managing-dns-zone-files-with-dnspython.html



Managing DNS zone files with dnspython

I've been using dnspython lately for transferring some DNS zone files from one name server to another. I found the package extremely useful, but poorly documented, so I decided to write this post as a mini-tutorial on using dnspython.

Running DNS queries

This is one of the things that's clearly spelled out on the Examples page. Here's how to run a DNS query to get the mail servers (MX records) for dnspython.org:

import dns.resolveranswers = dns.resolver.query('dnspython.org', 'MX')for rdata in answers:    print 'Host', rdata.exchange, 'has preference', rdata.preference
To run other types of queries, for example for IP addresses (A records) or name servers (NS records), replace MX with the desired record type (A, NS, etc.)

Reading a DNS zone from a file

In dnspython, a DNS zone is available as a Zone object. Assume you have the following DNS zone file called db.example.com:
$TTL 36000example.com. IN      SOA     ns1.example.com. hostmaster.example.com. (               2005081201      ; serial               28800   ; refresh (8 hours)               1800    ; retry (30 mins)               2592000 ; expire (30 days)               86400 ) ; minimum (1 day)example.com.     86400   NS      ns1.example.com.example.com.     86400   NS      ns2.example.com.example.com.     86400   MX 10   mail.example.com.example.com.     86400   MX 20   mail2.example.com.example.com.     86400   A       192.168.10.10ns1.example.com.        86400   A       192.168.1.10ns2.example.com.        86400   A       192.168.1.20mail.example.com.       86400   A       192.168.2.10mail2.example.com.      86400   A       192.168.2.20www2.example.com.       86400   A    192.168.10.20www.example.com.        86400 CNAME     example.com.ftp.example.com.        86400 CNAME     example.com.webmail.example.com.    86400 CNAME     example.com.

To have dnspython read this file into a Zone object, you can use this code:
import dns.zonefrom dns.exception import DNSExceptiondomain = "example.com"print "Getting zone object for domain", domainzone_file = "db.%s" % domaintry:    zone = dns.zone.from_file(zone_file, domain)    print "Zone origin:", zone.originexcept DNSException, e:    print e.__class__, e
A zone can be viewed as a dictionary mapping names to nodes; dnspython uses by default name representations which are relative to the 'origin' of the zone. In our zone file, 'example.com' is the origin of the zone, and it gets the special name '@'. A name such as www.example.com is exposed by default as 'www'.

A name corresponds to a node, and a node contains a collection of record dataset, or rdatasets. A record dataset contains all the records of a given type. In our example, the '@' node corresponding to the zone origin contains 4 rdatasets, one for each record type that we have: SOA, NS, MX and A. The NS rdataset contains a set of rdatas, which are the individual records of type NS. The rdata class has subclasses for all the possible record types, and each subclass contains information specific to that record type.

Enough talking, here is some code that will hopefully make the previous discussion a bit clearer:
import dns.zonefrom dns.exception import DNSExceptionfrom dns.rdataclass import *from dns.rdatatype import *domain = "example.com"print "Getting zone object for domain", domainzone_file = "db.%s" % domaintry:    zone = dns.zone.from_file(zone_file, domain)    print "Zone origin:", zone.origin    for name, node in zone.nodes.items():        rdatasets = node.rdatasets        print "\n**** BEGIN NODE ****"        print "node name:", name        for rdataset in rdatasets:            print "--- BEGIN RDATASET ---"            print "rdataset string representation:", rdataset            print "rdataset rdclass:", rdataset.rdclass            print "rdataset rdtype:", rdataset.rdtype            print "rdataset ttl:", rdataset.ttl            print "rdataset has following rdata:"            for rdata in rdataset:                print "-- BEGIN RDATA --"                print "rdata string representation:", rdata                if rdataset.rdtype == SOA:                    print "** SOA-specific rdata **"                    print "expire:", rdata.expire                    print "minimum:", rdata.minimum                    print "mname:", rdata.mname                    print "refresh:", rdata.refresh                    print "retry:", rdata.retry                    print "rname:", rdata.rname                    print "serial:", rdata.serial                if rdataset.rdtype == MX:                    print "** MX-specific rdata **"                    print "exchange:", rdata.exchange                    print "preference:", rdata.preference                if rdataset.rdtype == NS:                    print "** NS-specific rdata **"                    print "target:", rdata.target                if rdataset.rdtype == CNAME:                    print "** CNAME-specific rdata **"                    print "target:", rdata.target                if rdataset.rdtype == A:                    print "** A-specific rdata **"                    print "address:", rdata.addressexcept DNSException, e:    print e.__class__, e

When run against db.example.com, the code above produces this output.

Modifying a DNS zone file

Let's see how to add, delete and change records in our example.com zone file. dnspython offers several different ways to get to a record if you know its name or its type.

Here's how to modify the SOA record and increase its serial number, a very common operation for anybody who maintains DNS zones. I use the iterate_rdatas method of the Zone class, which is handy in this case, since we know that the rdataset actually contains one rdata of type SOA:
   for (name, ttl, rdata) in zone.iterate_rdatas(SOA):    serial = rdata.serial    new_serial = serial + 1    print "Changing SOA serial from %d to %d" %(serial, new_serial)    rdata.serial = new_serial


Here's how to delete a record by its name. I use the delete_node method of the Zone class:
node_delete = "www2"print "Deleting node", node_deletezone.delete_node(node_delete)
Here's how to change attributes of existing records. I use the find_rdataset method of the Zone class, which returns a rdataset containing the records I want to change. In the first section of the following code, I'm changing the IP address of 'mail', and in the second section I'm changing the TTL for all the NS records corresponding to the zone origin '@':
A_change = "mail"new_IP = "192.168.2.100"print "Changing A record for", A_change, "to", new_IPrdataset = zone.find_rdataset(A_change, rdtype=A)for rdata in rdataset:    rdata.address = new_IPrdataset = zone.find_rdataset("@", rdtype=NS)new_ttl = rdataset.ttl / 2print "Changing TTL for NS records to", new_ttlrdataset.ttl = new_ttl

Here's how to add records to the zone file. The find_rdataset method can be used in this case too, with the create parameter set to True, in which case it creates a new rdataset if it doesn't already exist. Individual rdata objects are then created by instantiating their corresponding classes with the correct parameters -- such as rdata = dns.rdtypes.IN.A.A(IN, A, address="192.168.10.30").

I show here how to add records of type A, CNAME, NS and MX:
  A_add = "www3"print "Adding record of type A:", A_addrdataset = zone.find_rdataset(A_add, rdtype=A, create=True)rdata = dns.rdtypes.IN.A.A(IN, A, address="192.168.10.30")rdataset.add(rdata, ttl=86400)CNAME_add = "www3_alias"target = dns.name.Name(("www3",))print "Adding record of type CNAME:", CNAME_addrdataset = zone.find_rdataset(CNAME_add, rdtype=CNAME, create=True)rdata = dns.rdtypes.ANY.CNAME.CNAME(IN, CNAME, target)rdataset.add(rdata, ttl=86400)A_add = "ns3"print "Adding record of type A:", A_addrdataset = zone.find_rdataset(A_add, rdtype=A, create=True)rdata = dns.rdtypes.IN.A.A(IN, A, address="192.168.1.30")rdataset.add(rdata, ttl=86400)NS_add = "@"target = dns.name.Name(("ns3",))print "Adding record of type NS:", NS_addrdataset = zone.find_rdataset(NS_add, rdtype=NS, create=True)rdata = dns.rdtypes.ANY.NS.NS(IN, NS, target)rdataset.add(rdata, ttl=86400)A_add = "mail3"print "Adding record of type A:", A_addrdataset = zone.find_rdataset(A_add, rdtype=A, create=True)rdata = dns.rdtypes.IN.A.A(IN, A, address="192.168.2.30")rdataset.add(rdata, ttl=86400)MX_add = "@"exchange = dns.name.Name(("mail3",))preference = 30print "Adding record of type MX:", MX_addrdataset = zone.find_rdataset(MX_add, rdtype=MX, create=True)rdata = dns.rdtypes.ANY.MX.MX(IN, MX, preference, exchange)rdataset.add(rdata, ttl=86400)

Finally, after modifying the zone file via the zone object, it's time to write it back to disk. This is easily accomplished with dnspython via the to_file method. I chose to write the modified zone to a new file, so that I have my original zone available for other tests:
new_zone_file = "new.db.%s" % domainprint "Writing modified zone to file %s" % new_zone_filezone.to_file(new_zone_file)

The new zone file looks something like this (note that all names have been relativized from the origin):
@ 36000 IN SOA ns1 hostmaster 2005081202 28800 1800 2592000 86400@ 43200 IN NS ns1@ 43200 IN NS ns2@ 43200 IN NS ns3@ 86400 IN MX 10 mail@ 86400 IN MX 20 mail2@ 86400 IN MX 30 mail3@ 86400 IN A 192.168.10.10ftp 86400 IN CNAME @mail 86400 IN A 192.168.2.100mail2 86400 IN A 192.168.2.20mail3 86400 IN A 192.168.2.30ns1 86400 IN A 192.168.1.10ns2 86400 IN A 192.168.1.20ns3 86400 IN A 192.168.1.30webmail 86400 IN CNAME @www 86400 IN CNAME @www3 86400 IN A 192.168.10.30www3_alias 86400 IN CNAME www3

Although it looks much different from the original db.example.com file, this file is also a valid DNS zone -- I tested it by having my DNS server load it.

Obtaining a DNS zone via a zone transfer

This is also easily done in dnspython via the from_xfr function of the zone module. Here's how to do a zone transfer for dnspython.org, trying all the name servers for that domain one by one:
import dns.resolverimport dns.queryimport dns.zonefrom dns.exception import DNSExceptionfrom dns.rdataclass import *from dns.rdatatype import *domain = "dnspython.org"print "Getting NS records for", domainanswers = dns.resolver.query(domain, 'NS')ns = []for rdata in answers:    n = str(rdata)    print "Found name server:", n    ns.append(n)for n in ns:    print "\nTrying a zone transfer for %s from name server %s" % (domain, n)    try:        zone = dns.zone.from_xfr(dns.query.xfr(n, domain))    except DNSException, e:        print e.__class__, e


Once we obtain the zone object, we can then manipulate it in exactly the same way as when we obtained it from a file.

Various ways to iterate through DNS records

Here are some other snippets of code that show how to iterate through records of different types assuming we retrieved a zone object from a file or via a zone transfer:
    print "\nALL 'IN' RECORDS EXCEPT 'SOA' and 'TXT':"    for name, node in zone.nodes.items():        rdatasets = node.rdatasets        for rdataset in rdatasets:            if rdataset.rdclass != IN or rdataset.rdtype in [SOA, TXT]:                continue            print name, rdataset    print "\nGET_RDATASET('A'):"    for name, node in zone.nodes.items():        rdataset = node.get_rdataset(rdclass=IN, rdtype=A)        if not rdataset:            continue        for rdataset in rdataset:            print name, rdataset    print "\nITERATE_RDATAS('A'):"    for (name, ttl, rdata) in zone.iterate_rdatas('A'):        print name, ttl, rdata    print "\nITERATE_RDATAS('MX'):"    for (name, ttl, rdata) in zone.iterate_rdatas('MX'):        print name, ttl, rdata    print "\nITERATE_RDATAS('CNAME'):"    for (name, ttl, rdata) in zone.iterate_rdatas('CNAME'):        print name, ttl, rdata
You can find the code referenced in this post in these 2 modules: zonemgmt.py and zone_transfer.py.






0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 取环前2天同房了怎么办 怀孕了发现有子宫肌瘤怎么办 宫腔中央性粘连怎么办 孕妇宫腔粘连带怎么办 内膜厚怎么办吃什么好 功血引起的贫血怎么办 吃宫血宁后月经不来了怎么办 孕晚期胎心不好怎么办 胎心不好怎么办让住院 39 5胎心不好怎么办 肺长了一个肿瘤怎么办 血糖高伤口不愈合怎么办 有轻微的狐臭该怎么办 嘴上汗毛太重怎么办 风把裙子吹起怎么办 每天三四点醒来就睡不着怎么办 打游戏手汗太多怎么办 老年人胸闷气短呼吸困难怎么办 冒险岛宠物饿了怎么办 不小心喝了黑墨怎么办 压的双眼皮开了怎么办 割了双眼皮显老怎么办 心脏供血不足怎么办呢 怀孕了哮喘犯了怎么办 喝了电解质不拉怎么办 喝电解质散吐了怎么办 皮鞋买大了一号怎么办 皮鞋买小了挤脚怎么办 新买的皮鞋挤脚怎么办 熊类仓鼠变瘦怎么办 喜利得电锤锁了怎么办 请病假公司不批怎么办 淘宝扣满12分怎么办 吃了霉变的大米怎么办 魅族账号忘了怎么办 魅族账号忘记了怎么办 密保问题忘记了怎么办 yy忘记密保问题怎么办 qq忘记密保问题怎么办 魅族3固件损坏怎么办 水卡插上没反应怎么办