1# Copyright (C) 2009 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"""EDNS Options"""
17
18NSID = 3
19
20class Option(object):
21    """Base class for all EDNS option types.
22    """
23
24    def __init__(self, otype):
25        """Initialize an option.
26        @param rdtype: The rdata type
27        @type rdtype: int
28        """
29        self.otype = otype
30
31    def to_wire(self, file):
32        """Convert an option to wire format.
33        """
34        raise NotImplementedError
35
36    def from_wire(cls, otype, wire, current, olen):
37        """Build an EDNS option object from wire format
38
39        @param otype: The option type
40        @type otype: int
41        @param wire: The wire-format message
42        @type wire: string
43        @param current: The offet in wire of the beginning of the rdata.
44        @type current: int
45        @param olen: The length of the wire-format option data
46        @type olen: int
47        @rtype: dns.ends.Option instance"""
48        raise NotImplementedError
49
50    from_wire = classmethod(from_wire)
51
52    def _cmp(self, other):
53        """Compare an ENDS option with another option of the same type.
54        Return < 0 if self < other, 0 if self == other, and > 0 if self > other.
55        """
56        raise NotImplementedError
57
58    def __eq__(self, other):
59        if not isinstance(other, Option):
60            return False
61        if self.otype != other.otype:
62            return False
63        return self._cmp(other) == 0
64
65    def __ne__(self, other):
66        if not isinstance(other, Option):
67            return False
68        if self.otype != other.otype:
69            return False
70        return self._cmp(other) != 0
71
72    def __lt__(self, other):
73        if not isinstance(other, Option) or \
74               self.otype != other.otype:
75            return NotImplemented
76        return self._cmp(other) < 0
77
78    def __le__(self, other):
79        if not isinstance(other, Option) or \
80               self.otype != other.otype:
81            return NotImplemented
82        return self._cmp(other) <= 0
83
84    def __ge__(self, other):
85        if not isinstance(other, Option) or \
86               self.otype != other.otype:
87            return NotImplemented
88        return self._cmp(other) >= 0
89
90    def __gt__(self, other):
91        if not isinstance(other, Option) or \
92               self.otype != other.otype:
93            return NotImplemented
94        return self._cmp(other) > 0
95
96
97class GenericOption(Option):
98    """Generate Rdata Class
99
100    This class is used for EDNS option types for which we have no better
101    implementation.
102    """
103
104    def __init__(self, otype, data):
105        super(GenericOption, self).__init__(otype)
106        self.data = data
107
108    def to_wire(self, file):
109        file.write(self.data)
110
111    def from_wire(cls, otype, wire, current, olen):
112        return cls(otype, wire[current : current + olen])
113
114    from_wire = classmethod(from_wire)
115
116    def _cmp(self, other):
117	return cmp(self.data, other.data)
118
119_type_to_class = {
120}
121
122def get_option_class(otype):
123    cls = _type_to_class.get(otype)
124    if cls is None:
125        cls = GenericOption
126    return cls
127
128def option_from_wire(otype, wire, current, olen):
129    """Build an EDNS option object from wire format
130
131    @param otype: The option type
132    @type otype: int
133    @param wire: The wire-format message
134    @type wire: string
135    @param current: The offet in wire of the beginning of the rdata.
136    @type current: int
137    @param olen: The length of the wire-format option data
138    @type olen: int
139    @rtype: dns.ends.Option instance"""
140
141    cls = get_option_class(otype)
142    return cls.from_wire(otype, wire, current, olen)
143