| @@ -27,7 +27,8 @@ from socket import AF_INET, AF_INET6 | |||||
| from earwigbot.commands import Command | from earwigbot.commands import Command | ||||
| _Range = namedtuple("_Range", ["family", "range", "size", "addresses"]) | |||||
| _Range = namedtuple("_Range", [ | |||||
| "family", "range", "low", "high", "size", "addresses"]) | |||||
| class CIDR(Command): | class CIDR(Command): | ||||
| """Calculates the smallest CIDR range that encompasses a list of IP | """Calculates the smallest CIDR range that encompasses a list of IP | ||||
| @@ -62,9 +63,10 @@ class CIDR(Command): | |||||
| cidr = self._calculate_range(ips[0][0], [ip[1] for ip in ips]) | cidr = self._calculate_range(ips[0][0], [ip[1] for ip in ips]) | ||||
| descr = self._describe(cidr.family, cidr.size) | descr = self._describe(cidr.family, cidr.size) | ||||
| msg = "Smallest CIDR range is \x02{0}\x0F ({1}){2}" | |||||
| msg = "Smallest CIDR range is \x02{0}\x0F ({1}: {2} – {3}){4}." | |||||
| self.reply(data, msg.format( | self.reply(data, msg.format( | ||||
| cidr.range, cidr.addresses, " – " + descr if descr else "")) | |||||
| cidr.range, cidr.addresses, cidr.low, cidr.high, | |||||
| "; " + descr if descr else "")) | |||||
| def _parse_arg(self, arg): | def _parse_arg(self, arg): | ||||
| """Converts an argument into an IP address.""" | """Converts an argument into an IP address.""" | ||||
| @@ -110,23 +112,30 @@ class CIDR(Command): | |||||
| size = i | size = i | ||||
| break | break | ||||
| mask = bin_ips[0][:size].ljust(len(bin_ips[0]), "0") | |||||
| packed = "".join( | |||||
| chr(int(mask[i:i + 8], 2)) for i in xrange(0, len(mask), 8)) | |||||
| bin_low = bin_ips[0][:size].ljust(len(bin_ips[0]), "0") | |||||
| bin_high = bin_ips[0][:size].ljust(len(bin_ips[0]), "1") | |||||
| low = self._format_bin(family, bin_low) | |||||
| high = self._format_bin(family, bin_high) | |||||
| return _Range( | return _Range( | ||||
| family, | |||||
| socket.inet_ntop(family, packed) + "/" + str(size), | |||||
| size, | |||||
| family, low + "/" + str(size), low, high, size, | |||||
| self._format_count(2 ** (len(bin_ips[0]) - size))) | self._format_count(2 ** (len(bin_ips[0]) - size))) | ||||
| def _format_count(self, count): | |||||
| @staticmethod | |||||
| def _format_bin(family, binary): | |||||
| """Convert an IP's binary representation to presentation format.""" | |||||
| return socket.inet_ntop(family, "".join( | |||||
| chr(int(binary[i:i + 8], 2)) for i in xrange(0, len(binary), 8))) | |||||
| @staticmethod | |||||
| def _format_count(count): | |||||
| """Nicely format a number of addresses affected by a range block.""" | """Nicely format a number of addresses affected by a range block.""" | ||||
| if count > 2 ** 32: | if count > 2 ** 32: | ||||
| base = "{0:.2E} addresses".format(count) | base = "{0:.2E} addresses".format(count) | ||||
| if count > 2 ** 96: | if count > 2 ** 96: | ||||
| return base + ", {0:.2E} /64 subnets".format(count >> 64) | return base + ", {0:.2E} /64 subnets".format(count >> 64) | ||||
| if count > 2 ** 63: | if count > 2 ** 63: | ||||
| return base + ", {0} /64 subnets".format(count >> 64) | |||||
| return base + ", {0:,} /64 subnets".format(count >> 64) | |||||
| return base | return base | ||||
| if count == 1: | if count == 1: | ||||
| return "1 address" | return "1 address" | ||||