dnspython  1.16.0
About: dnspython is a DNS toolkit (for Python 2.x) that supports almost all record types.
  Fossies Dox: dnspython-1.16.0.tar.gz  ("inofficial" and yet experimental doxygen-generated source code documentation)  

edns.py
Go to the documentation of this file.
1 # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
2 
3 # Copyright (C) 2009-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 """EDNS Options"""
19 
20 from __future__ import absolute_import
21 
22 import math
23 import struct
24 
25 import dns.inet
26 
27 #: NSID
28 NSID = 3
29 #: DAU
30 DAU = 5
31 #: DHU
32 DHU = 6
33 #: N3U
34 N3U = 7
35 #: ECS (client-subnet)
36 ECS = 8
37 #: EXPIRE
38 EXPIRE = 9
39 #: COOKIE
40 COOKIE = 10
41 #: KEEPALIVE
42 KEEPALIVE = 11
43 #: PADDING
44 PADDING = 12
45 #: CHAIN
46 CHAIN = 13
47 
48 class Option(object):
49 
50  """Base class for all EDNS option types."""
51 
52  def __init__(self, otype):
53  """Initialize an option.
54 
55  *otype*, an ``int``, is the option type.
56  """
57  self.otype = otype
58 
59  def to_wire(self, file):
60  """Convert an option to wire format.
61  """
62  raise NotImplementedError
63 
64  @classmethod
65  def from_wire(cls, otype, wire, current, olen):
66  """Build an EDNS option object from wire format.
67 
68  *otype*, an ``int``, is the option type.
69 
70  *wire*, a ``binary``, is the wire-format message.
71 
72  *current*, an ``int``, is the offset in *wire* of the beginning
73  of the rdata.
74 
75  *olen*, an ``int``, is the length of the wire-format option data
76 
77  Returns a ``dns.edns.Option``.
78  """
79 
80  raise NotImplementedError
81 
82  def _cmp(self, other):
83  """Compare an EDNS option with another option of the same type.
84 
85  Returns < 0 if < *other*, 0 if == *other*, and > 0 if > *other*.
86  """
87  raise NotImplementedError
88 
89  def __eq__(self, other):
90  if not isinstance(other, Option):
91  return False
92  if self.otype != other.otype:
93  return False
94  return self._cmp(other) == 0
95 
96  def __ne__(self, other):
97  if not isinstance(other, Option):
98  return False
99  if self.otype != other.otype:
100  return False
101  return self._cmp(other) != 0
102 
103  def __lt__(self, other):
104  if not isinstance(other, Option) or \
105  self.otype != other.otype:
106  return NotImplemented
107  return self._cmp(other) < 0
108 
109  def __le__(self, other):
110  if not isinstance(other, Option) or \
111  self.otype != other.otype:
112  return NotImplemented
113  return self._cmp(other) <= 0
114 
115  def __ge__(self, other):
116  if not isinstance(other, Option) or \
117  self.otype != other.otype:
118  return NotImplemented
119  return self._cmp(other) >= 0
120 
121  def __gt__(self, other):
122  if not isinstance(other, Option) or \
123  self.otype != other.otype:
124  return NotImplemented
125  return self._cmp(other) > 0
126 
127 
129 
130  """Generic Option Class
131 
132  This class is used for EDNS option types for which we have no better
133  implementation.
134  """
135 
136  def __init__(self, otype, data):
137  super(GenericOption, self).__init__(otype)
138  self.data = data
139 
140  def to_wire(self, file):
141  file.write(self.data)
142 
143  def to_text(self):
144  return "Generic %d" % self.otype
145 
146  @classmethod
147  def from_wire(cls, otype, wire, current, olen):
148  return cls(otype, wire[current: current + olen])
149 
150  def _cmp(self, other):
151  if self.data == other.data:
152  return 0
153  if self.data > other.data:
154  return 1
155  return -1
156 
157 
159  """EDNS Client Subnet (ECS, RFC7871)"""
160 
161  def __init__(self, address, srclen=None, scopelen=0):
162  """*address*, a ``text``, is the client address information.
163 
164  *srclen*, an ``int``, the source prefix length, which is the
165  leftmost number of bits of the address to be used for the
166  lookup. The default is 24 for IPv4 and 56 for IPv6.
167 
168  *scopelen*, an ``int``, the scope prefix length. This value
169  must be 0 in queries, and should be set in responses.
170  """
171 
172  super(ECSOption, self).__init__(ECS)
173  af = dns.inet.af_for_address(address)
174 
175  if af == dns.inet.AF_INET6:
176  self.family = 2
177  if srclen is None:
178  srclen = 56
179  elif af == dns.inet.AF_INET:
180  self.family = 1
181  if srclen is None:
182  srclen = 24
183  else:
184  raise ValueError('Bad ip family')
185 
186  self.address = address
187  self.srclen = srclen
188  self.scopelen = scopelen
189 
190  addrdata = dns.inet.inet_pton(af, address)
191  nbytes = int(math.ceil(srclen/8.0))
192 
193  # Truncate to srclen and pad to the end of the last octet needed
194  # See RFC section 6
195  self.addrdata = addrdata[:nbytes]
196  nbits = srclen % 8
197  if nbits != 0:
198  last = struct.pack('B', ord(self.addrdata[-1:]) & (0xff << nbits))
199  self.addrdata = self.addrdata[:-1] + last
200 
201  def to_text(self):
202  return "ECS {}/{} scope/{}".format(self.address, self.srclen,
203  self.scopelen)
204 
205  def to_wire(self, file):
206  file.write(struct.pack('!H', self.family))
207  file.write(struct.pack('!BB', self.srclen, self.scopelen))
208  file.write(self.addrdata)
209 
210  @classmethod
211  def from_wire(cls, otype, wire, cur, olen):
212  family, src, scope = struct.unpack('!HBB', wire[cur:cur+4])
213  cur += 4
214 
215  addrlen = int(math.ceil(src/8.0))
216 
217  if family == 1:
218  af = dns.inet.AF_INET
219  pad = 4 - addrlen
220  elif family == 2:
221  af = dns.inet.AF_INET6
222  pad = 16 - addrlen
223  else:
224  raise ValueError('unsupported family')
225 
226  addr = dns.inet.inet_ntop(af, wire[cur:cur+addrlen] + b'\x00' * pad)
227  return cls(addr, src, scope)
228 
229  def _cmp(self, other):
230  if self.addrdata == other.addrdata:
231  return 0
232  if self.addrdata > other.addrdata:
233  return 1
234  return -1
235 
236 _type_to_class = {
237  ECS: ECSOption
238 }
239 
240 def get_option_class(otype):
241  """Return the class for the specified option type.
242 
243  The GenericOption class is used if a more specific class is not
244  known.
245  """
246 
247  cls = _type_to_class.get(otype)
248  if cls is None:
249  cls = GenericOption
250  return cls
251 
252 
253 def option_from_wire(otype, wire, current, olen):
254  """Build an EDNS option object from wire format.
255 
256  *otype*, an ``int``, is the option type.
257 
258  *wire*, a ``binary``, is the wire-format message.
259 
260  *current*, an ``int``, is the offset in *wire* of the beginning
261  of the rdata.
262 
263  *olen*, an ``int``, is the length of the wire-format option data
264 
265  Returns an instance of a subclass of ``dns.edns.Option``.
266  """
267 
268  cls = get_option_class(otype)
269  return cls.from_wire(otype, wire, current, olen)
dns.edns.GenericOption.data
data
Definition: edns.py:138
dns.edns.Option.__ge__
def __ge__(self, other)
Definition: edns.py:115
dns.edns.Option.__ne__
def __ne__(self, other)
Definition: edns.py:96
dns.edns.ECSOption
Definition: edns.py:158
dns.edns.GenericOption.to_text
def to_text(self)
Definition: edns.py:143
dns.edns.GenericOption.to_wire
def to_wire(self, file)
Definition: edns.py:140
dns.edns.Option.__gt__
def __gt__(self, other)
Definition: edns.py:121
dns.edns.ECSOption.__init__
def __init__(self, address, srclen=None, scopelen=0)
Definition: edns.py:161
dns.edns.Option.__lt__
def __lt__(self, other)
Definition: edns.py:103
dns.edns.GenericOption.__init__
def __init__(self, otype, data)
Definition: edns.py:136
dns.edns.GenericOption
Definition: edns.py:128
dns.edns.ECSOption.family
family
Definition: edns.py:176
dns.edns.GenericOption.from_wire
def from_wire(cls, otype, wire, current, olen)
Definition: edns.py:147
dns.edns.Option
Definition: edns.py:48
dns.inet.af_for_address
def af_for_address(text)
Definition: inet.py:83
dns.edns.GenericOption._cmp
def _cmp(self, other)
Definition: edns.py:150
dns.inet
Definition: inet.py:1
dns.edns.ECSOption.to_wire
def to_wire(self, file)
Definition: edns.py:205
dns.edns.option_from_wire
def option_from_wire(otype, wire, current, olen)
Definition: edns.py:253
dns.edns.Option.from_wire
def from_wire(cls, otype, wire, current, olen)
Definition: edns.py:65
dns.edns.get_option_class
def get_option_class(otype)
Definition: edns.py:240
dns.edns.ECSOption._cmp
def _cmp(self, other)
Definition: edns.py:229
dns.edns.Option.__le__
def __le__(self, other)
Definition: edns.py:109
dns.edns.Option.to_wire
def to_wire(self, file)
Definition: edns.py:59
dns.edns.Option.__eq__
def __eq__(self, other)
Definition: edns.py:89
dns.edns.ECSOption.from_wire
def from_wire(cls, otype, wire, cur, olen)
Definition: edns.py:211
dns.edns.ECSOption.addrdata
addrdata
Definition: edns.py:195
dns.edns.Option._cmp
def _cmp(self, other)
Definition: edns.py:82
dns.edns.Option.otype
otype
Definition: edns.py:57
dns.edns.ECSOption.scopelen
scopelen
Definition: edns.py:188
dns.edns.ECSOption.to_text
def to_text(self)
Definition: edns.py:201
dns.inet.inet_pton
def inet_pton(family, text)
Definition: inet.py:41
dns.edns.ECSOption.address
address
Definition: edns.py:186
dns.edns.Option.__init__
def __init__(self, otype)
Definition: edns.py:52
dns.inet.inet_ntop
def inet_ntop(family, address)
Definition: inet.py:62
dns.edns.ECSOption.srclen
srclen
Definition: edns.py:187