wire.py 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
  2. import contextlib
  3. import struct
  4. import dns.exception
  5. import dns.name
  6. class Parser:
  7. def __init__(self, wire, current=0):
  8. self.wire = wire
  9. self.current = 0
  10. self.end = len(self.wire)
  11. if current:
  12. self.seek(current)
  13. self.furthest = current
  14. def remaining(self):
  15. return self.end - self.current
  16. def get_bytes(self, size):
  17. if size > self.remaining():
  18. raise dns.exception.FormError
  19. output = self.wire[self.current:self.current + size]
  20. self.current += size
  21. self.furthest = max(self.furthest, self.current)
  22. return output
  23. def get_counted_bytes(self, length_size=1):
  24. length = int.from_bytes(self.get_bytes(length_size), 'big')
  25. return self.get_bytes(length)
  26. def get_remaining(self):
  27. return self.get_bytes(self.remaining())
  28. def get_uint8(self):
  29. return struct.unpack('!B', self.get_bytes(1))[0]
  30. def get_uint16(self):
  31. return struct.unpack('!H', self.get_bytes(2))[0]
  32. def get_uint32(self):
  33. return struct.unpack('!I', self.get_bytes(4))[0]
  34. def get_uint48(self):
  35. return int.from_bytes(self.get_bytes(6), 'big')
  36. def get_struct(self, format):
  37. return struct.unpack(format, self.get_bytes(struct.calcsize(format)))
  38. def get_name(self, origin=None):
  39. name = dns.name.from_wire_parser(self)
  40. if origin:
  41. name = name.relativize(origin)
  42. return name
  43. def seek(self, where):
  44. # Note that seeking to the end is OK! (If you try to read
  45. # after such a seek, you'll get an exception as expected.)
  46. if where < 0 or where > self.end:
  47. raise dns.exception.FormError
  48. self.current = where
  49. @contextlib.contextmanager
  50. def restrict_to(self, size):
  51. if size > self.remaining():
  52. raise dns.exception.FormError
  53. saved_end = self.end
  54. try:
  55. self.end = self.current + size
  56. yield
  57. # We make this check here and not in the finally as we
  58. # don't want to raise if we're already raising for some
  59. # other reason.
  60. if self.current != self.end:
  61. raise dns.exception.FormError
  62. finally:
  63. self.end = saved_end
  64. @contextlib.contextmanager
  65. def restore_furthest(self):
  66. try:
  67. yield None
  68. finally:
  69. self.current = self.furthest