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