1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 """DNS rdata.
17
18 @var _rdata_modules: A dictionary mapping a (rdclass, rdtype) tuple to
19 the module which implements that type.
20 @type _rdata_modules: dict
21 @var _module_prefix: The prefix to use when forming modules names. The
22 default is 'dns.rdtypes'. Changing this value will break the library.
23 @type _module_prefix: string
24 @var _hex_chunk: At most this many octets that will be represented in each
25 chunk of hexstring that _hexify() produces before whitespace occurs.
26 @type _hex_chunk: int"""
27
28 import cStringIO
29
30 import dns.exception
31 import dns.name
32 import dns.rdataclass
33 import dns.rdatatype
34 import dns.tokenizer
35 import dns.wiredata
36
37 _hex_chunksize = 32
38
40 """Convert a binary string into its hex encoding, broken up into chunks
41 of I{chunksize} characters separated by a space.
42
43 @param data: the binary string
44 @type data: string
45 @param chunksize: the chunk size. Default is L{dns.rdata._hex_chunksize}
46 @rtype: string
47 """
48
49 if chunksize is None:
50 chunksize = _hex_chunksize
51 hex = data.encode('hex_codec')
52 l = len(hex)
53 if l > chunksize:
54 chunks = []
55 i = 0
56 while i < l:
57 chunks.append(hex[i : i + chunksize])
58 i += chunksize
59 hex = ' '.join(chunks)
60 return hex
61
62 _base64_chunksize = 32
63
65 """Convert a binary string into its base64 encoding, broken up into chunks
66 of I{chunksize} characters separated by a space.
67
68 @param data: the binary string
69 @type data: string
70 @param chunksize: the chunk size. Default is
71 L{dns.rdata._base64_chunksize}
72 @rtype: string
73 """
74
75 if chunksize is None:
76 chunksize = _base64_chunksize
77 b64 = data.encode('base64_codec')
78 b64 = b64.replace('\n', '')
79 l = len(b64)
80 if l > chunksize:
81 chunks = []
82 i = 0
83 while i < l:
84 chunks.append(b64[i : i + chunksize])
85 i += chunksize
86 b64 = ' '.join(chunks)
87 return b64
88
89 __escaped = {
90 '"' : True,
91 '\\' : True,
92 }
93
95 """Escape the characters in a quoted string which need it.
96
97 @param qstring: the string
98 @type qstring: string
99 @returns: the escaped string
100 @rtype: string
101 """
102
103 text = ''
104 for c in qstring:
105 if c in __escaped:
106 text += '\\' + c
107 elif ord(c) >= 0x20 and ord(c) < 0x7F:
108 text += c
109 else:
110 text += '\\%03d' % ord(c)
111 return text
112
114 """Determine the index of greatest byte that isn't all zeros, and
115 return the bitmap that contains all the bytes less than that index.
116
117 @param what: a string of octets representing a bitmap.
118 @type what: string
119 @rtype: string
120 """
121
122 for i in xrange(len(what) - 1, -1, -1):
123 if what[i] != '\x00':
124 break
125 return ''.join(what[0 : i + 1])
126
128 """Base class for all DNS rdata types.
129 """
130
131 __slots__ = ['rdclass', 'rdtype']
132
134 """Initialize an rdata.
135 @param rdclass: The rdata class
136 @type rdclass: int
137 @param rdtype: The rdata type
138 @type rdtype: int
139 """
140
141 self.rdclass = rdclass
142 self.rdtype = rdtype
143
145 """DNS SIG/RRSIG rdatas apply to a specific type; this type is
146 returned by the covers() function. If the rdata type is not
147 SIG or RRSIG, dns.rdatatype.NONE is returned. This is useful when
148 creating rdatasets, allowing the rdataset to contain only RRSIGs
149 of a particular type, e.g. RRSIG(NS).
150 @rtype: int
151 """
152
153 return dns.rdatatype.NONE
154
156 """Return a 32-bit type value, the least significant 16 bits of
157 which are the ordinary DNS type, and the upper 16 bits of which are
158 the "covered" type, if any.
159 @rtype: int
160 """
161
162 return self.covers() << 16 | self.rdtype
163
164 - def to_text(self, origin=None, relativize=True, **kw):
165 """Convert an rdata to text format.
166 @rtype: string
167 """
168 raise NotImplementedError
169
170 - def to_wire(self, file, compress = None, origin = None):
171 """Convert an rdata to wire format.
172 @rtype: string
173 """
174
175 raise NotImplementedError
176
178 """Convert rdata to a format suitable for digesting in hashes. This
179 is also the DNSSEC canonical form."""
180 f = cStringIO.StringIO()
181 self.to_wire(f, None, origin)
182 return f.getvalue()
183
185 """Check that the current contents of the rdata's fields are
186 valid. If you change an rdata by assigning to its fields,
187 it is a good idea to call validate() when you are done making
188 changes.
189 """
190 dns.rdata.from_text(self.rdclass, self.rdtype, self.to_text())
191
201
204
205 - def _cmp(self, other):
206 """Compare an rdata with another rdata of the same rdtype and
207 rdclass. Return < 0 if self < other in the DNSSEC ordering,
208 0 if self == other, and > 0 if self > other.
209 """
210
211 raise NotImplementedError
212
214 if not isinstance(other, Rdata):
215 return False
216 if self.rdclass != other.rdclass or \
217 self.rdtype != other.rdtype:
218 return False
219 return self._cmp(other) == 0
220
222 if not isinstance(other, Rdata):
223 return True
224 if self.rdclass != other.rdclass or \
225 self.rdtype != other.rdtype:
226 return True
227 return self._cmp(other) != 0
228
235
242
249
256
259
261
262
263
264
265
266
267 b1 = cStringIO.StringIO()
268 self.to_wire(b1, None, dns.name.root)
269 b2 = cStringIO.StringIO()
270 other.to_wire(b2, None, dns.name.root)
271 return cmp(b1.getvalue(), b2.getvalue())
272
273 - def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True):
274 """Build an rdata object from text format.
275
276 @param rdclass: The rdata class
277 @type rdclass: int
278 @param rdtype: The rdata type
279 @type rdtype: int
280 @param tok: The tokenizer
281 @type tok: dns.tokenizer.Tokenizer
282 @param origin: The origin to use for relative names
283 @type origin: dns.name.Name
284 @param relativize: should names be relativized?
285 @type relativize: bool
286 @rtype: dns.rdata.Rdata instance
287 """
288
289 raise NotImplementedError
290
291 from_text = classmethod(from_text)
292
293 - def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None):
294 """Build an rdata object from wire format
295
296 @param rdclass: The rdata class
297 @type rdclass: int
298 @param rdtype: The rdata type
299 @type rdtype: int
300 @param wire: The wire-format message
301 @type wire: string
302 @param current: The offet in wire of the beginning of the rdata.
303 @type current: int
304 @param rdlen: The length of the wire-format rdata
305 @type rdlen: int
306 @param origin: The origin to use for relative names
307 @type origin: dns.name.Name
308 @rtype: dns.rdata.Rdata instance
309 """
310
311 raise NotImplementedError
312
313 from_wire = classmethod(from_wire)
314
316 """Convert any domain names in the rdata to the specified
317 relativization.
318 """
319
320 pass
321
322
324 """Generate Rdata Class
325
326 This class is used for rdata types for which we have no better
327 implementation. It implements the DNS "unknown RRs" scheme.
328 """
329
330 __slots__ = ['data']
331
332 - def __init__(self, rdclass, rdtype, data):
335
336 - def to_text(self, origin=None, relativize=True, **kw):
337 return r'\# %d ' % len(self.data) + _hexify(self.data)
338
339 - def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True):
340 token = tok.get()
341 if not token.is_identifier() or token.value != '\#':
342 raise dns.exception.SyntaxError(r'generic rdata does not start with \#')
343 length = tok.get_int()
344 chunks = []
345 while 1:
346 token = tok.get()
347 if token.is_eol_or_eof():
348 break
349 chunks.append(token.value)
350 hex = ''.join(chunks)
351 data = hex.decode('hex_codec')
352 if len(data) != length:
353 raise dns.exception.SyntaxError('generic rdata hex data has wrong length')
354 return cls(rdclass, rdtype, data)
355
356 from_text = classmethod(from_text)
357
358 - def to_wire(self, file, compress = None, origin = None):
359 file.write(self.data)
360
361 - def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None):
363
364 from_wire = classmethod(from_wire)
365
366 - def _cmp(self, other):
368
369 _rdata_modules = {}
370 _module_prefix = 'dns.rdtypes'
371
373
374 def import_module(name):
375 mod = __import__(name)
376 components = name.split('.')
377 for comp in components[1:]:
378 mod = getattr(mod, comp)
379 return mod
380
381 mod = _rdata_modules.get((rdclass, rdtype))
382 rdclass_text = dns.rdataclass.to_text(rdclass)
383 rdtype_text = dns.rdatatype.to_text(rdtype)
384 rdtype_text = rdtype_text.replace('-', '_')
385 if not mod:
386 mod = _rdata_modules.get((dns.rdatatype.ANY, rdtype))
387 if not mod:
388 try:
389 mod = import_module('.'.join([_module_prefix,
390 rdclass_text, rdtype_text]))
391 _rdata_modules[(rdclass, rdtype)] = mod
392 except ImportError:
393 try:
394 mod = import_module('.'.join([_module_prefix,
395 'ANY', rdtype_text]))
396 _rdata_modules[(dns.rdataclass.ANY, rdtype)] = mod
397 except ImportError:
398 mod = None
399 if mod:
400 cls = getattr(mod, rdtype_text)
401 else:
402 cls = GenericRdata
403 return cls
404
405 -def from_text(rdclass, rdtype, tok, origin = None, relativize = True):
406 """Build an rdata object from text format.
407
408 This function attempts to dynamically load a class which
409 implements the specified rdata class and type. If there is no
410 class-and-type-specific implementation, the GenericRdata class
411 is used.
412
413 Once a class is chosen, its from_text() class method is called
414 with the parameters to this function.
415
416 If I{tok} is a string, then a tokenizer is created and the string
417 is used as its input.
418
419 @param rdclass: The rdata class
420 @type rdclass: int
421 @param rdtype: The rdata type
422 @type rdtype: int
423 @param tok: The tokenizer or input text
424 @type tok: dns.tokenizer.Tokenizer or string
425 @param origin: The origin to use for relative names
426 @type origin: dns.name.Name
427 @param relativize: Should names be relativized?
428 @type relativize: bool
429 @rtype: dns.rdata.Rdata instance"""
430
431 if isinstance(tok, str):
432 tok = dns.tokenizer.Tokenizer(tok)
433 cls = get_rdata_class(rdclass, rdtype)
434 if cls != GenericRdata:
435
436 token = tok.get()
437 tok.unget(token)
438 if token.is_identifier() and \
439 token.value == r'\#':
440
441
442
443
444
445 rdata = GenericRdata.from_text(rdclass, rdtype, tok, origin,
446 relativize)
447 return from_wire(rdclass, rdtype, rdata.data, 0, len(rdata.data),
448 origin)
449 return cls.from_text(rdclass, rdtype, tok, origin, relativize)
450
451 -def from_wire(rdclass, rdtype, wire, current, rdlen, origin = None):
452 """Build an rdata object from wire format
453
454 This function attempts to dynamically load a class which
455 implements the specified rdata class and type. If there is no
456 class-and-type-specific implementation, the GenericRdata class
457 is used.
458
459 Once a class is chosen, its from_wire() class method is called
460 with the parameters to this function.
461
462 @param rdclass: The rdata class
463 @type rdclass: int
464 @param rdtype: The rdata type
465 @type rdtype: int
466 @param wire: The wire-format message
467 @type wire: string
468 @param current: The offet in wire of the beginning of the rdata.
469 @type current: int
470 @param rdlen: The length of the wire-format rdata
471 @type rdlen: int
472 @param origin: The origin to use for relative names
473 @type origin: dns.name.Name
474 @rtype: dns.rdata.Rdata instance"""
475
476 wire = dns.wiredata.maybe_wrap(wire)
477 cls = get_rdata_class(rdclass, rdtype)
478 return cls.from_wire(rdclass, rdtype, wire, current, rdlen, origin)
479