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 Opcodes."""
19
20import dns.exception
21
22#: Query
23QUERY = 0
24#: Inverse Query (historical)
25IQUERY = 1
26#: Server Status (unspecified and unimplemented anywhere)
27STATUS = 2
28#: Notify
29NOTIFY = 4
30#: Dynamic Update
31UPDATE = 5
32
33_by_text = {
34    'QUERY': QUERY,
35    'IQUERY': IQUERY,
36    'STATUS': STATUS,
37    'NOTIFY': NOTIFY,
38    'UPDATE': UPDATE
39}
40
41# We construct the inverse mapping programmatically to ensure that we
42# cannot make any mistakes (e.g. omissions, cut-and-paste errors) that
43# would cause the mapping not to be true inverse.
44
45_by_value = {y: x for x, y in _by_text.items()}
46
47
48class UnknownOpcode(dns.exception.DNSException):
49    """An DNS opcode is unknown."""
50
51
52def from_text(text):
53    """Convert text into an opcode.
54
55    *text*, a ``text``, the textual opcode
56
57    Raises ``dns.opcode.UnknownOpcode`` if the opcode is unknown.
58
59    Returns an ``int``.
60    """
61
62    if text.isdigit():
63        value = int(text)
64        if value >= 0 and value <= 15:
65            return value
66    value = _by_text.get(text.upper())
67    if value is None:
68        raise UnknownOpcode
69    return value
70
71
72def from_flags(flags):
73    """Extract an opcode from DNS message flags.
74
75    *flags*, an ``int``, the DNS flags.
76
77    Returns an ``int``.
78    """
79
80    return (flags & 0x7800) >> 11
81
82
83def to_flags(value):
84    """Convert an opcode to a value suitable for ORing into DNS message
85    flags.
86
87    *value*, an ``int``, the DNS opcode value.
88
89    Returns an ``int``.
90    """
91
92    return (value << 11) & 0x7800
93
94
95def to_text(value):
96    """Convert an opcode to text.
97
98    *value*, an ``int`` the opcode value,
99
100    Raises ``dns.opcode.UnknownOpcode`` if the opcode is unknown.
101
102    Returns a ``text``.
103    """
104
105    text = _by_value.get(value)
106    if text is None:
107        text = str(value)
108    return text
109
110
111def is_update(flags):
112    """Is the opcode in flags UPDATE?
113
114    *flags*, an ``int``, the DNS message flags.
115
116    Returns a ``bool``.
117    """
118
119    return from_flags(flags) == UPDATE
120