1 # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
3 # Permission to use, copy, modify, and distribute this software and its
4 # documentation for any purpose with or without fee is hereby granted,
5 # provided that the above copyright notice and this permission notice
6 # appear in all copies.
8 # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
9 # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
11 # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
14 # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 """DNS Dynamic Update Support"""
26 class Update(dns.message.Message):
27 def __init__(self, zone, rdclass=dns.rdataclass.IN, keyring=None,
28 keyname=None, keyalgorithm=dns.tsig.default_algorithm):
29 """Initialize a new DNS Update object.
31 @param zone: The zone which is being updated.
32 @type zone: A dns.name.Name or string
33 @param rdclass: The class of the zone; defaults to dns.rdataclass.IN.
34 @type rdclass: An int designating the class, or a string whose value
35 is the name of a class.
36 @param keyring: The TSIG keyring to use; defaults to None.
38 @param keyname: The name of the TSIG key to use; defaults to None.
39 The key must be defined in the keyring. If a keyring is specified
40 but a keyname is not, then the key used will be the first key in the
41 keyring. Note that the order of keys in a dictionary is not defined,
42 so applications should supply a keyname when a keyring is used, unless
43 they know the keyring contains only one key.
44 @type keyname: dns.name.Name or string
45 @param keyalgorithm: The TSIG algorithm to use; defaults to
46 dns.tsig.default_algorithm. Constants for TSIG algorithms are defined
47 in dns.tsig, and the currently implemented algorithms are
48 HMAC_MD5, HMAC_SHA1, HMAC_SHA224, HMAC_SHA256, HMAC_SHA384, and
50 @type keyalgorithm: string
52 super(Update, self).__init__()
53 self.flags |= dns.opcode.to_flags(dns.opcode.UPDATE)
54 if isinstance(zone, (str, unicode)):
55 zone = dns.name.from_text(zone)
57 if isinstance(rdclass, str):
58 rdclass = dns.rdataclass.from_text(rdclass)
59 self.zone_rdclass = rdclass
60 self.find_rrset(self.question, self.origin, rdclass, dns.rdatatype.SOA,
61 create=True, force_unique=True)
62 if not keyring is None:
63 self.use_tsig(keyring, keyname, algorithm=keyalgorithm)
65 def _add_rr(self, name, ttl, rd, deleting=None, section=None):
66 """Add a single RR to the update section."""
69 section = self.authority
71 rrset = self.find_rrset(section, name, self.zone_rdclass, rd.rdtype,
72 covers, deleting, True, True)
75 def _add(self, replace, section, name, *args):
76 """Add records. The first argument is the replace mode. If
77 false, RRs are added to an existing RRset; if true, the RRset
78 is replaced with the specified contents. The second
79 argument is the section to add to. The third argument
80 is always a name. The other arguments can be:
86 - ttl, rdtype, string..."""
88 if isinstance(name, (str, unicode)):
89 name = dns.name.from_text(name, None)
90 if isinstance(args[0], dns.rdataset.Rdataset):
93 self.delete(name, rds.rdtype)
95 self._add_rr(name, rds.ttl, rd, section=section)
98 ttl = int(args.pop(0))
99 if isinstance(args[0], dns.rdata.Rdata):
101 self.delete(name, args[0].rdtype)
103 self._add_rr(name, ttl, rd, section=section)
106 if isinstance(rdtype, str):
107 rdtype = dns.rdatatype.from_text(rdtype)
109 self.delete(name, rdtype)
111 rd = dns.rdata.from_text(self.zone_rdclass, rdtype, s,
113 self._add_rr(name, ttl, rd, section=section)
115 def add(self, name, *args):
116 """Add records. The first argument is always a name. The other
123 - ttl, rdtype, string..."""
124 self._add(False, self.authority, name, *args)
126 def delete(self, name, *args):
127 """Delete records. The first argument is always a name. The other
136 - rdtype, [string...]"""
138 if isinstance(name, (str, unicode)):
139 name = dns.name.from_text(name, None)
141 rrset = self.find_rrset(self.authority, name, dns.rdataclass.ANY,
142 dns.rdatatype.ANY, dns.rdatatype.NONE,
143 dns.rdatatype.ANY, True, True)
144 elif isinstance(args[0], dns.rdataset.Rdataset):
147 self._add_rr(name, 0, rd, dns.rdataclass.NONE)
150 if isinstance(args[0], dns.rdata.Rdata):
152 self._add_rr(name, 0, rd, dns.rdataclass.NONE)
155 if isinstance(rdtype, (str, unicode)):
156 rdtype = dns.rdatatype.from_text(rdtype)
158 rrset = self.find_rrset(self.authority, name,
159 self.zone_rdclass, rdtype,
165 rd = dns.rdata.from_text(self.zone_rdclass, rdtype, s,
167 self._add_rr(name, 0, rd, dns.rdataclass.NONE)
169 def replace(self, name, *args):
170 """Replace records. The first argument is always a name. The other
177 - ttl, rdtype, string...
179 Note that if you want to replace the entire node, you should do
180 a delete of the name followed by one or more calls to add."""
182 self._add(True, self.authority, name, *args)
184 def present(self, name, *args):
185 """Require that an owner name (and optionally an rdata type,
186 or specific rdataset) exists as a prerequisite to the
187 execution of the update. The first argument is always a name.
188 The other arguments can be:
194 - rdtype, string..."""
196 if isinstance(name, (str, unicode)):
197 name = dns.name.from_text(name, None)
199 rrset = self.find_rrset(self.answer, name,
200 dns.rdataclass.ANY, dns.rdatatype.ANY,
201 dns.rdatatype.NONE, None,
203 elif isinstance(args[0], dns.rdataset.Rdataset) or \
204 isinstance(args[0], dns.rdata.Rdata) or \
206 if not isinstance(args[0], dns.rdataset.Rdataset):
210 self._add(False, self.answer, name, *args)
213 if isinstance(rdtype, (str, unicode)):
214 rdtype = dns.rdatatype.from_text(rdtype)
215 rrset = self.find_rrset(self.answer, name,
216 dns.rdataclass.ANY, rdtype,
217 dns.rdatatype.NONE, None,
220 def absent(self, name, rdtype=None):
221 """Require that an owner name (and optionally an rdata type) does
222 not exist as a prerequisite to the execution of the update."""
224 if isinstance(name, (str, unicode)):
225 name = dns.name.from_text(name, None)
227 rrset = self.find_rrset(self.answer, name,
228 dns.rdataclass.NONE, dns.rdatatype.ANY,
229 dns.rdatatype.NONE, None,
232 if isinstance(rdtype, (str, unicode)):
233 rdtype = dns.rdatatype.from_text(rdtype)
234 rrset = self.find_rrset(self.answer, name,
235 dns.rdataclass.NONE, rdtype,
236 dns.rdatatype.NONE, None,
239 def to_wire(self, origin=None, max_size=65535):
240 """Return a string containing the update in DNS compressed wire
245 return super(Update, self).to_wire(origin, max_size)