IPv4.py 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. import re
  2. class IPAddress:
  3. # a number between 0 and 255, no leading zeros
  4. _IP_NUMBER_REGEXP = r"(25[0-5]|2[0-4]\d|1?[1-9]?\d)"
  5. # 4 numbers between 0 and 255, joined together with dots
  6. IP_REGEXP = r"{0}\.{0}\.{0}\.{0}".format(_IP_NUMBER_REGEXP)
  7. def __init__(self, intlist):
  8. if not isinstance(intlist, list) or not all(isinstance(n, int) for n in intlist):
  9. raise TypeError("The first constructor argument must be an list of ints")
  10. if not len(intlist) == 4 or not all(0 <= n <= 255 for n in intlist):
  11. raise ValueError("The integer list must contain 4 ints in range of 0 and 255, like an ip-address")
  12. self.ipnum = int.from_bytes(bytes(intlist), "big")
  13. @staticmethod
  14. def parse(ip: str):
  15. match = re.match("^" + IPAddress.IP_REGEXP + "$", ip)
  16. if not match:
  17. raise ValueError("%s is no ipv4-address" % ip)
  18. numbers = [int(match.group(i)) for i in range(1, 5)]
  19. return IPAddress(numbers)
  20. @staticmethod
  21. def from_int(numeric: int):
  22. if numeric not in range(1 << 32):
  23. raise ValueError("numeric value must be in uint-range")
  24. return IPAddress(list(numeric.to_bytes(4, "big")))
  25. def to_int(self):
  26. return self.ipnum
  27. def is_private(self):
  28. return ReservedIPBlocks.is_private(self)
  29. def is_localhost(self):
  30. return ReservedIPBlocks.is_localhost(self)
  31. def is_multicast(self):
  32. return ReservedIPBlocks.is_multicast(self)
  33. def is_reserved(self):
  34. return ReservedIPBlocks.is_reserved(self)
  35. def is_zero_conf(self):
  36. return ReservedIPBlocks.is_zero_conf(self)
  37. def _tuple(self):
  38. return tuple(self.ipnum.to_bytes(4, "big"))
  39. def __repr__(self):
  40. return "IPAddress([%i, %i, %i, %i])" % self._tuple()
  41. def __str__(self):
  42. return "%i.%i.%i.%i" % self._tuple()
  43. def __hash__(self):
  44. return self.ipnum
  45. def __eq__(self, other):
  46. if other is None:
  47. return False
  48. return isinstance(other, IPAddress) and self.ipnum == other.ipnum
  49. def __lt__(self, other):
  50. if other is None:
  51. raise TypeError("Cannot compare to None")
  52. if not isinstance(other, IPAddress):
  53. raise NotImplemented # maybe other can compare to self
  54. return self.ipnum < other.ipnum
  55. class IPAddressBlock:
  56. CIDR_REGEXP = IPAddress.IP_REGEXP + r"(\/(3[0-2]|[12]?\d)|)?"
  57. def __init__(self, ip, netmask = 32):
  58. if isinstance(ip, str):
  59. ip = IPAddress.parse(ip)
  60. elif isinstance(ip, list):
  61. ip = IPAddress(ip)
  62. if not 1 <= netmask <= 32:
  63. raise ValueError("netmask must lie between 1 and 32")
  64. self.ipnum = ip.to_int() & self._bitmask(netmask)
  65. self.netmask = netmask
  66. @staticmethod
  67. def parse(cidr: str):
  68. match = re.match("^" + IPAddressBlock.CIDR_REGEXP + "$", cidr)
  69. if not match:
  70. raise ValueError("%s is no valid cidr-notation" % cidr)
  71. ip = [int(match.group(i)) for i in range(1, 5)]
  72. suffix = 32 if not match.group(6) else int(match.group(6))
  73. return IPAddressBlock(ip, suffix)
  74. def _bitmask(self, netmask):
  75. ones = lambda x: (1 << x) - 1
  76. return ones(32) ^ ones(32 - netmask)
  77. def __repr__(self):
  78. return "IPAddressBlock(%s, %i)" % (repr(IPAddress.from_int(self.ipnum)), self.netmask)
  79. def __self__(self):
  80. return IPAddress.from_int(self.ipnum) + "/" + str(self.netmask)
  81. def __contains__(self, ip):
  82. return (ip.to_int() & self._bitmask(self.netmask)) == self.ipnum
  83. class ReservedIPBlocks:
  84. PRIVATE_IP_SEGMENTS = [
  85. IPAddressBlock.parse(block)
  86. for block in
  87. ("10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16")
  88. ]
  89. LOCALHOST_SEGMENT = IPAddressBlock.parse("127.0.0.0/8")
  90. MULTICAST_SEGMENT = IPAddressBlock.parse("224.0.0.0/4")
  91. RESERVED_SEGMENT = IPAddressBlock.parse("240.0.0.0/4")
  92. ZERO_CONF_SEGMENT = IPAddressBlock.parse("169.254.0.0/16")
  93. @staticmethod
  94. def is_private(ip):
  95. return any(ip in block for block in ReservedIPBlocks.PRIVATE_IP_SEGMENTS)
  96. @staticmethod
  97. def is_localhost(ip):
  98. return ip in ReservedIPBlocks.LOCALHOST_SEGMENT
  99. @staticmethod
  100. def is_multicast(ip):
  101. return ip in ReservedIPBlocks.MULTICAST_SEGMENT
  102. @staticmethod
  103. def is_reserved(ip):
  104. return ip in ReservedIPBlocks.RESERVED_SEGMENT
  105. @staticmethod
  106. def is_zero_conf(ip):
  107. return ip in ReservedIPBlocks.ZERO_CONF_SEGMENT