Package dns :: Module edns
[hide private]
[frames] | no frames]

Source Code for Module dns.edns

  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 128 -class GenericOption(Option):
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 158 -class ECSOption(Option):
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)
270