--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/VirtualMailManager/network.py Thu Jun 28 19:26:50 2012 +0000
@@ -0,0 +1,100 @@
+# -*- coding: UTF-8 -*-
+# Copyright (c) 2011 - 2012, Pascal Volk
+# See COPYING for distribution information.
+"""
+ VirtualMailManager.network
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Network/IP address related class and function
+"""
+
+import socket
+
+
+class NetInfo(object):
+ """Simple class for CIDR network addresses an IP addresses."""
+ __slots__ = ('_addr', '_prefix', '_bits_max', '_family', '_nw_addr')
+
+ def __init__(self, nw_address):
+ """Creates a new `NetInfo` instance.
+
+ Argument:
+
+ `nw_address` : basestring
+ string representation of an IPv4/IPv6 address or network address.
+ E.g. 192.0.2.13, 192.0.2.0/24, 2001:db8::/32 or ::1
+ When the address has no netmask the prefix length will be set to
+ 32 for IPv4 addresses and 128 for IPv6 addresses.
+ """
+ self._addr = None
+ self._prefix = 0
+ self._bits_max = 0
+ self._family = 0
+ self._nw_addr = nw_address
+ self._parse_net_range()
+
+ def __hash__(self):
+ return hash((self._addr, self._family, self._prefix))
+
+ def __repr__(self):
+ return "NetInfo('%s')" % self._nw_addr
+
+ def _parse_net_range(self):
+ """Parse the network range of `self._nw_addr and assign values
+ to the class attributes.
+ `"""
+ sep = '/'
+ if self._nw_addr.count(sep):
+ ip_address, sep, self._prefix = self._nw_addr.partition(sep)
+ self._family, self._addr = get_ip_addr_info(ip_address)
+ else:
+ self._family, self._addr = get_ip_addr_info(self._nw_addr)
+ self._bits_max = (128, 32)[self._family is socket.AF_INET]
+ if self._prefix is 0:
+ self._prefix = self._bits_max
+ else:
+ try:
+ self._prefix = int(self._prefix)
+ except ValueError:
+ raise ValueError('Invalid prefix length: %r' % self._prefix)
+ if self._prefix > self._bits_max or self._prefix < 0:
+ raise ValueError('Invalid prefix length: %r' % self._prefix)
+
+ @property
+ def family(self):
+ """Address family: `socket.AF_INET` or `socket.AF_INET6`"""
+ return self._family
+
+ def address_in_net(self, ip_address):
+ """Checks if the `ip_address` belongs to the same subnet."""
+ family, address = get_ip_addr_info(ip_address)
+ if family != self._family:
+ return False
+ return address >> self._bits_max - self._prefix == \
+ self._addr >> self._bits_max - self._prefix
+
+
+def get_ip_addr_info(ip_address):
+ """Checks if the string `ip_address` is a valid IPv4 or IPv6 address.
+
+ When the `ip_address` could be validated successfully a tuple
+ `(address_family, address_as_long)` will be returned. The
+ `address_family`will be either `socket.AF_INET` or `socket.AF_INET6`.
+ """
+ if not isinstance(ip_address, basestring) or not ip_address:
+ raise TypeError('ip_address must be a non empty string.')
+ if not ip_address.count(':'):
+ family = socket.AF_INET
+ try:
+ address = socket.inet_aton(ip_address)
+ except socket.error:
+ raise ValueError('Not a valid IPv4 address: %r' % ip_address)
+ elif not socket.has_ipv6:
+ raise ValueError('Unsupported IP address (IPv6): %r' % ip_address)
+ else:
+ family = socket.AF_INET6
+ try:
+ address = socket.inet_pton(family, ip_address)
+ except socket.error:
+ raise ValueError('Not a valid IPv6 address: %r' % ip_address)
+ return (family, long(address.encode('hex'), 16))