1# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
2
3# Copyright (C) 2001-2017 Nominum, Inc.
4#
5# Permission to use, copy, modify, and distribute this software and its
6# documentation for any purpose with or without fee is hereby granted,
7# provided that the above copyright notice and this permission notice
8# appear in all copies.
9#
10# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
11# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
13# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
16# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
18"""DNS Rdata Types."""
19
20import re
21
22import dns.exception
23
24NONE = 0
25A = 1
26NS = 2
27MD = 3
28MF = 4
29CNAME = 5
30SOA = 6
31MB = 7
32MG = 8
33MR = 9
34NULL = 10
35WKS = 11
36PTR = 12
37HINFO = 13
38MINFO = 14
39MX = 15
40TXT = 16
41RP = 17
42AFSDB = 18
43X25 = 19
44ISDN = 20
45RT = 21
46NSAP = 22
47NSAP_PTR = 23
48SIG = 24
49KEY = 25
50PX = 26
51GPOS = 27
52AAAA = 28
53LOC = 29
54NXT = 30
55SRV = 33
56NAPTR = 35
57KX = 36
58CERT = 37
59A6 = 38
60DNAME = 39
61OPT = 41
62APL = 42
63DS = 43
64SSHFP = 44
65IPSECKEY = 45
66RRSIG = 46
67NSEC = 47
68DNSKEY = 48
69DHCID = 49
70NSEC3 = 50
71NSEC3PARAM = 51
72TLSA = 52
73HIP = 55
74CDS = 59
75CDNSKEY = 60
76OPENPGPKEY = 61
77CSYNC = 62
78SPF = 99
79UNSPEC = 103
80EUI48 = 108
81EUI64 = 109
82TKEY = 249
83TSIG = 250
84IXFR = 251
85AXFR = 252
86MAILB = 253
87MAILA = 254
88ANY = 255
89URI = 256
90CAA = 257
91AVC = 258
92TA = 32768
93DLV = 32769
94
95_by_text = {
96    'NONE': NONE,
97    'A': A,
98    'NS': NS,
99    'MD': MD,
100    'MF': MF,
101    'CNAME': CNAME,
102    'SOA': SOA,
103    'MB': MB,
104    'MG': MG,
105    'MR': MR,
106    'NULL': NULL,
107    'WKS': WKS,
108    'PTR': PTR,
109    'HINFO': HINFO,
110    'MINFO': MINFO,
111    'MX': MX,
112    'TXT': TXT,
113    'RP': RP,
114    'AFSDB': AFSDB,
115    'X25': X25,
116    'ISDN': ISDN,
117    'RT': RT,
118    'NSAP': NSAP,
119    'NSAP-PTR': NSAP_PTR,
120    'SIG': SIG,
121    'KEY': KEY,
122    'PX': PX,
123    'GPOS': GPOS,
124    'AAAA': AAAA,
125    'LOC': LOC,
126    'NXT': NXT,
127    'SRV': SRV,
128    'NAPTR': NAPTR,
129    'KX': KX,
130    'CERT': CERT,
131    'A6': A6,
132    'DNAME': DNAME,
133    'OPT': OPT,
134    'APL': APL,
135    'DS': DS,
136    'SSHFP': SSHFP,
137    'IPSECKEY': IPSECKEY,
138    'RRSIG': RRSIG,
139    'NSEC': NSEC,
140    'DNSKEY': DNSKEY,
141    'DHCID': DHCID,
142    'NSEC3': NSEC3,
143    'NSEC3PARAM': NSEC3PARAM,
144    'TLSA': TLSA,
145    'HIP': HIP,
146    'CDS': CDS,
147    'CDNSKEY': CDNSKEY,
148    'OPENPGPKEY': OPENPGPKEY,
149    'CSYNC': CSYNC,
150    'SPF': SPF,
151    'UNSPEC': UNSPEC,
152    'EUI48': EUI48,
153    'EUI64': EUI64,
154    'TKEY': TKEY,
155    'TSIG': TSIG,
156    'IXFR': IXFR,
157    'AXFR': AXFR,
158    'MAILB': MAILB,
159    'MAILA': MAILA,
160    'ANY': ANY,
161    'URI': URI,
162    'CAA': CAA,
163    'AVC': AVC,
164    'TA': TA,
165    'DLV': DLV,
166}
167
168# We construct the inverse mapping programmatically to ensure that we
169# cannot make any mistakes (e.g. omissions, cut-and-paste errors) that
170# would cause the mapping not to be true inverse.
171
172_by_value = {y: x for x, y in _by_text.items()}
173
174_metatypes = {
175    OPT: True
176}
177
178_singletons = {
179    SOA: True,
180    NXT: True,
181    DNAME: True,
182    NSEC: True,
183    CNAME: True,
184}
185
186_unknown_type_pattern = re.compile('TYPE([0-9]+)$', re.I)
187
188
189class UnknownRdatatype(dns.exception.DNSException):
190    """DNS resource record type is unknown."""
191
192
193def from_text(text):
194    """Convert text into a DNS rdata type value.
195
196    The input text can be a defined DNS RR type mnemonic or
197    instance of the DNS generic type syntax.
198
199    For example, "NS" and "TYPE2" will both result in a value of 2.
200
201    Raises ``dns.rdatatype.UnknownRdatatype`` if the type is unknown.
202
203    Raises ``ValueError`` if the rdata type value is not >= 0 and <= 65535.
204
205    Returns an ``int``.
206    """
207
208    value = _by_text.get(text.upper())
209    if value is None:
210        match = _unknown_type_pattern.match(text)
211        if match is None:
212            raise UnknownRdatatype
213        value = int(match.group(1))
214        if value < 0 or value > 65535:
215            raise ValueError("type must be between >= 0 and <= 65535")
216    return value
217
218
219def to_text(value):
220    """Convert a DNS rdata type value to text.
221
222    If the value has a known mnemonic, it will be used, otherwise the
223    DNS generic type syntax will be used.
224
225    Raises ``ValueError`` if the rdata type value is not >= 0 and <= 65535.
226
227    Returns a ``str``.
228    """
229
230    if value < 0 or value > 65535:
231        raise ValueError("type must be between >= 0 and <= 65535")
232    text = _by_value.get(value)
233    if text is None:
234        text = 'TYPE' + repr(value)
235    return text
236
237
238def is_metatype(rdtype):
239    """True if the specified type is a metatype.
240
241    *rdtype* is an ``int``.
242
243    The currently defined metatypes are TKEY, TSIG, IXFR, AXFR, MAILA,
244    MAILB, ANY, and OPT.
245
246    Returns a ``bool``.
247    """
248
249    if rdtype >= TKEY and rdtype <= ANY or rdtype in _metatypes:
250        return True
251    return False
252
253
254def is_singleton(rdtype):
255    """Is the specified type a singleton type?
256
257    Singleton types can only have a single rdata in an rdataset, or a single
258    RR in an RRset.
259
260    The currently defined singleton types are CNAME, DNAME, NSEC, NXT, and
261    SOA.
262
263    *rdtype* is an ``int``.
264
265    Returns a ``bool``.
266    """
267
268    if rdtype in _singletons:
269        return True
270    return False
271
272
273def register_type(rdtype, rdtype_text, is_singleton=False):  # pylint: disable=redefined-outer-name
274    """Dynamically register an rdatatype.
275
276    *rdtype*, an ``int``, the rdatatype to register.
277
278    *rdtype_text*, a ``text``, the textual form of the rdatatype.
279
280    *is_singleton*, a ``bool``, indicating if the type is a singleton (i.e.
281    RRsets of the type can have only one member.)
282    """
283
284    _by_text[rdtype_text] = rdtype
285    _by_value[rdtype] = rdtype_text
286    if is_singleton:
287        _singletons[rdtype] = True
288