dnspython  1.16.0
About: dnspython is a DNS toolkit (for Python 2.x) that supports almost all record types.
  Fossies Dox: dnspython-1.16.0.tar.gz  ("inofficial" and yet experimental doxygen-generated source code documentation)  

rdataset.py
Go to the documentation of this file.
1 # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
2 
3 # Copyright (C) 2001-2017 Nominum, Inc.
4 #
5 # Permission to use, copy, modify, and distribute this software and its
6 # documentation for any purpose with or without fee is hereby granted,
7 # provided that the above copyright notice and this permission notice
8 # appear in all copies.
9 #
10 # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
11 # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
13 # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
16 # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 
18 """DNS rdatasets (an rdataset is a set of rdatas of a given type and class)"""
19 
20 import random
21 from io import StringIO
22 import struct
23 
24 import dns.exception
25 import dns.rdatatype
26 import dns.rdataclass
27 import dns.rdata
28 import dns.set
29 from ._compat import string_types
30 
31 # define SimpleSet here for backwards compatibility
32 SimpleSet = dns.set.Set
33 
34 
36  """An attempt was made to add a DNS SIG/RRSIG whose covered type
37  is not the same as that of the other rdatas in the rdataset."""
38 
39 
41  """An attempt was made to add DNS RR data of an incompatible type."""
42 
43 
45 
46  """A DNS rdataset."""
47 
48  __slots__ = ['rdclass', 'rdtype', 'covers', 'ttl']
49 
50  def __init__(self, rdclass, rdtype, covers=dns.rdatatype.NONE, ttl=0):
51  """Create a new rdataset of the specified class and type.
52 
53  *rdclass*, an ``int``, the rdataclass.
54 
55  *rdtype*, an ``int``, the rdatatype.
56 
57  *covers*, an ``int``, the covered rdatatype.
58 
59  *ttl*, an ``int``, the TTL.
60  """
61 
62  super(Rdataset, self).__init__()
63  self.rdclass = rdclass
64  self.rdtype = rdtype
65  self.covers = covers
66  self.ttl = ttl
67 
68  def _clone(self):
69  obj = super(Rdataset, self)._clone()
70  obj.rdclass = self.rdclass
71  obj.rdtype = self.rdtype
72  obj.covers = self.covers
73  obj.ttl = self.ttl
74  return obj
75 
76  def update_ttl(self, ttl):
77  """Perform TTL minimization.
78 
79  Set the TTL of the rdataset to be the lesser of the set's current
80  TTL or the specified TTL. If the set contains no rdatas, set the TTL
81  to the specified TTL.
82 
83  *ttl*, an ``int``.
84  """
85 
86  if len(self) == 0:
87  self.ttl = ttl
88  elif ttl < self.ttl:
89  self.ttl = ttl
90 
91  def add(self, rd, ttl=None):
92  """Add the specified rdata to the rdataset.
93 
94  If the optional *ttl* parameter is supplied, then
95  ``self.update_ttl(ttl)`` will be called prior to adding the rdata.
96 
97  *rd*, a ``dns.rdata.Rdata``, the rdata
98 
99  *ttl*, an ``int``, the TTL.
100 
101  Raises ``dns.rdataset.IncompatibleTypes`` if the type and class
102  do not match the type and class of the rdataset.
103 
104  Raises ``dns.rdataset.DifferingCovers`` if the type is a signature
105  type and the covered type does not match that of the rdataset.
106  """
107 
108  #
109  # If we're adding a signature, do some special handling to
110  # check that the signature covers the same type as the
111  # other rdatas in this rdataset. If this is the first rdata
112  # in the set, initialize the covers field.
113  #
114  if self.rdclass != rd.rdclass or self.rdtype != rd.rdtype:
115  raise IncompatibleTypes
116  if ttl is not None:
117  self.update_ttl(ttl)
118  if self.rdtype == dns.rdatatype.RRSIG or \
119  self.rdtype == dns.rdatatype.SIG:
120  covers = rd.covers()
121  if len(self) == 0 and self.covers == dns.rdatatype.NONE:
122  self.covers = covers
123  elif self.covers != covers:
124  raise DifferingCovers
125  if dns.rdatatype.is_singleton(rd.rdtype) and len(self) > 0:
126  self.clear()
127  super(Rdataset, self).add(rd)
128 
129  def union_update(self, other):
130  self.update_ttl(other.ttl)
131  super(Rdataset, self).union_update(other)
132 
133  def intersection_update(self, other):
134  self.update_ttl(other.ttl)
135  super(Rdataset, self).intersection_update(other)
136 
137  def update(self, other):
138  """Add all rdatas in other to self.
139 
140  *other*, a ``dns.rdataset.Rdataset``, the rdataset from which
141  to update.
142  """
143 
144  self.update_ttl(other.ttl)
145  super(Rdataset, self).update(other)
146 
147  def __repr__(self):
148  if self.covers == 0:
149  ctext = ''
150  else:
151  ctext = '(' + dns.rdatatype.to_text(self.covers) + ')'
152  return '<DNS ' + dns.rdataclass.to_text(self.rdclass) + ' ' + \
153  dns.rdatatype.to_text(self.rdtype) + ctext + ' rdataset>'
154 
155  def __str__(self):
156  return self.to_text()
157 
158  def __eq__(self, other):
159  if not isinstance(other, Rdataset):
160  return False
161  if self.rdclass != other.rdclass or \
162  self.rdtype != other.rdtype or \
163  self.covers != other.covers:
164  return False
165  return super(Rdataset, self).__eq__(other)
166 
167  def __ne__(self, other):
168  return not self.__eq__(other)
169 
170  def to_text(self, name=None, origin=None, relativize=True,
171  override_rdclass=None, **kw):
172  """Convert the rdataset into DNS master file format.
173 
174  See ``dns.name.Name.choose_relativity`` for more information
175  on how *origin* and *relativize* determine the way names
176  are emitted.
177 
178  Any additional keyword arguments are passed on to the rdata
179  ``to_text()`` method.
180 
181  *name*, a ``dns.name.Name``. If name is not ``None``, emit RRs with
182  *name* as the owner name.
183 
184  *origin*, a ``dns.name.Name`` or ``None``, the origin for relative
185  names.
186 
187  *relativize*, a ``bool``. If ``True``, names will be relativized
188  to *origin*.
189  """
190 
191  if name is not None:
192  name = name.choose_relativity(origin, relativize)
193  ntext = str(name)
194  pad = ' '
195  else:
196  ntext = ''
197  pad = ''
198  s = StringIO()
199  if override_rdclass is not None:
200  rdclass = override_rdclass
201  else:
202  rdclass = self.rdclass
203  if len(self) == 0:
204  #
205  # Empty rdatasets are used for the question section, and in
206  # some dynamic updates, so we don't need to print out the TTL
207  # (which is meaningless anyway).
208  #
209  s.write(u'{}{}{} {}\n'.format(ntext, pad,
210  dns.rdataclass.to_text(rdclass),
212  else:
213  for rd in self:
214  s.write(u'%s%s%d %s %s %s\n' %
215  (ntext, pad, self.ttl, dns.rdataclass.to_text(rdclass),
217  rd.to_text(origin=origin, relativize=relativize,
218  **kw)))
219  #
220  # We strip off the final \n for the caller's convenience in printing
221  #
222  return s.getvalue()[:-1]
223 
224  def to_wire(self, name, file, compress=None, origin=None,
225  override_rdclass=None, want_shuffle=True):
226  """Convert the rdataset to wire format.
227 
228  *name*, a ``dns.name.Name`` is the owner name to use.
229 
230  *file* is the file where the name is emitted (typically a
231  BytesIO file).
232 
233  *compress*, a ``dict``, is the compression table to use. If
234  ``None`` (the default), names will not be compressed.
235 
236  *origin* is a ``dns.name.Name`` or ``None``. If the name is
237  relative and origin is not ``None``, then *origin* will be appended
238  to it.
239 
240  *override_rdclass*, an ``int``, is used as the class instead of the
241  class of the rdataset. This is useful when rendering rdatasets
242  associated with dynamic updates.
243 
244  *want_shuffle*, a ``bool``. If ``True``, then the order of the
245  Rdatas within the Rdataset will be shuffled before rendering.
246 
247  Returns an ``int``, the number of records emitted.
248  """
249 
250  if override_rdclass is not None:
251  rdclass = override_rdclass
252  want_shuffle = False
253  else:
254  rdclass = self.rdclass
255  file.seek(0, 2)
256  if len(self) == 0:
257  name.to_wire(file, compress, origin)
258  stuff = struct.pack("!HHIH", self.rdtype, rdclass, 0, 0)
259  file.write(stuff)
260  return 1
261  else:
262  if want_shuffle:
263  l = list(self)
264  random.shuffle(l)
265  else:
266  l = self
267  for rd in l:
268  name.to_wire(file, compress, origin)
269  stuff = struct.pack("!HHIH", self.rdtype, rdclass,
270  self.ttl, 0)
271  file.write(stuff)
272  start = file.tell()
273  rd.to_wire(file, compress, origin)
274  end = file.tell()
275  assert end - start < 65536
276  file.seek(start - 2)
277  stuff = struct.pack("!H", end - start)
278  file.write(stuff)
279  file.seek(0, 2)
280  return len(self)
281 
282  def match(self, rdclass, rdtype, covers):
283  """Returns ``True`` if this rdataset matches the specified class,
284  type, and covers.
285  """
286  if self.rdclass == rdclass and \
287  self.rdtype == rdtype and \
288  self.covers == covers:
289  return True
290  return False
291 
292 
293 def from_text_list(rdclass, rdtype, ttl, text_rdatas):
294  """Create an rdataset with the specified class, type, and TTL, and with
295  the specified list of rdatas in text format.
296 
297  Returns a ``dns.rdataset.Rdataset`` object.
298  """
299 
300  if isinstance(rdclass, string_types):
301  rdclass = dns.rdataclass.from_text(rdclass)
302  if isinstance(rdtype, string_types):
303  rdtype = dns.rdatatype.from_text(rdtype)
304  r = Rdataset(rdclass, rdtype)
305  r.update_ttl(ttl)
306  for t in text_rdatas:
307  rd = dns.rdata.from_text(r.rdclass, r.rdtype, t)
308  r.add(rd)
309  return r
310 
311 
312 def from_text(rdclass, rdtype, ttl, *text_rdatas):
313  """Create an rdataset with the specified class, type, and TTL, and with
314  the specified rdatas in text format.
315 
316  Returns a ``dns.rdataset.Rdataset`` object.
317  """
318 
319  return from_text_list(rdclass, rdtype, ttl, text_rdatas)
320 
321 
322 def from_rdata_list(ttl, rdatas):
323  """Create an rdataset with the specified TTL, and with
324  the specified list of rdata objects.
325 
326  Returns a ``dns.rdataset.Rdataset`` object.
327  """
328 
329  if len(rdatas) == 0:
330  raise ValueError("rdata list must not be empty")
331  r = None
332  for rd in rdatas:
333  if r is None:
334  r = Rdataset(rd.rdclass, rd.rdtype)
335  r.update_ttl(ttl)
336  r.add(rd)
337  return r
338 
339 
340 def from_rdata(ttl, *rdatas):
341  """Create an rdataset with the specified TTL, and with
342  the specified rdata objects.
343 
344  Returns a ``dns.rdataset.Rdataset`` object.
345  """
346 
347  return from_rdata_list(ttl, rdatas)
dns.rdataset.Rdataset
Definition: rdataset.py:44
dns.rdataset.Rdataset.match
def match(self, rdclass, rdtype, covers)
Definition: rdataset.py:282
dns.rdataset.Rdataset.to_text
def to_text(self, name=None, origin=None, relativize=True, override_rdclass=None, **kw)
Definition: rdataset.py:170
dns.rdataset.Rdataset.__str__
def __str__(self)
Definition: rdataset.py:155
dns.rdataset.Rdataset.to_wire
def to_wire(self, name, file, compress=None, origin=None, override_rdclass=None, want_shuffle=True)
Definition: rdataset.py:224
dns.rdataset.from_text
def from_text(rdclass, rdtype, ttl, *text_rdatas)
Definition: rdataset.py:312
dns.rdataclass
Definition: rdataclass.py:1
dns.rdataset.IncompatibleTypes
Definition: rdataset.py:40
dns.exception.DNSException
Definition: exception.py:24
dns.rdataset.Rdataset.ttl
ttl
Definition: rdataset.py:66
dns.rdataset.Rdataset.__repr__
def __repr__(self)
Definition: rdataset.py:147
dns.rdatatype.is_singleton
def is_singleton(rdtype)
Definition: rdatatype.py:254
dns.rdataset.Rdataset.__init__
def __init__(self, rdclass, rdtype, covers=dns.rdatatype.NONE, ttl=0)
Definition: rdataset.py:50
dns.set
Definition: set.py:1
dns.rdata.from_text
def from_text(rdclass, rdtype, tok, origin=None, relativize=True)
Definition: rdata.py:344
dns.set.Set.clear
def clear(self)
Definition: set.py:207
dns.rdataset.Rdataset.rdclass
rdclass
Definition: rdataset.py:63
dns.rdataset.from_rdata_list
def from_rdata_list(ttl, rdatas)
Definition: rdataset.py:322
dns.rdataset.Rdataset.__eq__
def __eq__(self, other)
Definition: rdataset.py:158
dns.rdataclass.to_text
def to_text(value)
Definition: rdataclass.py:93
dns.rdataset.Rdataset.update
def update(self, other)
Definition: rdataset.py:137
dns.rdataset.Rdataset.covers
covers
Definition: rdataset.py:65
dns.set.Set
Definition: set.py:18
dns.rdataset.Rdataset._clone
def _clone(self)
Definition: rdataset.py:68
dns.rdataset.from_rdata
def from_rdata(ttl, *rdatas)
Definition: rdataset.py:340
dns.rdataset.DifferingCovers
Definition: rdataset.py:35
dns.rdataset.Rdataset.intersection_update
def intersection_update(self, other)
Definition: rdataset.py:133
dns.rdataset.Rdataset.rdtype
rdtype
Definition: rdataset.py:64
dns.rdataset.Rdataset.add
def add(self, rd, ttl=None)
Definition: rdataset.py:91
dns.rdataset.Rdataset.union_update
def union_update(self, other)
Definition: rdataset.py:129
dns.rdata
Definition: rdata.py:1
dns.rdataset.Rdataset.update_ttl
def update_ttl(self, ttl)
Definition: rdataset.py:76
dns.rdatatype
Definition: rdatatype.py:1
dns.rdatatype.to_text
def to_text(value)
Definition: rdatatype.py:219
dns.rdatatype.from_text
def from_text(text)
Definition: rdatatype.py:193
dns.rdataset.Rdataset.__ne__
def __ne__(self, other)
Definition: rdataset.py:167
dns.rdataset.from_text_list
def from_text_list(rdclass, rdtype, ttl, text_rdatas)
Definition: rdataset.py:293
dns.exception
Definition: exception.py:1
dns.rdataclass.from_text
def from_text(text)
Definition: rdataclass.py:67