Source code for netaddr.strategy.ipv6

# -----------------------------------------------------------------------------
#   Copyright (c) 2008 by David P. D. Moss. All rights reserved.
#
#   Released under the BSD license. See the LICENSE file for details.
# -----------------------------------------------------------------------------
"""
IPv6 address logic.
"""
import struct as _struct

OPT_IMPORTS = False

#   Check whether we need to use fallback code or not.
try:
    import socket as _socket

    #   These might all generate exceptions on different platforms.
    if not _socket.has_ipv6:
        raise Exception('IPv6 disabled')
    _socket.inet_pton
    _socket.AF_INET6
    from _socket import inet_pton as _inet_pton, inet_ntop as _inet_ntop, AF_INET6

    OPT_IMPORTS = True
except Exception:
    from netaddr.fbsocket import inet_pton as _inet_pton, inet_ntop as _inet_ntop, AF_INET6

from netaddr.core import AddrFormatError
from netaddr.strategy import (
    valid_words as _valid_words,
    int_to_words as _int_to_words,
    words_to_int as _words_to_int,
    valid_bits as _valid_bits,
    bits_to_int as _bits_to_int,
    int_to_bits as _int_to_bits,
    valid_bin as _valid_bin,
    int_to_bin as _int_to_bin,
    bin_to_int as _bin_to_int,
)

#: The width (in bits) of this address type.
width = 128

#: The individual word size (in bits) of this address type.
word_size = 16

#: The separator character used between each word.
word_sep = ':'

#: The AF_* constant value of this address type.
family = AF_INET6

#: A friendly string name address type.
family_name = 'IPv6'

#: The version of this address type.
version = 6

#: The number base to be used when interpreting word values as integers.
word_base = 16

#: The maximum integer value that can be represented by this address type.
max_int = 2**width - 1

#: The number of words in this address type.
num_words = width // word_size

#: The maximum integer value for an individual word in this address type.
max_word = 2**word_size - 1

#: A dictionary mapping IPv6 CIDR prefixes to the equivalent netmasks.
prefix_to_netmask = dict([(i, max_int ^ (2 ** (width - i) - 1)) for i in range(0, width + 1)])

#: A dictionary mapping IPv6 netmasks to their equivalent CIDR prefixes.
netmask_to_prefix = dict([(max_int ^ (2 ** (width - i) - 1), i) for i in range(0, width + 1)])

#: A dictionary mapping IPv6 CIDR prefixes to the equivalent hostmasks.
prefix_to_hostmask = dict([(i, (2 ** (width - i) - 1)) for i in range(0, width + 1)])

#: A dictionary mapping IPv6 hostmasks to their equivalent CIDR prefixes.
hostmask_to_prefix = dict([((2 ** (width - i) - 1), i) for i in range(0, width + 1)])

# -----------------------------------------------------------------------------
#   Dialect classes.
# -----------------------------------------------------------------------------


[docs] class ipv6_compact(object): """An IPv6 dialect class - compact form.""" #: The format string used to converting words into string values. word_fmt = '%x' #: Boolean flag indicating if IPv6 compaction algorithm should be used. compact = True
[docs] class ipv6_full(ipv6_compact): """An IPv6 dialect class - 'all zeroes' form.""" #: Boolean flag indicating if IPv6 compaction algorithm should be used. compact = False
[docs] class ipv6_verbose(ipv6_compact): """An IPv6 dialect class - extra wide 'all zeroes' form.""" #: The format string used to converting words into string values. word_fmt = '%.4x' #: Boolean flag indicating if IPv6 compaction algorithm should be used. compact = False
def valid_str(addr, flags=0): """ :param addr: An IPv6 address in presentation (string) format. :param flags: decides which rules are applied to the interpretation of the addr value. Future use - currently has no effect. :return: ``True`` if IPv6 address is valid, ``False`` otherwise. .. versionchanged:: 1.0.0 Returns ``False`` instead of raising :exc:`AddrFormatError` for empty strings. """ if not isinstance(addr, str): raise TypeError('Invalid type: %s' % type(addr)) try: _inet_pton(AF_INET6, addr) except OSError: return False return True def str_to_int(addr, flags=0): """ :param addr: An IPv6 address in string form. :param flags: decides which rules are applied to the interpretation of the addr value. Future use - currently has no effect. :return: The equivalent unsigned integer for a given IPv6 address. """ try: packed_int = _inet_pton(AF_INET6, addr) except OSError: raise AddrFormatError('%r is not a valid IPv6 address string!' % (addr,)) return packed_to_int(packed_int) def int_to_str(int_val, dialect=None): """ :param int_val: An unsigned integer. :param dialect: (optional) a Python class defining formatting options. :return: The IPv6 presentation (string) format address equivalent to the unsigned integer provided. """ if dialect is None: dialect = ipv6_compact addr = None try: packed_int = int_to_packed(int_val) if dialect.compact: # Default return value. addr = _inet_ntop(AF_INET6, packed_int) else: # Custom return value. words = list(_struct.unpack('>8H', packed_int)) tokens = [dialect.word_fmt % word for word in words] addr = word_sep.join(tokens) except Exception: raise ValueError('%r is not a valid 128-bit unsigned integer!' % (int_val,)) return addr def int_to_arpa(int_val): """ :param int_val: An unsigned integer. :return: The reverse DNS lookup for an IPv6 address in network byte order integer form. """ addr = int_to_str(int_val, ipv6_verbose) tokens = list(addr.replace(':', '')) tokens.reverse() # We won't support ip6.int here - see RFC 3152 for details. tokens = tokens + ['ip6', 'arpa', ''] return '.'.join(tokens) def int_to_packed(int_val): """ :param int_val: the integer to be packed. :return: a packed string that is equivalent to value represented by an unsigned integer. """ words = int_to_words(int_val, 4, 32) return _struct.pack('>4I', *words) def packed_to_int(packed_int): """ :param packed_int: a packed string containing an unsigned integer. It is assumed that string is packed in network byte order. :return: An unsigned integer equivalent to value of network address represented by packed binary string. """ words = list(_struct.unpack('>4I', packed_int)) int_val = 0 for i, num in enumerate(reversed(words)): word = num word = word << 32 * i int_val = int_val | word return int_val def valid_words(words): return _valid_words(words, word_size, num_words) def int_to_words(int_val, num_words=None, word_size=None): if num_words is None: num_words = globals()['num_words'] if word_size is None: word_size = globals()['word_size'] return _int_to_words(int_val, word_size, num_words) def words_to_int(words): return _words_to_int(words, word_size, num_words) def valid_bits(bits): return _valid_bits(bits, width, word_sep) def bits_to_int(bits): return _bits_to_int(bits, width, word_sep) def int_to_bits(int_val, word_sep=None): if word_sep is None: word_sep = globals()['word_sep'] return _int_to_bits(int_val, word_size, num_words, word_sep) def valid_bin(bin_val): return _valid_bin(bin_val, width) def int_to_bin(int_val): return _int_to_bin(int_val, width) def bin_to_int(bin_val): return _bin_to_int(bin_val, width)