Package dns :: Module rdataset
[hide private]
[frames] | no frames]

Source Code for Module dns.rdataset

  1  # Copyright (C) 2001-2007, 2009-2011 Nominum, Inc. 
  2  # 
  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. 
  7  # 
  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. 
 15   
 16  """DNS rdatasets (an rdataset is a set of rdatas of a given type and class)""" 
 17   
 18  import random 
 19  from io 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  from ._compat import string_types 
 28   
 29  # define SimpleSet here for backwards compatibility 
 30  SimpleSet = dns.set.Set 
 31   
 32   
33 -class DifferingCovers(dns.exception.DNSException):
34 35 """An attempt was made to add a DNS SIG/RRSIG whose covered type 36 is not the same as that of the other rdatas in the rdataset."""
37 38
39 -class IncompatibleTypes(dns.exception.DNSException):
40 41 """An attempt was made to add DNS RR data of an incompatible type."""
42 43
44 -class Rdataset(dns.set.Set):
45 46 """A DNS rdataset. 47 48 @ivar rdclass: The class of the rdataset 49 @type rdclass: int 50 @ivar rdtype: The type of the rdataset 51 @type rdtype: int 52 @ivar covers: The covered type. Usually this value is 53 dns.rdatatype.NONE, but if the rdtype is dns.rdatatype.SIG or 54 dns.rdatatype.RRSIG, then the covers value will be the rdata 55 type the SIG/RRSIG covers. The library treats the SIG and RRSIG 56 types as if they were a family of 57 types, e.g. RRSIG(A), RRSIG(NS), RRSIG(SOA). This makes RRSIGs much 58 easier to work with than if RRSIGs covering different rdata 59 types were aggregated into a single RRSIG rdataset. 60 @type covers: int 61 @ivar ttl: The DNS TTL (Time To Live) value 62 @type ttl: int 63 """ 64 65 __slots__ = ['rdclass', 'rdtype', 'covers', 'ttl'] 66
67 - def __init__(self, rdclass, rdtype, covers=dns.rdatatype.NONE):
68 """Create a new rdataset of the specified class and type. 69 70 @see: the description of the class instance variables for the 71 meaning of I{rdclass} and I{rdtype}""" 72 73 super(Rdataset, self).__init__() 74 self.rdclass = rdclass 75 self.rdtype = rdtype 76 self.covers = covers 77 self.ttl = 0
78
79 - def _clone(self):
80 obj = super(Rdataset, self)._clone() 81 obj.rdclass = self.rdclass 82 obj.rdtype = self.rdtype 83 obj.covers = self.covers 84 obj.ttl = self.ttl 85 return obj
86
87 - def update_ttl(self, ttl):
88 """Set the TTL of the rdataset to be the lesser of the set's current 89 TTL or the specified TTL. If the set contains no rdatas, set the TTL 90 to the specified TTL. 91 @param ttl: The TTL 92 @type ttl: int""" 93 94 if len(self) == 0: 95 self.ttl = ttl 96 elif ttl < self.ttl: 97 self.ttl = ttl
98
99 - def add(self, rd, ttl=None):
100 """Add the specified rdata to the rdataset. 101 102 If the optional I{ttl} parameter is supplied, then 103 self.update_ttl(ttl) will be called prior to adding the rdata. 104 105 @param rd: The rdata 106 @type rd: dns.rdata.Rdata object 107 @param ttl: The TTL 108 @type ttl: int""" 109 110 # 111 # If we're adding a signature, do some special handling to 112 # check that the signature covers the same type as the 113 # other rdatas in this rdataset. If this is the first rdata 114 # in the set, initialize the covers field. 115 # 116 if self.rdclass != rd.rdclass or self.rdtype != rd.rdtype: 117 raise IncompatibleTypes 118 if ttl is not None: 119 self.update_ttl(ttl) 120 if self.rdtype == dns.rdatatype.RRSIG or \ 121 self.rdtype == dns.rdatatype.SIG: 122 covers = rd.covers() 123 if len(self) == 0 and self.covers == dns.rdatatype.NONE: 124 self.covers = covers 125 elif self.covers != covers: 126 raise DifferingCovers 127 if dns.rdatatype.is_singleton(rd.rdtype) and len(self) > 0: 128 self.clear() 129 super(Rdataset, self).add(rd)
130
131 - def union_update(self, other):
132 self.update_ttl(other.ttl) 133 super(Rdataset, self).union_update(other)
134
135 - def intersection_update(self, other):
136 self.update_ttl(other.ttl) 137 super(Rdataset, self).intersection_update(other)
138
139 - def update(self, other):
140 """Add all rdatas in other to self. 141 142 @param other: The rdataset from which to update 143 @type other: dns.rdataset.Rdataset object""" 144 145 self.update_ttl(other.ttl) 146 super(Rdataset, self).update(other)
147
148 - def __repr__(self):
149 if self.covers == 0: 150 ctext = '' 151 else: 152 ctext = '(' + dns.rdatatype.to_text(self.covers) + ')' 153 return '<DNS ' + dns.rdataclass.to_text(self.rdclass) + ' ' + \ 154 dns.rdatatype.to_text(self.rdtype) + ctext + ' rdataset>'
155
156 - def __str__(self):
157 return self.to_text()
158
159 - def __eq__(self, other):
160 """Two rdatasets are equal if they have the same class, type, and 161 covers, and contain the same rdata. 162 @rtype: bool""" 163 164 if not isinstance(other, Rdataset): 165 return False 166 if self.rdclass != other.rdclass or \ 167 self.rdtype != other.rdtype or \ 168 self.covers != other.covers: 169 return False 170 return super(Rdataset, self).__eq__(other)
171
172 - def __ne__(self, other):
173 return not self.__eq__(other)
174
175 - def to_text(self, name=None, origin=None, relativize=True, 176 override_rdclass=None, **kw):
177 """Convert the rdataset into DNS master file format. 178 179 @see: L{dns.name.Name.choose_relativity} for more information 180 on how I{origin} and I{relativize} determine the way names 181 are emitted. 182 183 Any additional keyword arguments are passed on to the rdata 184 to_text() method. 185 186 @param name: If name is not None, emit a RRs with I{name} as 187 the owner name. 188 @type name: dns.name.Name object 189 @param origin: The origin for relative names, or None. 190 @type origin: dns.name.Name object 191 @param relativize: True if names should names be relativized 192 @type relativize: bool""" 193 if name is not None: 194 name = name.choose_relativity(origin, relativize) 195 ntext = str(name) 196 pad = ' ' 197 else: 198 ntext = '' 199 pad = '' 200 s = StringIO() 201 if override_rdclass is not None: 202 rdclass = override_rdclass 203 else: 204 rdclass = self.rdclass 205 if len(self) == 0: 206 # 207 # Empty rdatasets are used for the question section, and in 208 # some dynamic updates, so we don't need to print out the TTL 209 # (which is meaningless anyway). 210 # 211 s.write(u'%s%s%s %s\n' % (ntext, pad, 212 dns.rdataclass.to_text(rdclass), 213 dns.rdatatype.to_text(self.rdtype))) 214 else: 215 for rd in self: 216 s.write(u'%s%s%d %s %s %s\n' % 217 (ntext, pad, self.ttl, dns.rdataclass.to_text(rdclass), 218 dns.rdatatype.to_text(self.rdtype), 219 rd.to_text(origin=origin, relativize=relativize, 220 **kw))) 221 # 222 # We strip off the final \n for the caller's convenience in printing 223 # 224 return s.getvalue()[:-1]
225
226 - def to_wire(self, name, file, compress=None, origin=None, 227 override_rdclass=None, want_shuffle=True):
228 """Convert the rdataset to wire format. 229 230 @param name: The owner name of the RRset that will be emitted 231 @type name: dns.name.Name object 232 @param file: The file to which the wire format data will be appended 233 @type file: file 234 @param compress: The compression table to use; the default is None. 235 @type compress: dict 236 @param origin: The origin to be appended to any relative names when 237 they are emitted. The default is None. 238 @returns: the number of records emitted 239 @rtype: int 240 """ 241 242 if override_rdclass is not None: 243 rdclass = override_rdclass 244 want_shuffle = False 245 else: 246 rdclass = self.rdclass 247 file.seek(0, 2) 248 if len(self) == 0: 249 name.to_wire(file, compress, origin) 250 stuff = struct.pack("!HHIH", self.rdtype, rdclass, 0, 0) 251 file.write(stuff) 252 return 1 253 else: 254 if want_shuffle: 255 l = list(self) 256 random.shuffle(l) 257 else: 258 l = self 259 for rd in l: 260 name.to_wire(file, compress, origin) 261 stuff = struct.pack("!HHIH", self.rdtype, rdclass, 262 self.ttl, 0) 263 file.write(stuff) 264 start = file.tell() 265 rd.to_wire(file, compress, origin) 266 end = file.tell() 267 assert end - start < 65536 268 file.seek(start - 2) 269 stuff = struct.pack("!H", end - start) 270 file.write(stuff) 271 file.seek(0, 2) 272 return len(self)
273
274 - def match(self, rdclass, rdtype, covers):
275 """Returns True if this rdataset matches the specified class, type, 276 and covers""" 277 if self.rdclass == rdclass and \ 278 self.rdtype == rdtype and \ 279 self.covers == covers: 280 return True 281 return False
282 283
284 -def from_text_list(rdclass, rdtype, ttl, text_rdatas):
285 """Create an rdataset with the specified class, type, and TTL, and with 286 the specified list of rdatas in text format. 287 288 @rtype: dns.rdataset.Rdataset object 289 """ 290 291 if isinstance(rdclass, string_types): 292 rdclass = dns.rdataclass.from_text(rdclass) 293 if isinstance(rdtype, string_types): 294 rdtype = dns.rdatatype.from_text(rdtype) 295 r = Rdataset(rdclass, rdtype) 296 r.update_ttl(ttl) 297 for t in text_rdatas: 298 rd = dns.rdata.from_text(r.rdclass, r.rdtype, t) 299 r.add(rd) 300 return r
301 302
303 -def from_text(rdclass, rdtype, ttl, *text_rdatas):
304 """Create an rdataset with the specified class, type, and TTL, and with 305 the specified rdatas in text format. 306 307 @rtype: dns.rdataset.Rdataset object 308 """ 309 310 return from_text_list(rdclass, rdtype, ttl, text_rdatas)
311 312
313 -def from_rdata_list(ttl, rdatas):
314 """Create an rdataset with the specified TTL, and with 315 the specified list of rdata objects. 316 317 @rtype: dns.rdataset.Rdataset object 318 """ 319 320 if len(rdatas) == 0: 321 raise ValueError("rdata list must not be empty") 322 r = None 323 for rd in rdatas: 324 if r is None: 325 r = Rdataset(rd.rdclass, rd.rdtype) 326 r.update_ttl(ttl) 327 r.add(rd) 328 return r
329 330
331 -def from_rdata(ttl, *rdatas):
332 """Create an rdataset with the specified TTL, and with 333 the specified rdata objects. 334 335 @rtype: dns.rdataset.Rdataset object 336 """ 337 338 return from_rdata_list(ttl, rdatas)
339