edns.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464
  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. import math
  18. import socket
  19. import struct
  20. import dns.enum
  21. import dns.inet
  22. import dns.rdata
  23. class OptionType(dns.enum.IntEnum):
  24. #: NSID
  25. NSID = 3
  26. #: DAU
  27. DAU = 5
  28. #: DHU
  29. DHU = 6
  30. #: N3U
  31. N3U = 7
  32. #: ECS (client-subnet)
  33. ECS = 8
  34. #: EXPIRE
  35. EXPIRE = 9
  36. #: COOKIE
  37. COOKIE = 10
  38. #: KEEPALIVE
  39. KEEPALIVE = 11
  40. #: PADDING
  41. PADDING = 12
  42. #: CHAIN
  43. CHAIN = 13
  44. #: EDE (extended-dns-error)
  45. EDE = 15
  46. @classmethod
  47. def _maximum(cls):
  48. return 65535
  49. class Option:
  50. """Base class for all EDNS option types."""
  51. def __init__(self, otype):
  52. """Initialize an option.
  53. *otype*, an ``int``, is the option type.
  54. """
  55. self.otype = OptionType.make(otype)
  56. def to_wire(self, file=None):
  57. """Convert an option to wire format.
  58. Returns a ``bytes`` or ``None``.
  59. """
  60. raise NotImplementedError # pragma: no cover
  61. @classmethod
  62. def from_wire_parser(cls, otype, parser):
  63. """Build an EDNS option object from wire format.
  64. *otype*, an ``int``, is the option type.
  65. *parser*, a ``dns.wire.Parser``, the parser, which should be
  66. restructed to the option length.
  67. Returns a ``dns.edns.Option``.
  68. """
  69. raise NotImplementedError # pragma: no cover
  70. def _cmp(self, other):
  71. """Compare an EDNS option with another option of the same type.
  72. Returns < 0 if < *other*, 0 if == *other*, and > 0 if > *other*.
  73. """
  74. wire = self.to_wire()
  75. owire = other.to_wire()
  76. if wire == owire:
  77. return 0
  78. if wire > owire:
  79. return 1
  80. return -1
  81. def __eq__(self, other):
  82. if not isinstance(other, Option):
  83. return False
  84. if self.otype != other.otype:
  85. return False
  86. return self._cmp(other) == 0
  87. def __ne__(self, other):
  88. if not isinstance(other, Option):
  89. return True
  90. if self.otype != other.otype:
  91. return True
  92. return self._cmp(other) != 0
  93. def __lt__(self, other):
  94. if not isinstance(other, Option) or \
  95. self.otype != other.otype:
  96. return NotImplemented
  97. return self._cmp(other) < 0
  98. def __le__(self, other):
  99. if not isinstance(other, Option) or \
  100. self.otype != other.otype:
  101. return NotImplemented
  102. return self._cmp(other) <= 0
  103. def __ge__(self, other):
  104. if not isinstance(other, Option) or \
  105. self.otype != other.otype:
  106. return NotImplemented
  107. return self._cmp(other) >= 0
  108. def __gt__(self, other):
  109. if not isinstance(other, Option) or \
  110. self.otype != other.otype:
  111. return NotImplemented
  112. return self._cmp(other) > 0
  113. def __str__(self):
  114. return self.to_text()
  115. class GenericOption(Option):
  116. """Generic Option Class
  117. This class is used for EDNS option types for which we have no better
  118. implementation.
  119. """
  120. def __init__(self, otype, data):
  121. super().__init__(otype)
  122. self.data = dns.rdata.Rdata._as_bytes(data, True)
  123. def to_wire(self, file=None):
  124. if file:
  125. file.write(self.data)
  126. else:
  127. return self.data
  128. def to_text(self):
  129. return "Generic %d" % self.otype
  130. @classmethod
  131. def from_wire_parser(cls, otype, parser):
  132. return cls(otype, parser.get_remaining())
  133. class ECSOption(Option):
  134. """EDNS Client Subnet (ECS, RFC7871)"""
  135. def __init__(self, address, srclen=None, scopelen=0):
  136. """*address*, a ``str``, is the client address information.
  137. *srclen*, an ``int``, the source prefix length, which is the
  138. leftmost number of bits of the address to be used for the
  139. lookup. The default is 24 for IPv4 and 56 for IPv6.
  140. *scopelen*, an ``int``, the scope prefix length. This value
  141. must be 0 in queries, and should be set in responses.
  142. """
  143. super().__init__(OptionType.ECS)
  144. af = dns.inet.af_for_address(address)
  145. if af == socket.AF_INET6:
  146. self.family = 2
  147. if srclen is None:
  148. srclen = 56
  149. address = dns.rdata.Rdata._as_ipv6_address(address)
  150. srclen = dns.rdata.Rdata._as_int(srclen, 0, 128)
  151. scopelen = dns.rdata.Rdata._as_int(scopelen, 0, 128)
  152. elif af == socket.AF_INET:
  153. self.family = 1
  154. if srclen is None:
  155. srclen = 24
  156. address = dns.rdata.Rdata._as_ipv4_address(address)
  157. srclen = dns.rdata.Rdata._as_int(srclen, 0, 32)
  158. scopelen = dns.rdata.Rdata._as_int(scopelen, 0, 32)
  159. else: # pragma: no cover (this will never happen)
  160. raise ValueError('Bad address family')
  161. self.address = address
  162. self.srclen = srclen
  163. self.scopelen = scopelen
  164. addrdata = dns.inet.inet_pton(af, address)
  165. nbytes = int(math.ceil(srclen / 8.0))
  166. # Truncate to srclen and pad to the end of the last octet needed
  167. # See RFC section 6
  168. self.addrdata = addrdata[:nbytes]
  169. nbits = srclen % 8
  170. if nbits != 0:
  171. last = struct.pack('B',
  172. ord(self.addrdata[-1:]) & (0xff << (8 - nbits)))
  173. self.addrdata = self.addrdata[:-1] + last
  174. def to_text(self):
  175. return "ECS {}/{} scope/{}".format(self.address, self.srclen,
  176. self.scopelen)
  177. @staticmethod
  178. def from_text(text):
  179. """Convert a string into a `dns.edns.ECSOption`
  180. *text*, a `str`, the text form of the option.
  181. Returns a `dns.edns.ECSOption`.
  182. Examples:
  183. >>> import dns.edns
  184. >>>
  185. >>> # basic example
  186. >>> dns.edns.ECSOption.from_text('1.2.3.4/24')
  187. >>>
  188. >>> # also understands scope
  189. >>> dns.edns.ECSOption.from_text('1.2.3.4/24/32')
  190. >>>
  191. >>> # IPv6
  192. >>> dns.edns.ECSOption.from_text('2001:4b98::1/64/64')
  193. >>>
  194. >>> # it understands results from `dns.edns.ECSOption.to_text()`
  195. >>> dns.edns.ECSOption.from_text('ECS 1.2.3.4/24/32')
  196. """
  197. optional_prefix = 'ECS'
  198. tokens = text.split()
  199. ecs_text = None
  200. if len(tokens) == 1:
  201. ecs_text = tokens[0]
  202. elif len(tokens) == 2:
  203. if tokens[0] != optional_prefix:
  204. raise ValueError('could not parse ECS from "{}"'.format(text))
  205. ecs_text = tokens[1]
  206. else:
  207. raise ValueError('could not parse ECS from "{}"'.format(text))
  208. n_slashes = ecs_text.count('/')
  209. if n_slashes == 1:
  210. address, srclen = ecs_text.split('/')
  211. scope = 0
  212. elif n_slashes == 2:
  213. address, srclen, scope = ecs_text.split('/')
  214. else:
  215. raise ValueError('could not parse ECS from "{}"'.format(text))
  216. try:
  217. scope = int(scope)
  218. except ValueError:
  219. raise ValueError('invalid scope ' +
  220. '"{}": scope must be an integer'.format(scope))
  221. try:
  222. srclen = int(srclen)
  223. except ValueError:
  224. raise ValueError('invalid srclen ' +
  225. '"{}": srclen must be an integer'.format(srclen))
  226. return ECSOption(address, srclen, scope)
  227. def to_wire(self, file=None):
  228. value = (struct.pack('!HBB', self.family, self.srclen, self.scopelen) +
  229. self.addrdata)
  230. if file:
  231. file.write(value)
  232. else:
  233. return value
  234. @classmethod
  235. def from_wire_parser(cls, otype, parser):
  236. family, src, scope = parser.get_struct('!HBB')
  237. addrlen = int(math.ceil(src / 8.0))
  238. prefix = parser.get_bytes(addrlen)
  239. if family == 1:
  240. pad = 4 - addrlen
  241. addr = dns.ipv4.inet_ntoa(prefix + b'\x00' * pad)
  242. elif family == 2:
  243. pad = 16 - addrlen
  244. addr = dns.ipv6.inet_ntoa(prefix + b'\x00' * pad)
  245. else:
  246. raise ValueError('unsupported family')
  247. return cls(addr, src, scope)
  248. class EDECode(dns.enum.IntEnum):
  249. OTHER = 0
  250. UNSUPPORTED_DNSKEY_ALGORITHM = 1
  251. UNSUPPORTED_DS_DIGEST_TYPE = 2
  252. STALE_ANSWER = 3
  253. FORGED_ANSWER = 4
  254. DNSSEC_INDETERMINATE = 5
  255. DNSSEC_BOGUS = 6
  256. SIGNATURE_EXPIRED = 7
  257. SIGNATURE_NOT_YET_VALID = 8
  258. DNSKEY_MISSING = 9
  259. RRSIGS_MISSING = 10
  260. NO_ZONE_KEY_BIT_SET = 11
  261. NSEC_MISSING = 12
  262. CACHED_ERROR = 13
  263. NOT_READY = 14
  264. BLOCKED = 15
  265. CENSORED = 16
  266. FILTERED = 17
  267. PROHIBITED = 18
  268. STALE_NXDOMAIN_ANSWER = 19
  269. NOT_AUTHORITATIVE = 20
  270. NOT_SUPPORTED = 21
  271. NO_REACHABLE_AUTHORITY = 22
  272. NETWORK_ERROR = 23
  273. INVALID_DATA = 24
  274. @classmethod
  275. def _maximum(cls):
  276. return 65535
  277. class EDEOption(Option):
  278. """Extended DNS Error (EDE, RFC8914)"""
  279. def __init__(self, code, text=None):
  280. """*code*, a ``dns.edns.EDECode`` or ``str``, the info code of the
  281. extended error.
  282. *text*, a ``str`` or ``None``, specifying additional information about
  283. the error.
  284. """
  285. super().__init__(OptionType.EDE)
  286. self.code = EDECode.make(code)
  287. if text is not None and not isinstance(text, str):
  288. raise ValueError('text must be string or None')
  289. self.code = code
  290. self.text = text
  291. def to_text(self):
  292. output = f'EDE {self.code}'
  293. if self.text is not None:
  294. output += f': {self.text}'
  295. return output
  296. def to_wire(self, file=None):
  297. value = struct.pack('!H', self.code)
  298. if self.text is not None:
  299. value += self.text.encode('utf8')
  300. if file:
  301. file.write(value)
  302. else:
  303. return value
  304. @classmethod
  305. def from_wire_parser(cls, otype, parser):
  306. code = parser.get_uint16()
  307. text = parser.get_remaining()
  308. if text:
  309. if text[-1] == 0: # text MAY be null-terminated
  310. text = text[:-1]
  311. text = text.decode('utf8')
  312. else:
  313. text = None
  314. return cls(code, text)
  315. _type_to_class = {
  316. OptionType.ECS: ECSOption,
  317. OptionType.EDE: EDEOption,
  318. }
  319. def get_option_class(otype):
  320. """Return the class for the specified option type.
  321. The GenericOption class is used if a more specific class is not
  322. known.
  323. """
  324. cls = _type_to_class.get(otype)
  325. if cls is None:
  326. cls = GenericOption
  327. return cls
  328. def option_from_wire_parser(otype, parser):
  329. """Build an EDNS option object from wire format.
  330. *otype*, an ``int``, is the option type.
  331. *parser*, a ``dns.wire.Parser``, the parser, which should be
  332. restricted to the option length.
  333. Returns an instance of a subclass of ``dns.edns.Option``.
  334. """
  335. cls = get_option_class(otype)
  336. otype = OptionType.make(otype)
  337. return cls.from_wire_parser(otype, parser)
  338. def option_from_wire(otype, wire, current, olen):
  339. """Build an EDNS option object from wire format.
  340. *otype*, an ``int``, is the option type.
  341. *wire*, a ``bytes``, is the wire-format message.
  342. *current*, an ``int``, is the offset in *wire* of the beginning
  343. of the rdata.
  344. *olen*, an ``int``, is the length of the wire-format option data
  345. Returns an instance of a subclass of ``dns.edns.Option``.
  346. """
  347. parser = dns.wire.Parser(wire, current)
  348. with parser.restrict_to(olen):
  349. return option_from_wire_parser(otype, parser)
  350. def register_type(implementation, otype):
  351. """Register the implementation of an option type.
  352. *implementation*, a ``class``, is a subclass of ``dns.edns.Option``.
  353. *otype*, an ``int``, is the option type.
  354. """
  355. _type_to_class[otype] = implementation
  356. ### BEGIN generated OptionType constants
  357. NSID = OptionType.NSID
  358. DAU = OptionType.DAU
  359. DHU = OptionType.DHU
  360. N3U = OptionType.N3U
  361. ECS = OptionType.ECS
  362. EXPIRE = OptionType.EXPIRE
  363. COOKIE = OptionType.COOKIE
  364. KEEPALIVE = OptionType.KEEPALIVE
  365. PADDING = OptionType.PADDING
  366. CHAIN = OptionType.CHAIN
  367. EDE = OptionType.EDE
  368. ### END generated OptionType constants