You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

269 lines
7.3 KiB

4 years ago
  1. # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
  2. # Copyright (C) 2009-2017 Nominum, Inc.
  3. #
  4. # Permission to use, copy, modify, and distribute this software and its
  5. # documentation for any purpose with or without fee is hereby granted,
  6. # provided that the above copyright notice and this permission notice
  7. # appear in all copies.
  8. #
  9. # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
  10. # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  11. # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
  12. # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  13. # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  14. # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
  15. # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  16. """EDNS Options"""
  17. from __future__ import absolute_import
  18. import math
  19. import struct
  20. import dns.inet
  21. #: NSID
  22. NSID = 3
  23. #: DAU
  24. DAU = 5
  25. #: DHU
  26. DHU = 6
  27. #: N3U
  28. N3U = 7
  29. #: ECS (client-subnet)
  30. ECS = 8
  31. #: EXPIRE
  32. EXPIRE = 9
  33. #: COOKIE
  34. COOKIE = 10
  35. #: KEEPALIVE
  36. KEEPALIVE = 11
  37. #: PADDING
  38. PADDING = 12
  39. #: CHAIN
  40. CHAIN = 13
  41. class Option(object):
  42. """Base class for all EDNS option types."""
  43. def __init__(self, otype):
  44. """Initialize an option.
  45. *otype*, an ``int``, is the option type.
  46. """
  47. self.otype = otype
  48. def to_wire(self, file):
  49. """Convert an option to wire format.
  50. """
  51. raise NotImplementedError
  52. @classmethod
  53. def from_wire(cls, otype, wire, current, olen):
  54. """Build an EDNS option object from wire format.
  55. *otype*, an ``int``, is the option type.
  56. *wire*, a ``binary``, is the wire-format message.
  57. *current*, an ``int``, is the offset in *wire* of the beginning
  58. of the rdata.
  59. *olen*, an ``int``, is the length of the wire-format option data
  60. Returns a ``dns.edns.Option``.
  61. """
  62. raise NotImplementedError
  63. def _cmp(self, other):
  64. """Compare an EDNS option with another option of the same type.
  65. Returns < 0 if < *other*, 0 if == *other*, and > 0 if > *other*.
  66. """
  67. raise NotImplementedError
  68. def __eq__(self, other):
  69. if not isinstance(other, Option):
  70. return False
  71. if self.otype != other.otype:
  72. return False
  73. return self._cmp(other) == 0
  74. def __ne__(self, other):
  75. if not isinstance(other, Option):
  76. return False
  77. if self.otype != other.otype:
  78. return False
  79. return self._cmp(other) != 0
  80. def __lt__(self, other):
  81. if not isinstance(other, Option) or \
  82. self.otype != other.otype:
  83. return NotImplemented
  84. return self._cmp(other) < 0
  85. def __le__(self, other):
  86. if not isinstance(other, Option) or \
  87. self.otype != other.otype:
  88. return NotImplemented
  89. return self._cmp(other) <= 0
  90. def __ge__(self, other):
  91. if not isinstance(other, Option) or \
  92. self.otype != other.otype:
  93. return NotImplemented
  94. return self._cmp(other) >= 0
  95. def __gt__(self, other):
  96. if not isinstance(other, Option) or \
  97. self.otype != other.otype:
  98. return NotImplemented
  99. return self._cmp(other) > 0
  100. class GenericOption(Option):
  101. """Generic Option Class
  102. This class is used for EDNS option types for which we have no better
  103. implementation.
  104. """
  105. def __init__(self, otype, data):
  106. super(GenericOption, self).__init__(otype)
  107. self.data = data
  108. def to_wire(self, file):
  109. file.write(self.data)
  110. def to_text(self):
  111. return "Generic %d" % self.otype
  112. @classmethod
  113. def from_wire(cls, otype, wire, current, olen):
  114. return cls(otype, wire[current: current + olen])
  115. def _cmp(self, other):
  116. if self.data == other.data:
  117. return 0
  118. if self.data > other.data:
  119. return 1
  120. return -1
  121. class ECSOption(Option):
  122. """EDNS Client Subnet (ECS, RFC7871)"""
  123. def __init__(self, address, srclen=None, scopelen=0):
  124. """*address*, a ``text``, is the client address information.
  125. *srclen*, an ``int``, the source prefix length, which is the
  126. leftmost number of bits of the address to be used for the
  127. lookup. The default is 24 for IPv4 and 56 for IPv6.
  128. *scopelen*, an ``int``, the scope prefix length. This value
  129. must be 0 in queries, and should be set in responses.
  130. """
  131. super(ECSOption, self).__init__(ECS)
  132. af = dns.inet.af_for_address(address)
  133. if af == dns.inet.AF_INET6:
  134. self.family = 2
  135. if srclen is None:
  136. srclen = 56
  137. elif af == dns.inet.AF_INET:
  138. self.family = 1
  139. if srclen is None:
  140. srclen = 24
  141. else:
  142. raise ValueError('Bad ip family')
  143. self.address = address
  144. self.srclen = srclen
  145. self.scopelen = scopelen
  146. addrdata = dns.inet.inet_pton(af, address)
  147. nbytes = int(math.ceil(srclen/8.0))
  148. # Truncate to srclen and pad to the end of the last octet needed
  149. # See RFC section 6
  150. self.addrdata = addrdata[:nbytes]
  151. nbits = srclen % 8
  152. if nbits != 0:
  153. last = struct.pack('B', ord(self.addrdata[-1:]) & (0xff << nbits))
  154. self.addrdata = self.addrdata[:-1] + last
  155. def to_text(self):
  156. return "ECS {}/{} scope/{}".format(self.address, self.srclen,
  157. self.scopelen)
  158. def to_wire(self, file):
  159. file.write(struct.pack('!H', self.family))
  160. file.write(struct.pack('!BB', self.srclen, self.scopelen))
  161. file.write(self.addrdata)
  162. @classmethod
  163. def from_wire(cls, otype, wire, cur, olen):
  164. family, src, scope = struct.unpack('!HBB', wire[cur:cur+4])
  165. cur += 4
  166. addrlen = int(math.ceil(src/8.0))
  167. if family == 1:
  168. af = dns.inet.AF_INET
  169. pad = 4 - addrlen
  170. elif family == 2:
  171. af = dns.inet.AF_INET6
  172. pad = 16 - addrlen
  173. else:
  174. raise ValueError('unsupported family')
  175. addr = dns.inet.inet_ntop(af, wire[cur:cur+addrlen] + b'\x00' * pad)
  176. return cls(addr, src, scope)
  177. def _cmp(self, other):
  178. if self.addrdata == other.addrdata:
  179. return 0
  180. if self.addrdata > other.addrdata:
  181. return 1
  182. return -1
  183. _type_to_class = {
  184. ECS: ECSOption
  185. }
  186. def get_option_class(otype):
  187. """Return the class for the specified option type.
  188. The GenericOption class is used if a more specific class is not
  189. known.
  190. """
  191. cls = _type_to_class.get(otype)
  192. if cls is None:
  193. cls = GenericOption
  194. return cls
  195. def option_from_wire(otype, wire, current, olen):
  196. """Build an EDNS option object from wire format.
  197. *otype*, an ``int``, is the option type.
  198. *wire*, a ``binary``, is the wire-format message.
  199. *current*, an ``int``, is the offset in *wire* of the beginning
  200. of the rdata.
  201. *olen*, an ``int``, is the length of the wire-format option data
  202. Returns an instance of a subclass of ``dns.edns.Option``.
  203. """
  204. cls = get_option_class(otype)
  205. return cls.from_wire(otype, wire, current, olen)