1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 """DNS Dynamic Update Support"""
17
18
19 import dns.message
20 import dns.name
21 import dns.opcode
22 import dns.rdata
23 import dns.rdataclass
24 import dns.rdataset
25 import dns.tsig
26 from ._compat import string_types
27
28
29 -class Update(dns.message.Message):
30
33 """Initialize a new DNS Update object.
34
35 @param zone: The zone which is being updated.
36 @type zone: A dns.name.Name or string
37 @param rdclass: The class of the zone; defaults to dns.rdataclass.IN.
38 @type rdclass: An int designating the class, or a string whose value
39 is the name of a class.
40 @param keyring: The TSIG keyring to use; defaults to None.
41 @type keyring: dict
42 @param keyname: The name of the TSIG key to use; defaults to None.
43 The key must be defined in the keyring. If a keyring is specified
44 but a keyname is not, then the key used will be the first key in the
45 keyring. Note that the order of keys in a dictionary is not defined,
46 so applications should supply a keyname when a keyring is used, unless
47 they know the keyring contains only one key.
48 @type keyname: dns.name.Name or string
49 @param keyalgorithm: The TSIG algorithm to use; defaults to
50 dns.tsig.default_algorithm. Constants for TSIG algorithms are defined
51 in dns.tsig, and the currently implemented algorithms are
52 HMAC_MD5, HMAC_SHA1, HMAC_SHA224, HMAC_SHA256, HMAC_SHA384, and
53 HMAC_SHA512.
54 @type keyalgorithm: string
55 """
56 super(Update, self).__init__()
57 self.flags |= dns.opcode.to_flags(dns.opcode.UPDATE)
58 if isinstance(zone, string_types):
59 zone = dns.name.from_text(zone)
60 self.origin = zone
61 if isinstance(rdclass, string_types):
62 rdclass = dns.rdataclass.from_text(rdclass)
63 self.zone_rdclass = rdclass
64 self.find_rrset(self.question, self.origin, rdclass, dns.rdatatype.SOA,
65 create=True, force_unique=True)
66 if keyring is not None:
67 self.use_tsig(keyring, keyname, algorithm=keyalgorithm)
68
69 - def _add_rr(self, name, ttl, rd, deleting=None, section=None):
78
79 - def _add(self, replace, section, name, *args):
80 """Add records. The first argument is the replace mode. If
81 false, RRs are added to an existing RRset; if true, the RRset
82 is replaced with the specified contents. The second
83 argument is the section to add to. The third argument
84 is always a name. The other arguments can be:
85
86 - rdataset...
87
88 - ttl, rdata...
89
90 - ttl, rdtype, string..."""
91
92 if isinstance(name, string_types):
93 name = dns.name.from_text(name, None)
94 if isinstance(args[0], dns.rdataset.Rdataset):
95 for rds in args:
96 if replace:
97 self.delete(name, rds.rdtype)
98 for rd in rds:
99 self._add_rr(name, rds.ttl, rd, section=section)
100 else:
101 args = list(args)
102 ttl = int(args.pop(0))
103 if isinstance(args[0], dns.rdata.Rdata):
104 if replace:
105 self.delete(name, args[0].rdtype)
106 for rd in args:
107 self._add_rr(name, ttl, rd, section=section)
108 else:
109 rdtype = args.pop(0)
110 if isinstance(rdtype, string_types):
111 rdtype = dns.rdatatype.from_text(rdtype)
112 if replace:
113 self.delete(name, rdtype)
114 for s in args:
115 rd = dns.rdata.from_text(self.zone_rdclass, rdtype, s,
116 self.origin)
117 self._add_rr(name, ttl, rd, section=section)
118
119 - def add(self, name, *args):
120 """Add records. The first argument is always a name. The other
121 arguments can be:
122
123 - rdataset...
124
125 - ttl, rdata...
126
127 - ttl, rdtype, string..."""
128 self._add(False, self.authority, name, *args)
129
130 - def delete(self, name, *args):
131 """Delete records. The first argument is always a name. The other
132 arguments can be:
133
134 - I{nothing}
135
136 - rdataset...
137
138 - rdata...
139
140 - rdtype, [string...]"""
141
142 if isinstance(name, string_types):
143 name = dns.name.from_text(name, None)
144 if len(args) == 0:
145 self.find_rrset(self.authority, name, dns.rdataclass.ANY,
146 dns.rdatatype.ANY, dns.rdatatype.NONE,
147 dns.rdatatype.ANY, True, True)
148 elif isinstance(args[0], dns.rdataset.Rdataset):
149 for rds in args:
150 for rd in rds:
151 self._add_rr(name, 0, rd, dns.rdataclass.NONE)
152 else:
153 args = list(args)
154 if isinstance(args[0], dns.rdata.Rdata):
155 for rd in args:
156 self._add_rr(name, 0, rd, dns.rdataclass.NONE)
157 else:
158 rdtype = args.pop(0)
159 if isinstance(rdtype, string_types):
160 rdtype = dns.rdatatype.from_text(rdtype)
161 if len(args) == 0:
162 self.find_rrset(self.authority, name,
163 self.zone_rdclass, rdtype,
164 dns.rdatatype.NONE,
165 dns.rdataclass.ANY,
166 True, True)
167 else:
168 for s in args:
169 rd = dns.rdata.from_text(self.zone_rdclass, rdtype, s,
170 self.origin)
171 self._add_rr(name, 0, rd, dns.rdataclass.NONE)
172
174 """Replace records. The first argument is always a name. The other
175 arguments can be:
176
177 - rdataset...
178
179 - ttl, rdata...
180
181 - ttl, rdtype, string...
182
183 Note that if you want to replace the entire node, you should do
184 a delete of the name followed by one or more calls to add."""
185
186 self._add(True, self.authority, name, *args)
187
189 """Require that an owner name (and optionally an rdata type,
190 or specific rdataset) exists as a prerequisite to the
191 execution of the update. The first argument is always a name.
192 The other arguments can be:
193
194 - rdataset...
195
196 - rdata...
197
198 - rdtype, string..."""
199
200 if isinstance(name, string_types):
201 name = dns.name.from_text(name, None)
202 if len(args) == 0:
203 self.find_rrset(self.answer, name,
204 dns.rdataclass.ANY, dns.rdatatype.ANY,
205 dns.rdatatype.NONE, None,
206 True, True)
207 elif isinstance(args[0], dns.rdataset.Rdataset) or \
208 isinstance(args[0], dns.rdata.Rdata) or \
209 len(args) > 1:
210 if not isinstance(args[0], dns.rdataset.Rdataset):
211
212 args = list(args)
213 args.insert(0, 0)
214 self._add(False, self.answer, name, *args)
215 else:
216 rdtype = args[0]
217 if isinstance(rdtype, string_types):
218 rdtype = dns.rdatatype.from_text(rdtype)
219 self.find_rrset(self.answer, name,
220 dns.rdataclass.ANY, rdtype,
221 dns.rdatatype.NONE, None,
222 True, True)
223
224 - def absent(self, name, rdtype=None):
225 """Require that an owner name (and optionally an rdata type) does
226 not exist as a prerequisite to the execution of the update."""
227
228 if isinstance(name, string_types):
229 name = dns.name.from_text(name, None)
230 if rdtype is None:
231 self.find_rrset(self.answer, name,
232 dns.rdataclass.NONE, dns.rdatatype.ANY,
233 dns.rdatatype.NONE, None,
234 True, True)
235 else:
236 if isinstance(rdtype, string_types):
237 rdtype = dns.rdatatype.from_text(rdtype)
238 self.find_rrset(self.answer, name,
239 dns.rdataclass.NONE, rdtype,
240 dns.rdatatype.NONE, None,
241 True, True)
242
243 - def to_wire(self, origin=None, max_size=65535):
244 """Return a string containing the update in DNS compressed wire
245 format.
246 @rtype: string"""
247 if origin is None:
248 origin = self.origin
249 return super(Update, self).to_wire(origin, max_size)
250