1# Copyright (C) 2001-2007, 2009-2011 Nominum, Inc.
2#
3# Permission to use, copy, modify, and distribute this software and its
4# documentation for any purpose with or without fee is hereby granted,
5# provided that the above copyright notice and this permission notice
6# appear in all copies.
7#
8# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
9# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
11# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
14# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15
16"""DNS Rdata Classes.
17
18@var _by_text: The rdata class textual name to value mapping
19@type _by_text: dict
20@var _by_value: The rdata class value to textual name mapping
21@type _by_value: dict
22@var _metaclasses: If an rdataclass is a metaclass, there will be a mapping
23whose key is the rdatatype value and whose value is True in this dictionary.
24@type _metaclasses: dict"""
25
26import re
27
28import dns.exception
29
30RESERVED0 = 0
31IN = 1
32CH = 3
33HS = 4
34NONE = 254
35ANY = 255
36
37_by_text = {
38    'RESERVED0': RESERVED0,
39    'IN': IN,
40    'CH': CH,
41    'HS': HS,
42    'NONE': NONE,
43    'ANY': ANY
44}
45
46# We construct the inverse mapping programmatically to ensure that we
47# cannot make any mistakes (e.g. omissions, cut-and-paste errors) that
48# would cause the mapping not to be true inverse.
49
50_by_value = dict((y, x) for x, y in _by_text.items())
51
52# Now that we've built the inverse map, we can add class aliases to
53# the _by_text mapping.
54
55_by_text.update({
56    'INTERNET': IN,
57    'CHAOS': CH,
58    'HESIOD': HS
59})
60
61_metaclasses = {
62    NONE: True,
63    ANY: True
64}
65
66_unknown_class_pattern = re.compile('CLASS([0-9]+)$', re.I)
67
68
69class UnknownRdataclass(dns.exception.DNSException):
70
71    """A DNS class is unknown."""
72
73
74def from_text(text):
75    """Convert text into a DNS rdata class value.
76    @param text: the text
77    @type text: string
78    @rtype: int
79    @raises dns.rdataclass.UnknownRdataclass: the class is unknown
80    @raises ValueError: the rdata class value is not >= 0 and <= 65535
81    """
82
83    value = _by_text.get(text.upper())
84    if value is None:
85        match = _unknown_class_pattern.match(text)
86        if match is None:
87            raise UnknownRdataclass
88        value = int(match.group(1))
89        if value < 0 or value > 65535:
90            raise ValueError("class must be between >= 0 and <= 65535")
91    return value
92
93
94def to_text(value):
95    """Convert a DNS rdata class to text.
96    @param value: the rdata class value
97    @type value: int
98    @rtype: string
99    @raises ValueError: the rdata class value is not >= 0 and <= 65535
100    """
101
102    if value < 0 or value > 65535:
103        raise ValueError("class must be between >= 0 and <= 65535")
104    text = _by_value.get(value)
105    if text is None:
106        text = 'CLASS' + repr(value)
107    return text
108
109
110def is_metaclass(rdclass):
111    """True if the class is a metaclass.
112    @param rdclass: the rdata class
113    @type rdclass: int
114    @rtype: bool"""
115
116    if rdclass in _metaclasses:
117        return True
118    return False
119