1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 """DNS Dynamic Update Support"""
17
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
25
26 -class Update(dns.message.Message):
29 """Initialize a new DNS Update object.
30
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)
64
65 - def _add_rr(self, name, ttl, rd, deleting=None, section=None):
74
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:
81
82 - rdataset...
83
84 - ttl, rdata...
85
86 - ttl, rdtype, string..."""
87
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)
114
115 - def add(self, name, *args):
116 """Add records. The first argument is always a name. The other
117 arguments can be:
118
119 - rdataset...
120
121 - ttl, rdata...
122
123 - ttl, rdtype, string..."""
124 self._add(False, self.authority, name, *args)
125
126 - def delete(self, name, *args):
127 """Delete records. The first argument is always a name. The other
128 arguments can be:
129
130 - I{nothing}
131
132 - rdataset...
133
134 - rdata...
135
136 - rdtype, [string...]"""
137
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)
168
170 """Replace records. The first argument is always a name. The other
171 arguments can be:
172
173 - rdataset...
174
175 - ttl, rdata...
176
177 - ttl, rdtype, string...
178
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."""
181
182 self._add(True, self.authority, name, *args)
183
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:
189
190 - rdataset...
191
192 - rdata...
193
194 - rdtype, string..."""
195
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
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)
219
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."""
223
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)
238
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)
246