#
# Copyright (C) 2010-2017 Samuel Abels
# The MIT License (MIT)
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files
# (the "Software"), to deal in the Software without restriction,
# including without limitation the rights to use, copy, modify, merge,
# publish, distribute, sublicense, and/or sell copies of the Software,
# and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"""
IPv6 address calculation and conversion.
"""
from builtins import range
[docs]def is_ip(string):
"""
Returns True if the given string is an IPv6 address, False otherwise.
:type string: string
:param string: Any string.
:rtype: bool
:return: True if the string is an IP address, False otherwise.
"""
try:
normalize_ip(string)
except ValueError:
return False
return True
[docs]def normalize_ip(ip):
"""
Transform the address into a standard, fixed-length form, such as:
1234:0:01:02:: -> 1234:0000:0001:0002:0000:0000:0000:0000
1234::A -> 1234:0000:0000:0000:0000:0000:0000:000a
:type ip: string
:param ip: An IP address.
:rtype: string
:return: The normalized IP.
"""
theip = ip
if theip.startswith('::'):
theip = '0' + theip
if theip.endswith('::'):
theip += '0'
segments = theip.split(':')
if len(segments) == 1:
raise ValueError('no colons in ipv6 address: ' + repr(ip))
fill = 8 - len(segments)
if fill < 0:
raise ValueError('ipv6 address has too many segments: ' + repr(ip))
result = []
for segment in segments:
if segment == '':
if fill == 0:
raise ValueError('unexpected double colon: ' + repr(ip))
for n in range(fill + 1):
result.append('0000')
fill = 0
else:
try:
int(segment, 16)
except ValueError:
raise ValueError('invalid hex value in ' + repr(ip))
result.append(segment.rjust(4, '0'))
return ':'.join(result).lower()
[docs]def clean_ip(ip):
"""
Cleans the ip address up, useful for removing leading zeros, e.g.::
1234:0:01:02:: -> 1234:0:1:2::
1234:0000:0000:0000:0000:0000:0000:000A -> 1234::a
1234:0000:0000:0000:0001:0000:0000:0000 -> 1234:0:0:0:1::
0000:0000:0000:0000:0001:0000:0000:0000 -> ::1:0:0:0
:type ip: string
:param ip: An IP address.
:rtype: string
:return: The cleaned up IP.
"""
theip = normalize_ip(ip)
segments = ['%x' % int(s, 16) for s in theip.split(':')]
# Find the longest consecutive sequence of zeroes.
seq = {0: 0}
start = None
count = 0
for n, segment in enumerate(segments):
if segment != '0':
start = None
count = 0
continue
if start is None:
start = n
count += 1
seq[count] = start
# Replace those zeroes by a double colon.
count = max(seq)
start = seq[count]
result = []
for n, segment in enumerate(segments):
if n == start and count > 1:
if n == 0:
result.append('')
result.append('')
if n == 7:
result.append('')
continue
elif start < n < start + count:
if n == 7:
result.append('')
continue
result.append(segment)
return ':'.join(result)
[docs]def parse_prefix(prefix, default_length=128):
"""
Splits the given IP prefix into a network address and a prefix length.
If the prefix does not have a length (i.e., it is a simple IP address),
it is presumed to have the given default length.
:type prefix: string
:param prefix: An IP mask.
:type default_length: long
:param default_length: The default ip prefix length.
:rtype: string, int
:return: A tuple containing the IP address and prefix length.
"""
if '/' in prefix:
network, pfxlen = prefix.split('/')
else:
network = prefix
pfxlen = default_length
return network, int(pfxlen)