wiredata.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. # Copyright (C) 2011 Nominum, Inc.
  2. #
  3. # Permission to use, copy, modify, and distribute this software and its
  4. # documentation for any purpose with or without fee is hereby granted,
  5. # provided that the above copyright notice and this permission notice
  6. # appear in all copies.
  7. #
  8. # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
  9. # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  10. # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
  11. # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  12. # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  13. # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
  14. # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15. """DNS Wire Data Helper"""
  16. import sys
  17. import dns.exception
  18. from ._compat import binary_type, string_types
  19. # Figure out what constant python passes for an unspecified slice bound.
  20. # It's supposed to be sys.maxint, yet on 64-bit windows sys.maxint is 2^31 - 1
  21. # but Python uses 2^63 - 1 as the constant. Rather than making pointless
  22. # extra comparisons, duplicating code, or weakening WireData, we just figure
  23. # out what constant Python will use.
  24. class _SliceUnspecifiedBound(binary_type):
  25. def __getitem__(self, key):
  26. return key.stop
  27. if sys.version_info < (3,):
  28. def __getslice__(self, i, j): # pylint: disable=getslice-method
  29. return self.__getitem__(slice(i, j))
  30. _unspecified_bound = _SliceUnspecifiedBound()[1:]
  31. class WireData(binary_type):
  32. # WireData is a string with stricter slicing
  33. def __getitem__(self, key):
  34. try:
  35. if isinstance(key, slice):
  36. # make sure we are not going outside of valid ranges,
  37. # do stricter control of boundaries than python does
  38. # by default
  39. start = key.start
  40. stop = key.stop
  41. if sys.version_info < (3,):
  42. if stop == _unspecified_bound:
  43. # handle the case where the right bound is unspecified
  44. stop = len(self)
  45. if start < 0 or stop < 0:
  46. raise dns.exception.FormError
  47. # If it's not an empty slice, access left and right bounds
  48. # to make sure they're valid
  49. if start != stop:
  50. super(WireData, self).__getitem__(start)
  51. super(WireData, self).__getitem__(stop - 1)
  52. else:
  53. for index in (start, stop):
  54. if index is None:
  55. continue
  56. elif abs(index) > len(self):
  57. raise dns.exception.FormError
  58. return WireData(super(WireData, self).__getitem__(
  59. slice(start, stop)))
  60. return bytearray(self.unwrap())[key]
  61. except IndexError:
  62. raise dns.exception.FormError
  63. if sys.version_info < (3,):
  64. def __getslice__(self, i, j): # pylint: disable=getslice-method
  65. return self.__getitem__(slice(i, j))
  66. def __iter__(self):
  67. i = 0
  68. while 1:
  69. try:
  70. yield self[i]
  71. i += 1
  72. except dns.exception.FormError:
  73. raise StopIteration
  74. def unwrap(self):
  75. return binary_type(self)
  76. def maybe_wrap(wire):
  77. if isinstance(wire, WireData):
  78. return wire
  79. elif isinstance(wire, binary_type):
  80. return WireData(wire)
  81. elif isinstance(wire, string_types):
  82. return WireData(wire.encode())
  83. raise ValueError("unhandled type %s" % type(wire))