#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2016, CESNET, z. s. p. o.
# Use of this source is governed by an ISC license, see LICENSE file.
__version__ = '0.1.10'
__author__ = 'Pavel Kácha <pavel.kacha@cesnet.cz>'
import socket
import struct
import numbers
import sys
try:
basestring
except NameError:
basestring = str
[docs]class Range(object):
__slots__ = ()
single = int
def __len__(self):
return self.high() - self.low() + 1
def __eq__(self, other):
return (self.low() == other.low() and self.high() == other.high())
def __ne__(self, other):
return not self.__eq__(other)
def __contains__(self, other):
return (self.low() <= other.low() and self.high() >= other.high())
def __iter__(self):
for i in range(self.low(), self.high()+1):
yield self.single(i)
def __getitem__(self, key):
if isinstance(key, slice):
return (self.single(self.low() + i) for i in range(*key.indices(len(self))))
else:
if key < 0:
idx = self.high() + key + 1
else:
idx = self.low() + key
if self.low() <= idx <= self.high():
return self.single(idx)
else:
raise IndexError
def __repr__(self):
return "%s('%s')" % (type(self).__name__, str(self))
[docs]class IPBase(Range):
__slots__ = ()
def __init__(self, s):
if isinstance(s, basestring):
rng = self._from_str(s)
elif isinstance(s, IPBase):
rng = self._from_range(s)
else:
rng = self._from_val(s)
self._assign(rng)
[docs] def cidr_split(self):
lo, hi = self.low(), self.high()
lo, hi = min(lo, hi), max(lo, hi)
while lo<=hi:
lower_bits = (~lo & (lo-1)).bit_length()
size = hi - lo + 1
size_bits = size.bit_length() - 1
bits = min(lower_bits, size_bits)
yield self.net((lo, self.bit_length-bits))
lo += 1 << bits
def _from_val(self, v):
try:
a, b = v
return int(a), int(b)
except Exception:
raise ValueError("Two value tuple expected, got %s" % v)
[docs]class IPRangeBase(IPBase):
__slots__ = ("lo", "hi")
def _from_range(self, r):
return (r.low(), r.high())
def _from_str(self, s):
try:
ip1, ip2 = s.split("-")
return (self.from_str(ip1), self.from_str(ip2))
except Exception:
raise ValueError("Wrong range format: %s" % s)
def _assign(self, v):
self.lo = min(v)
self.hi = max(v)
[docs] def low(self): return self.lo
[docs] def high(self): return self.hi
def __str__(self):
return "%s-%s" % (self.to_str(self.lo), self.to_str(self.hi))
def __hash__(self):
return hash((self.lo, self.hi))
[docs]class IPNetBase(IPBase):
__slots__ = ("base", "cidr", "mask")
def _from_range(self, r):
lo = r.low()
mask = len(r) - 1
if (len(r) & mask) or (lo & mask):
raise ValueError("%s is not a proper network prefix" % r)
return lo, self.bit_length - mask.bit_length()
def _from_str(self, s):
try:
net, cidr = s.split("/")
base = self.from_str(net)
cidr = int(cidr)
return base, cidr
except Exception:
raise ValueError("Wrong network format: %s" % s)
def _assign(self, v):
self.base, self.cidr = v
self.mask = (self.full_mask << (self.bit_length - self.cidr)) & self.full_mask
[docs] def low(self): return self.base & self.mask
[docs] def high(self): return self.base | (self.mask ^ self.full_mask)
def __str__(self):
return "%s/%i" % (self.to_str(self.base), self.cidr)
def __hash__(self):
return hash((self.base, self.mask))
[docs]class IPAddrBase(IPBase):
__slots__ = ("ip")
def _from_range(self, r):
if len(r)!=1:
raise ValueError("Unable to convert network %s to one ip address" % r)
return r.low()
def _from_str(self, s): return self.from_str(s)
def _from_val(self, r):
try:
return int(r)
except Exception:
raise ValueError("Integer expected as IP")
def _assign(self, v): self.ip = v
def __str__(self): return self.to_str(self.ip)
def __int__(self): return self.ip
def __hash__(self): return hash(self.ip)
[docs] def low(self): return self.ip
[docs] def high(self): return self.ip
[docs]def ip4_from_str(s):
try:
return struct.unpack("!L", socket.inet_pton(socket.AF_INET, s))[0]
except Exception:
raise ValueError("Wrong IPv4 address format: %s" % s)
[docs]def ip4_to_str(i):
try:
return socket.inet_ntop(socket.AF_INET, struct.pack('!L', i))
except Exception:
raise ValueError("Unable to convert to IPv6 address: %s" % i)
[docs]def ip6_from_str(s):
try:
hi, lo = struct.unpack("!QQ", socket.inet_pton(socket.AF_INET6, s))
return hi << 64 | lo
except Exception:
raise ValueError("Wrong IPv6 address format: %s" % s)
[docs]def ip6_to_str(i):
try:
hi = i >> 64
lo = i & 0xFFFFFFFFFFFFFFFF
return socket.inet_ntop(socket.AF_INET6, struct.pack('!QQ', hi, lo))
except Exception:
raise ValueError("Unable to convert to IPv6 address: %s" % i)
[docs]class IP4(IPAddrBase):
__slots__ = ()
bit_length = 32
full_mask = 2**bit_length-1
from_str = staticmethod(ip4_from_str)
to_str = staticmethod(ip4_to_str)
if sys.version_info < (3,):
def to_ptr_str(self):
return ".".join(str(ord(s)) for s in (reversed(struct.pack("!L", self.ip)))) + ".in-addr.arpa."
else:
[docs] def to_ptr_str(self):
return ".".join(str(s) for s in self.ip.to_bytes(4, "little")) + ".in-addr.arpa."
IP4.single = IP4
[docs]class IP4Range(IPRangeBase):
__slots__ = ()
bit_length = IP4.bit_length
full_mask = IP4.full_mask
single = IP4
from_str = staticmethod(ip4_from_str)
to_str = staticmethod(ip4_to_str)
[docs]class IP4Net(IPNetBase):
__slots__ = ()
bit_length = IP4.bit_length
full_mask = IP4.full_mask
single = IP4
from_str = staticmethod(ip4_from_str)
to_str = staticmethod(ip4_to_str)
IP4.net = IP4Net
IP4Range.net = IP4Net
IP4Net.net = IP4Net
[docs]class IP6(IPAddrBase):
__slots__ = ()
bit_length = 128
full_mask = 2**bit_length-1
from_str = staticmethod(ip6_from_str)
to_str = staticmethod(ip6_to_str)
[docs] def to_ptr_str(self):
return ".".join(reversed("%016x" % self.ip)) + ".ip6.arpa."
IP6.single = IP6
[docs]class IP6Range(IPRangeBase):
__slots__ = ()
bit_length = IP6.bit_length
full_mask = IP6.full_mask
single = IP6
from_str = staticmethod(ip6_from_str)
to_str = staticmethod(ip6_to_str)
[docs]class IP6Net(IPNetBase):
__slots__ = ()
bit_length = IP6.bit_length
full_mask = IP6.full_mask
single = IP6
from_str = staticmethod(ip6_from_str)
to_str = staticmethod(ip6_to_str)
IP6.net = IP6Net
IP6Range.net = IP6Net
IP6Net.net = IP6Net
[docs]def from_str(s):
for t in IP4Net, IP4Range, IP4, IP6Net, IP6Range, IP6:
try:
return t(s)
except ValueError:
pass
raise ValueError("%s does not appear as IP address, network or range string" % s)
[docs]def from_str_v4(s):
for t in IP4Net, IP4Range, IP4:
try:
return t(s)
except ValueError:
pass
raise ValueError("%s does not appear as IPv4 address, network or range string" % s)
[docs]def from_str_v6(s):
for t in IP6Net, IP6Range, IP6:
try:
return t(s)
except ValueError:
pass
raise ValueError("%s does not appear as IPv6 address, network or range string" % s)
[docs]def ip_from_str(s):
for t in IP4, IP6:
try:
return t(s)
except ValueError:
pass
raise ValueError("%s does not appear as IPv4 nor IPv6 address" % s)