s4-auth: Rework memory handling to use a tmp_ctx
[Samba.git] / lib / dnspython / dns / update.py
blobe6922269180875bb6c37fc73eb035cb6865aa850
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"""
18 import dns.message
19 import dns.name
20 import dns.opcode
21 import dns.rdata
22 import dns.rdataclass
23 import dns.rdataset
24 import dns.tsig
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.
37 @type keyring: dict
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
49 HMAC_SHA512.
50 @type keyalgorithm: string
51 """
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)
56 self.origin = 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."""
68 if section is None:
69 section = self.authority
70 covers = rd.covers()
71 rrset = self.find_rrset(section, name, self.zone_rdclass, rd.rdtype,
72 covers, deleting, True, True)
73 rrset.add(rd, ttl)
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:
82 - rdataset...
84 - ttl, rdata...
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):
91 for rds in args:
92 if replace:
93 self.delete(name, rds.rdtype)
94 for rd in rds:
95 self._add_rr(name, rds.ttl, rd, section=section)
96 else:
97 args = list(args)
98 ttl = int(args.pop(0))
99 if isinstance(args[0], dns.rdata.Rdata):
100 if replace:
101 self.delete(name, args[0].rdtype)
102 for rd in args:
103 self._add_rr(name, ttl, rd, section=section)
104 else:
105 rdtype = args.pop(0)
106 if isinstance(rdtype, str):
107 rdtype = dns.rdatatype.from_text(rdtype)
108 if replace:
109 self.delete(name, rdtype)
110 for s in args:
111 rd = dns.rdata.from_text(self.zone_rdclass, rdtype, s,
112 self.origin)
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
117 arguments can be:
119 - rdataset...
121 - ttl, rdata...
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
128 arguments can be:
130 - I{nothing}
132 - rdataset...
134 - rdata...
136 - rdtype, [string...]"""
138 if isinstance(name, (str, unicode)):
139 name = dns.name.from_text(name, None)
140 if len(args) == 0:
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):
145 for rds in args:
146 for rd in rds:
147 self._add_rr(name, 0, rd, dns.rdataclass.NONE)
148 else:
149 args = list(args)
150 if isinstance(args[0], dns.rdata.Rdata):
151 for rd in args:
152 self._add_rr(name, 0, rd, dns.rdataclass.NONE)
153 else:
154 rdtype = args.pop(0)
155 if isinstance(rdtype, (str, unicode)):
156 rdtype = dns.rdatatype.from_text(rdtype)
157 if len(args) == 0:
158 rrset = self.find_rrset(self.authority, name,
159 self.zone_rdclass, rdtype,
160 dns.rdatatype.NONE,
161 dns.rdataclass.ANY,
162 True, True)
163 else:
164 for s in args:
165 rd = dns.rdata.from_text(self.zone_rdclass, rdtype, s,
166 self.origin)
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
171 arguments can be:
173 - rdataset...
175 - ttl, rdata...
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:
190 - rdataset...
192 - rdata...
194 - rdtype, string..."""
196 if isinstance(name, (str, unicode)):
197 name = dns.name.from_text(name, None)
198 if len(args) == 0:
199 rrset = self.find_rrset(self.answer, name,
200 dns.rdataclass.ANY, dns.rdatatype.ANY,
201 dns.rdatatype.NONE, None,
202 True, True)
203 elif isinstance(args[0], dns.rdataset.Rdataset) or \
204 isinstance(args[0], dns.rdata.Rdata) or \
205 len(args) > 1:
206 if not isinstance(args[0], dns.rdataset.Rdataset):
207 # Add a 0 TTL
208 args = list(args)
209 args.insert(0, 0)
210 self._add(False, self.answer, name, *args)
211 else:
212 rdtype = args[0]
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,
218 True, True)
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)
226 if rdtype is None:
227 rrset = self.find_rrset(self.answer, name,
228 dns.rdataclass.NONE, dns.rdatatype.ANY,
229 dns.rdatatype.NONE, None,
230 True, True)
231 else:
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,
237 True, True)
239 def to_wire(self, origin=None, max_size=65535):
240 """Return a string containing the update in DNS compressed wire
241 format.
242 @rtype: string"""
243 if origin is None:
244 origin = self.origin
245 return super(Update, self).to_wire(origin, max_size)