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 b1 = cStringIO.StringIO()
263 self.to_wire(b1)
264 b2 = cStringIO.StringIO()
265 other.to_wire(b2)
266 return cmp(b1.getvalue(), b2.getvalue())
267
268 - def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True):
269 """Build an rdata object from text format.
270
271 @param rdclass: The rdata class
272 @type rdclass: int
273 @param rdtype: The rdata type
274 @type rdtype: int
275 @param tok: The tokenizer
276 @type tok: dns.tokenizer.Tokenizer
277 @param origin: The origin to use for relative names
278 @type origin: dns.name.Name
279 @param relativize: should names be relativized?
280 @type relativize: bool
281 @rtype: dns.rdata.Rdata instance
282 """
283
284 raise NotImplementedError
285
286 from_text = classmethod(from_text)
287
288 - def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None):
289 """Build an rdata object from wire format
290
291 @param rdclass: The rdata class
292 @type rdclass: int
293 @param rdtype: The rdata type
294 @type rdtype: int
295 @param wire: The wire-format message
296 @type wire: string
297 @param current: The offet in wire of the beginning of the rdata.
298 @type current: int
299 @param rdlen: The length of the wire-format rdata
300 @type rdlen: int
301 @param origin: The origin to use for relative names
302 @type origin: dns.name.Name
303 @rtype: dns.rdata.Rdata instance
304 """
305
306 raise NotImplementedError
307
308 from_wire = classmethod(from_wire)
309
311 """Convert any domain names in the rdata to the specified
312 relativization.
313 """
314
315 pass
316
317
319 """Generate Rdata Class
320
321 This class is used for rdata types for which we have no better
322 implementation. It implements the DNS "unknown RRs" scheme.
323 """
324
325 __slots__ = ['data']
326
327 - def __init__(self, rdclass, rdtype, data):
330
331 - def to_text(self, origin=None, relativize=True, **kw):
332 return r'\# %d ' % len(self.data) + _hexify(self.data)
333
334 - def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True):
335 token = tok.get()
336 if not token.is_identifier() or token.value != '\#':
337 raise dns.exception.SyntaxError(r'generic rdata does not start with \#')
338 length = tok.get_int()
339 chunks = []
340 while 1:
341 token = tok.get()
342 if token.is_eol_or_eof():
343 break
344 chunks.append(token.value)
345 hex = ''.join(chunks)
346 data = hex.decode('hex_codec')
347 if len(data) != length:
348 raise dns.exception.SyntaxError('generic rdata hex data has wrong length')
349 return cls(rdclass, rdtype, data)
350
351 from_text = classmethod(from_text)
352
353 - def to_wire(self, file, compress = None, origin = None):
354 file.write(self.data)
355
356 - def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None):
358
359 from_wire = classmethod(from_wire)
360
361 - def _cmp(self, other):
363
364 _rdata_modules = {}
365 _module_prefix = 'dns.rdtypes'
366
368
369 def import_module(name):
370 mod = __import__(name)
371 components = name.split('.')
372 for comp in components[1:]:
373 mod = getattr(mod, comp)
374 return mod
375
376 mod = _rdata_modules.get((rdclass, rdtype))
377 rdclass_text = dns.rdataclass.to_text(rdclass)
378 rdtype_text = dns.rdatatype.to_text(rdtype)
379 rdtype_text = rdtype_text.replace('-', '_')
380 if not mod:
381 mod = _rdata_modules.get((dns.rdatatype.ANY, rdtype))
382 if not mod:
383 try:
384 mod = import_module('.'.join([_module_prefix,
385 rdclass_text, rdtype_text]))
386 _rdata_modules[(rdclass, rdtype)] = mod
387 except ImportError:
388 try:
389 mod = import_module('.'.join([_module_prefix,
390 'ANY', rdtype_text]))
391 _rdata_modules[(dns.rdataclass.ANY, rdtype)] = mod
392 except ImportError:
393 mod = None
394 if mod:
395 cls = getattr(mod, rdtype_text)
396 else:
397 cls = GenericRdata
398 return cls
399
400 -def from_text(rdclass, rdtype, tok, origin = None, relativize = True):
401 """Build an rdata object from text format.
402
403 This function attempts to dynamically load a class which
404 implements the specified rdata class and type. If there is no
405 class-and-type-specific implementation, the GenericRdata class
406 is used.
407
408 Once a class is chosen, its from_text() class method is called
409 with the parameters to this function.
410
411 @param rdclass: The rdata class
412 @type rdclass: int
413 @param rdtype: The rdata type
414 @type rdtype: int
415 @param tok: The tokenizer
416 @type tok: dns.tokenizer.Tokenizer
417 @param origin: The origin to use for relative names
418 @type origin: dns.name.Name
419 @param relativize: Should names be relativized?
420 @type relativize: bool
421 @rtype: dns.rdata.Rdata instance"""
422
423 if isinstance(tok, str):
424 tok = dns.tokenizer.Tokenizer(tok)
425 cls = get_rdata_class(rdclass, rdtype)
426 if cls != GenericRdata:
427
428 token = tok.get()
429 tok.unget(token)
430 if token.is_identifier() and \
431 token.value == r'\#':
432
433
434
435
436
437 rdata = GenericRdata.from_text(rdclass, rdtype, tok, origin,
438 relativize)
439 return from_wire(rdclass, rdtype, rdata.data, 0, len(rdata.data),
440 origin)
441 return cls.from_text(rdclass, rdtype, tok, origin, relativize)
442
443 -def from_wire(rdclass, rdtype, wire, current, rdlen, origin = None):
444 """Build an rdata object from wire format
445
446 This function attempts to dynamically load a class which
447 implements the specified rdata class and type. If there is no
448 class-and-type-specific implementation, the GenericRdata class
449 is used.
450
451 Once a class is chosen, its from_wire() class method is called
452 with the parameters to this function.
453
454 @param rdclass: The rdata class
455 @type rdclass: int
456 @param rdtype: The rdata type
457 @type rdtype: int
458 @param wire: The wire-format message
459 @type wire: string
460 @param current: The offet in wire of the beginning of the rdata.
461 @type current: int
462 @param rdlen: The length of the wire-format rdata
463 @type rdlen: int
464 @param origin: The origin to use for relative names
465 @type origin: dns.name.Name
466 @rtype: dns.rdata.Rdata instance"""
467
468 cls = get_rdata_class(rdclass, rdtype)
469 return cls.from_wire(rdclass, rdtype, wire, current, rdlen, origin)
470