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  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  # define SimpleSet here for backwards compatibility 
 29  SimpleSet = dns.set.Set 
 30   
31 -class DifferingCovers(dns.exception.DNSException):
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
35 -class IncompatibleTypes(dns.exception.DNSException):
36 """An attempt was made to add DNS RR data of an incompatible type."""
37
38 -class Rdataset(dns.set.Set):
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
60 - def __init__(self, rdclass, rdtype, covers=dns.rdatatype.NONE):
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
72 - def _clone(self):
73 obj = super(Rdataset, self)._clone() 74 obj.rdclass = self.rdclass 75 obj.rdtype = self.rdtype 76 obj.covers = self.covers 77 obj.ttl = self.ttl 78 return obj
79
80 - def update_ttl(self, ttl):
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):
93 """Add the specified rdata to the rdataset. 94 95 If the optional I{ttl} parameter is supplied, then 96 self.update_ttl(ttl) will be called prior to adding the rdata. 97 98 @param rd: The rdata 99 @type rd: dns.rdata.Rdata object 100 @param ttl: The TTL 101 @type ttl: int""" 102 103 # 104 # If we're adding a signature, do some special handling to 105 # check that the signature covers the same type as the 106 # other rdatas in this rdataset. If this is the first rdata 107 # in the set, initialize the covers field. 108 # 109 if self.rdclass != rd.rdclass or self.rdtype != rd.rdtype: 110 raise IncompatibleTypes 111 if not ttl is None: 112 self.update_ttl(ttl) 113 if self.rdtype == dns.rdatatype.RRSIG or \ 114 self.rdtype == dns.rdatatype.SIG: 115 covers = rd.covers() 116 if len(self) == 0 and self.covers == dns.rdatatype.NONE: 117 self.covers = covers 118 elif self.covers != covers: 119 raise DifferingCovers 120 if dns.rdatatype.is_singleton(rd.rdtype) and len(self) > 0: 121 self.clear() 122 super(Rdataset, self).add(rd)
123
124 - def union_update(self, other):
125 self.update_ttl(other.ttl) 126 super(Rdataset, self).union_update(other)
127
128 - def intersection_update(self, other):
129 self.update_ttl(other.ttl) 130 super(Rdataset, self).intersection_update(other)
131
132 - def update(self, other):
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
141 - def __repr__(self):
142 if self.covers == 0: 143 ctext = '' 144 else: 145 ctext = '(' + dns.rdatatype.to_text(self.covers) + ')' 146 return '<DNS ' + dns.rdataclass.to_text(self.rdclass) + ' ' + \ 147 dns.rdatatype.to_text(self.rdtype) + ctext + ' rdataset>'
148
149 - def __str__(self):
150 return self.to_text()
151
152 - def __eq__(self, other):
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
165 - def __ne__(self, other):
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 # Empty rdatasets are used for the question section, and in 201 # some dynamic updates, so we don't need to print out the TTL 202 # (which is meaningless anyway). 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 # We strip off the final \n for the caller's convenience in printing 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
302 -def from_rdata_list(ttl, rdatas):
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
320 -def from_rdata(ttl, *rdatas):
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