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