VirtualMailManager/network.py
author Pascal Volk <user@localhost.localdomain.org>
Thu, 28 Jun 2012 18:33:32 +0000
branchv0.6.x
changeset 568 14abdd04ddf5
parent 421 ff2a61e155db
child 643 df1e3b67882a
child 675 d24f094d1cb5
permissions -rw-r--r--
Updated copyright notices to include the year 2012.
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
421
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
     1
# -*- coding: UTF-8 -*-
568
14abdd04ddf5 Updated copyright notices to include the year 2012.
Pascal Volk <user@localhost.localdomain.org>
parents: 421
diff changeset
     2
# Copyright (c) 2011 - 2012, Pascal Volk
421
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
     3
# See COPYING for distribution information.
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
     4
"""
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
     5
    VirtualMailManager.network
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
     6
    ~~~~~~~~~~~~~~~~~~~~~~~~~~
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
     7
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
     8
    Network/IP address related class and function
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
     9
"""
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    10
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    11
import socket
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    12
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    13
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    14
class NetInfo(object):
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    15
    """Simple class for CIDR network addresses an IP addresses."""
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    16
    __slots__ = ('_addr', '_prefix', '_bits_max', '_family', '_nw_addr')
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    17
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    18
    def __init__(self, nw_address):
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    19
        """Creates a new `NetInfo` instance.
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    20
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    21
        Argument:
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    22
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    23
        `nw_address` : basestring
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    24
          string representation of an IPv4/IPv6 address or network address.
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    25
          E.g. 192.0.2.13, 192.0.2.0/24, 2001:db8::/32 or ::1
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    26
          When the address has no netmask the prefix length will be set to
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    27
          32 for IPv4 addresses and 128 for IPv6 addresses.
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    28
        """
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    29
        self._addr = None
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    30
        self._prefix = 0
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    31
        self._bits_max = 0
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    32
        self._family = 0
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    33
        self._nw_addr = nw_address
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    34
        self._parse_net_range()
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    35
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    36
    def __hash__(self):
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    37
        return hash((self._addr, self._family, self._prefix))
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    38
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    39
    def __repr__(self):
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    40
        return "NetInfo('%s')" % self._nw_addr
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    41
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    42
    def _parse_net_range(self):
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    43
        """Parse the network range of `self._nw_addr and assign values
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    44
        to the class attributes.
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    45
        `"""
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    46
        sep = '/'
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    47
        if self._nw_addr.count(sep):
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    48
            ip_address, sep, self._prefix = self._nw_addr.partition(sep)
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    49
            self._family, self._addr = get_ip_addr_info(ip_address)
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    50
        else:
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    51
            self._family, self._addr = get_ip_addr_info(self._nw_addr)
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    52
        self._bits_max = (128, 32)[self._family is socket.AF_INET]
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    53
        if self._prefix is 0:
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    54
            self._prefix = self._bits_max
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    55
        else:
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    56
            try:
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    57
                self._prefix = int(self._prefix)
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    58
            except ValueError:
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    59
                raise ValueError('Invalid prefix length: %r' % self._prefix)
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    60
        if self._prefix > self._bits_max or self._prefix < 0:
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    61
            raise ValueError('Invalid prefix length: %r' % self._prefix)
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    62
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    63
    @property
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    64
    def family(self):
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    65
        """Address family: `socket.AF_INET` or `socket.AF_INET6`"""
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    66
        return self._family
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    67
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    68
    def address_in_net(self, ip_address):
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    69
        """Checks if the `ip_address` belongs to the same subnet."""
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    70
        family, address = get_ip_addr_info(ip_address)
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    71
        if family != self._family:
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    72
            return False
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    73
        return address >> self._bits_max - self._prefix == \
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    74
               self._addr >> self._bits_max - self._prefix
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    75
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    76
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    77
def get_ip_addr_info(ip_address):
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    78
    """Checks if the string `ip_address` is a valid IPv4 or IPv6 address.
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    79
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    80
    When the `ip_address` could be validated successfully a tuple
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    81
    `(address_family, address_as_long)` will be returned. The
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    82
    `address_family`will be either `socket.AF_INET` or `socket.AF_INET6`.
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    83
    """
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    84
    if not isinstance(ip_address, basestring) or not ip_address:
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    85
        raise TypeError('ip_address must be a non empty string.')
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    86
    if not ip_address.count(':'):
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    87
        family = socket.AF_INET
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    88
        try:
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    89
            address = socket.inet_aton(ip_address)
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    90
        except socket.error:
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    91
            raise ValueError('Not a valid IPv4 address: %r' % ip_address)
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    92
    elif not socket.has_ipv6:
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    93
        raise ValueError('Unsupported IP address (IPv6): %r' % ip_address)
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    94
    else:
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    95
        family = socket.AF_INET6
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    96
        try:
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    97
            address = socket.inet_pton(family, ip_address)
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    98
        except socket.error:
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
    99
            raise ValueError('Not a valid IPv6 address: %r' % ip_address)
ff2a61e155db VMM/network: Added new module network.
Pascal Volk <neverseen@users.sourceforge.net>
parents:
diff changeset
   100
    return (family, long(address.encode('hex'), 16))