1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 """DNS rdatasets (an rdataset is a set of rdatas of a given type and class)"""
17
18 import random
19 import StringIO
20 import struct
21
22 import dns.exception
23 import dns.rdatatype
24 import dns.rdataclass
25 import dns.rdata
26 import dns.set
27
28
29 SimpleSet = dns.set.Set
30
32 """An attempt was made to add a DNS SIG/RRSIG whose covered type
33 is not the same as that of the other rdatas in the rdataset."""
34
36 """An attempt was made to add DNS RR data of an incompatible type."""
37
39 """A DNS rdataset.
40
41 @ivar rdclass: The class of the rdataset
42 @type rdclass: int
43 @ivar rdtype: The type of the rdataset
44 @type rdtype: int
45 @ivar covers: The covered type. Usually this value is
46 dns.rdatatype.NONE, but if the rdtype is dns.rdatatype.SIG or
47 dns.rdatatype.RRSIG, then the covers value will be the rdata
48 type the SIG/RRSIG covers. The library treats the SIG and RRSIG
49 types as if they were a family of
50 types, e.g. RRSIG(A), RRSIG(NS), RRSIG(SOA). This makes RRSIGs much
51 easier to work with than if RRSIGs covering different rdata
52 types were aggregated into a single RRSIG rdataset.
53 @type covers: int
54 @ivar ttl: The DNS TTL (Time To Live) value
55 @type ttl: int
56 """
57
58 __slots__ = ['rdclass', 'rdtype', 'covers', 'ttl']
59
61 """Create a new rdataset of the specified class and type.
62
63 @see: the description of the class instance variables for the
64 meaning of I{rdclass} and I{rdtype}"""
65
66 super(Rdataset, self).__init__()
67 self.rdclass = rdclass
68 self.rdtype = rdtype
69 self.covers = covers
70 self.ttl = 0
71
79
81 """Set the TTL of the rdataset to be the lesser of the set's current
82 TTL or the specified TTL. If the set contains no rdatas, set the TTL
83 to the specified TTL.
84 @param ttl: The TTL
85 @type ttl: int"""
86
87 if len(self) == 0:
88 self.ttl = ttl
89 elif ttl < self.ttl:
90 self.ttl = ttl
91
92 - def add(self, rd, ttl=None):
123
127
131
133 """Add all rdatas in other to self.
134
135 @param other: The rdataset from which to update
136 @type other: dns.rdataset.Rdataset object"""
137
138 self.update_ttl(other.ttl)
139 super(Rdataset, self).update(other)
140
148
151
153 """Two rdatasets are equal if they have the same class, type, and
154 covers, and contain the same rdata.
155 @rtype: bool"""
156
157 if not isinstance(other, Rdataset):
158 return False
159 if self.rdclass != other.rdclass or \
160 self.rdtype != other.rdtype or \
161 self.covers != other.covers:
162 return False
163 return super(Rdataset, self).__eq__(other)
164
166 return not self.__eq__(other)
167
168 - def to_text(self, name=None, origin=None, relativize=True,
169 override_rdclass=None, **kw):
170 """Convert the rdataset into DNS master file format.
171
172 @see: L{dns.name.Name.choose_relativity} for more information
173 on how I{origin} and I{relativize} determine the way names
174 are emitted.
175
176 Any additional keyword arguments are passed on to the rdata
177 to_text() method.
178
179 @param name: If name is not None, emit a RRs with I{name} as
180 the owner name.
181 @type name: dns.name.Name object
182 @param origin: The origin for relative names, or None.
183 @type origin: dns.name.Name object
184 @param relativize: True if names should names be relativized
185 @type relativize: bool"""
186 if not name is None:
187 name = name.choose_relativity(origin, relativize)
188 ntext = str(name)
189 pad = ' '
190 else:
191 ntext = ''
192 pad = ''
193 s = StringIO.StringIO()
194 if not override_rdclass is None:
195 rdclass = override_rdclass
196 else:
197 rdclass = self.rdclass
198 if len(self) == 0:
199
200
201
202
203
204 print >> s, '%s%s%s %s' % (ntext, pad,
205 dns.rdataclass.to_text(rdclass),
206 dns.rdatatype.to_text(self.rdtype))
207 else:
208 for rd in self:
209 print >> s, '%s%s%d %s %s %s' % \
210 (ntext, pad, self.ttl, dns.rdataclass.to_text(rdclass),
211 dns.rdatatype.to_text(self.rdtype),
212 rd.to_text(origin=origin, relativize=relativize, **kw))
213
214
215
216 return s.getvalue()[:-1]
217
218 - def to_wire(self, name, file, compress=None, origin=None,
219 override_rdclass=None, want_shuffle=True):
220 """Convert the rdataset to wire format.
221
222 @param name: The owner name of the RRset that will be emitted
223 @type name: dns.name.Name object
224 @param file: The file to which the wire format data will be appended
225 @type file: file
226 @param compress: The compression table to use; the default is None.
227 @type compress: dict
228 @param origin: The origin to be appended to any relative names when
229 they are emitted. The default is None.
230 @returns: the number of records emitted
231 @rtype: int
232 """
233
234 if not override_rdclass is None:
235 rdclass = override_rdclass
236 want_shuffle = False
237 else:
238 rdclass = self.rdclass
239 file.seek(0, 2)
240 if len(self) == 0:
241 name.to_wire(file, compress, origin)
242 stuff = struct.pack("!HHIH", self.rdtype, rdclass, 0, 0)
243 file.write(stuff)
244 return 1
245 else:
246 if want_shuffle:
247 l = list(self)
248 random.shuffle(l)
249 else:
250 l = self
251 for rd in l:
252 name.to_wire(file, compress, origin)
253 stuff = struct.pack("!HHIH", self.rdtype, rdclass,
254 self.ttl, 0)
255 file.write(stuff)
256 start = file.tell()
257 rd.to_wire(file, compress, origin)
258 end = file.tell()
259 assert end - start < 65536
260 file.seek(start - 2)
261 stuff = struct.pack("!H", end - start)
262 file.write(stuff)
263 file.seek(0, 2)
264 return len(self)
265
266 - def match(self, rdclass, rdtype, covers):
267 """Returns True if this rdataset matches the specified class, type,
268 and covers"""
269 if self.rdclass == rdclass and \
270 self.rdtype == rdtype and \
271 self.covers == covers:
272 return True
273 return False
274
275 -def from_text_list(rdclass, rdtype, ttl, text_rdatas):
276 """Create an rdataset with the specified class, type, and TTL, and with
277 the specified list of rdatas in text format.
278
279 @rtype: dns.rdataset.Rdataset object
280 """
281
282 if isinstance(rdclass, (str, unicode)):
283 rdclass = dns.rdataclass.from_text(rdclass)
284 if isinstance(rdtype, (str, unicode)):
285 rdtype = dns.rdatatype.from_text(rdtype)
286 r = Rdataset(rdclass, rdtype)
287 r.update_ttl(ttl)
288 for t in text_rdatas:
289 rd = dns.rdata.from_text(r.rdclass, r.rdtype, t)
290 r.add(rd)
291 return r
292
293 -def from_text(rdclass, rdtype, ttl, *text_rdatas):
294 """Create an rdataset with the specified class, type, and TTL, and with
295 the specified rdatas in text format.
296
297 @rtype: dns.rdataset.Rdataset object
298 """
299
300 return from_text_list(rdclass, rdtype, ttl, text_rdatas)
301
303 """Create an rdataset with the specified TTL, and with
304 the specified list of rdata objects.
305
306 @rtype: dns.rdataset.Rdataset object
307 """
308
309 if len(rdatas) == 0:
310 raise ValueError("rdata list must not be empty")
311 r = None
312 for rd in rdatas:
313 if r is None:
314 r = Rdataset(rd.rdclass, rd.rdtype)
315 r.update_ttl(ttl)
316 first_time = False
317 r.add(rd)
318 return r
319
321 """Create an rdataset with the specified TTL, and with
322 the specified rdata objects.
323
324 @rtype: dns.rdataset.Rdataset object
325 """
326
327 return from_rdata_list(ttl, rdatas)
328