fix vendor
This commit is contained in:
1972
vendor/netaddr/ip/__init__.py
vendored
Normal file
1972
vendor/netaddr/ip/__init__.py
vendored
Normal file
File diff suppressed because it is too large
Load Diff
312
vendor/netaddr/ip/glob.py
vendored
Normal file
312
vendor/netaddr/ip/glob.py
vendored
Normal file
@@ -0,0 +1,312 @@
|
||||
#-----------------------------------------------------------------------------
|
||||
# Copyright (c) 2008 by David P. D. Moss. All rights reserved.
|
||||
#
|
||||
# Released under the BSD license. See the LICENSE file for details.
|
||||
#-----------------------------------------------------------------------------
|
||||
"""
|
||||
Routines and classes for supporting and expressing IP address ranges using a
|
||||
glob style syntax.
|
||||
|
||||
"""
|
||||
from netaddr.core import AddrFormatError, AddrConversionError
|
||||
from netaddr.ip import IPRange, IPAddress, IPNetwork, iprange_to_cidrs
|
||||
from netaddr.compat import _is_str
|
||||
|
||||
|
||||
def valid_glob(ipglob):
|
||||
"""
|
||||
:param ipglob: An IP address range in a glob-style format.
|
||||
|
||||
:return: ``True`` if IP range glob is valid, ``False`` otherwise.
|
||||
"""
|
||||
#TODO: Add support for abbreviated ipglobs.
|
||||
#TODO: e.g. 192.0.*.* == 192.0.*
|
||||
#TODO: *.*.*.* == *
|
||||
#TODO: Add strict flag to enable verbose ipglob checking.
|
||||
if not _is_str(ipglob):
|
||||
return False
|
||||
|
||||
seen_hyphen = False
|
||||
seen_asterisk = False
|
||||
|
||||
octets = ipglob.split('.')
|
||||
|
||||
if len(octets) != 4:
|
||||
return False
|
||||
|
||||
for octet in octets:
|
||||
if '-' in octet:
|
||||
if seen_hyphen:
|
||||
return False
|
||||
seen_hyphen = True
|
||||
if seen_asterisk:
|
||||
# Asterisks cannot precede hyphenated octets.
|
||||
return False
|
||||
try:
|
||||
(octet1, octet2) = [int(i) for i in octet.split('-')]
|
||||
except ValueError:
|
||||
return False
|
||||
if octet1 >= octet2:
|
||||
return False
|
||||
if not 0 <= octet1 <= 254:
|
||||
return False
|
||||
if not 1 <= octet2 <= 255:
|
||||
return False
|
||||
elif octet == '*':
|
||||
seen_asterisk = True
|
||||
else:
|
||||
if seen_hyphen is True:
|
||||
return False
|
||||
if seen_asterisk is True:
|
||||
return False
|
||||
try:
|
||||
if not 0 <= int(octet) <= 255:
|
||||
return False
|
||||
except ValueError:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def glob_to_iptuple(ipglob):
|
||||
"""
|
||||
A function that accepts a glob-style IP range and returns the component
|
||||
lower and upper bound IP address.
|
||||
|
||||
:param ipglob: an IP address range in a glob-style format.
|
||||
|
||||
:return: a tuple contain lower and upper bound IP objects.
|
||||
"""
|
||||
if not valid_glob(ipglob):
|
||||
raise AddrFormatError('not a recognised IP glob range: %r!' % (ipglob,))
|
||||
|
||||
start_tokens = []
|
||||
end_tokens = []
|
||||
|
||||
for octet in ipglob.split('.'):
|
||||
if '-' in octet:
|
||||
tokens = octet.split('-')
|
||||
start_tokens.append(tokens[0])
|
||||
end_tokens.append(tokens[1])
|
||||
elif octet == '*':
|
||||
start_tokens.append('0')
|
||||
end_tokens.append('255')
|
||||
else:
|
||||
start_tokens.append(octet)
|
||||
end_tokens.append(octet)
|
||||
|
||||
return IPAddress('.'.join(start_tokens)), IPAddress('.'.join(end_tokens))
|
||||
|
||||
|
||||
def glob_to_iprange(ipglob):
|
||||
"""
|
||||
A function that accepts a glob-style IP range and returns the equivalent
|
||||
IP range.
|
||||
|
||||
:param ipglob: an IP address range in a glob-style format.
|
||||
|
||||
:return: an IPRange object.
|
||||
"""
|
||||
if not valid_glob(ipglob):
|
||||
raise AddrFormatError('not a recognised IP glob range: %r!' % (ipglob,))
|
||||
|
||||
start_tokens = []
|
||||
end_tokens = []
|
||||
|
||||
for octet in ipglob.split('.'):
|
||||
if '-' in octet:
|
||||
tokens = octet.split('-')
|
||||
start_tokens.append(tokens[0])
|
||||
end_tokens.append(tokens[1])
|
||||
elif octet == '*':
|
||||
start_tokens.append('0')
|
||||
end_tokens.append('255')
|
||||
else:
|
||||
start_tokens.append(octet)
|
||||
end_tokens.append(octet)
|
||||
|
||||
return IPRange('.'.join(start_tokens), '.'.join(end_tokens))
|
||||
|
||||
|
||||
def iprange_to_globs(start, end):
|
||||
"""
|
||||
A function that accepts an arbitrary start and end IP address or subnet
|
||||
and returns one or more glob-style IP ranges.
|
||||
|
||||
:param start: the start IP address or subnet.
|
||||
|
||||
:param end: the end IP address or subnet.
|
||||
|
||||
:return: a list containing one or more IP globs.
|
||||
"""
|
||||
start = IPAddress(start)
|
||||
end = IPAddress(end)
|
||||
|
||||
if start.version != 4 and end.version != 4:
|
||||
raise AddrConversionError('IP glob ranges only support IPv4!')
|
||||
|
||||
def _iprange_to_glob(lb, ub):
|
||||
# Internal function to process individual IP globs.
|
||||
t1 = [int(_) for _ in str(lb).split('.')]
|
||||
t2 = [int(_) for _ in str(ub).split('.')]
|
||||
|
||||
tokens = []
|
||||
|
||||
seen_hyphen = False
|
||||
seen_asterisk = False
|
||||
|
||||
for i in range(4):
|
||||
if t1[i] == t2[i]:
|
||||
# A normal octet.
|
||||
tokens.append(str(t1[i]))
|
||||
elif (t1[i] == 0) and (t2[i] == 255):
|
||||
# An asterisk octet.
|
||||
tokens.append('*')
|
||||
seen_asterisk = True
|
||||
else:
|
||||
# Create a hyphenated octet - only one allowed per IP glob.
|
||||
if not seen_asterisk:
|
||||
if not seen_hyphen:
|
||||
tokens.append('%s-%s' % (t1[i], t2[i]))
|
||||
seen_hyphen = True
|
||||
else:
|
||||
raise AddrConversionError(
|
||||
'only 1 hyphenated octet per IP glob allowed!')
|
||||
else:
|
||||
raise AddrConversionError(
|
||||
"asterisks are not allowed before hyphenated octets!")
|
||||
|
||||
return '.'.join(tokens)
|
||||
|
||||
globs = []
|
||||
|
||||
try:
|
||||
# IP range can be represented by a single glob.
|
||||
ipglob = _iprange_to_glob(start, end)
|
||||
if not valid_glob(ipglob):
|
||||
#TODO: this is a workaround, it is produces non-optimal but valid
|
||||
#TODO: glob conversions. Fix inner function so that is always
|
||||
#TODO: produces a valid glob.
|
||||
raise AddrConversionError('invalid ip glob created')
|
||||
globs.append(ipglob)
|
||||
except AddrConversionError:
|
||||
# Break IP range up into CIDRs before conversion to globs.
|
||||
#
|
||||
#TODO: this is still not completely optimised but is good enough
|
||||
#TODO: for the moment.
|
||||
#
|
||||
for cidr in iprange_to_cidrs(start, end):
|
||||
ipglob = _iprange_to_glob(cidr[0], cidr[-1])
|
||||
globs.append(ipglob)
|
||||
|
||||
return globs
|
||||
|
||||
|
||||
def glob_to_cidrs(ipglob):
|
||||
"""
|
||||
A function that accepts a glob-style IP range and returns a list of one
|
||||
or more IP CIDRs that exactly matches it.
|
||||
|
||||
:param ipglob: an IP address range in a glob-style format.
|
||||
|
||||
:return: a list of one or more IP objects.
|
||||
"""
|
||||
return iprange_to_cidrs(*glob_to_iptuple(ipglob))
|
||||
|
||||
|
||||
def cidr_to_glob(cidr):
|
||||
"""
|
||||
A function that accepts an IP subnet in a glob-style format and returns
|
||||
a list of CIDR subnets that exactly matches the specified glob.
|
||||
|
||||
:param cidr: an IP object CIDR subnet.
|
||||
|
||||
:return: a list of one or more IP addresses and subnets.
|
||||
"""
|
||||
ip = IPNetwork(cidr)
|
||||
globs = iprange_to_globs(ip[0], ip[-1])
|
||||
if len(globs) != 1:
|
||||
# There should only ever be a one to one mapping between a CIDR and
|
||||
# an IP glob range.
|
||||
raise AddrConversionError('bad CIDR to IP glob conversion!')
|
||||
return globs[0]
|
||||
|
||||
|
||||
class IPGlob(IPRange):
|
||||
"""
|
||||
Represents an IP address range using a glob-style syntax ``x.x.x-y.*``
|
||||
|
||||
Individual octets can be represented using the following shortcuts :
|
||||
|
||||
1. ``*`` - the asterisk octet (represents values ``0`` through ``255``)
|
||||
2. ``x-y`` - the hyphenated octet (represents values ``x`` through ``y``)
|
||||
|
||||
A few basic rules also apply :
|
||||
|
||||
1. ``x`` must always be less than ``y``, therefore :
|
||||
|
||||
- ``x`` can only be ``0`` through ``254``
|
||||
- ``y`` can only be ``1`` through ``255``
|
||||
|
||||
2. only one hyphenated octet per IP glob is allowed
|
||||
3. only asterisks are permitted after a hyphenated octet
|
||||
|
||||
Examples:
|
||||
|
||||
+------------------+------------------------------+
|
||||
| IP glob | Description |
|
||||
+==================+==============================+
|
||||
| ``192.0.2.1`` | a single address |
|
||||
+------------------+------------------------------+
|
||||
| ``192.0.2.0-31`` | 32 addresses |
|
||||
+------------------+------------------------------+
|
||||
| ``192.0.2.*`` | 256 addresses |
|
||||
+------------------+------------------------------+
|
||||
| ``192.0.2-3.*`` | 512 addresses |
|
||||
+------------------+------------------------------+
|
||||
| ``192.0-1.*.*`` | 131,072 addresses |
|
||||
+------------------+------------------------------+
|
||||
| ``*.*.*.*`` | the whole IPv4 address space |
|
||||
+------------------+------------------------------+
|
||||
|
||||
.. note :: \
|
||||
IP glob ranges are not directly equivalent to CIDR blocks. \
|
||||
They can represent address ranges that do not fall on strict bit mask \
|
||||
boundaries. They are suitable for use in configuration files, being \
|
||||
more obvious and readable than their CIDR counterparts, especially for \
|
||||
admins and end users with little or no networking knowledge or \
|
||||
experience. All CIDR addresses can always be represented as IP globs \
|
||||
but the reverse is not always true.
|
||||
"""
|
||||
__slots__ = ('_glob',)
|
||||
|
||||
def __init__(self, ipglob):
|
||||
(start, end) = glob_to_iptuple(ipglob)
|
||||
super(IPGlob, self).__init__(start, end)
|
||||
self.glob = iprange_to_globs(self._start, self._end)[0]
|
||||
|
||||
def __getstate__(self):
|
||||
""":return: Pickled state of an `IPGlob` object."""
|
||||
return super(IPGlob, self).__getstate__()
|
||||
|
||||
def __setstate__(self, state):
|
||||
""":param state: data used to unpickle a pickled `IPGlob` object."""
|
||||
super(IPGlob, self).__setstate__(state)
|
||||
self.glob = iprange_to_globs(self._start, self._end)[0]
|
||||
|
||||
def _get_glob(self):
|
||||
return self._glob
|
||||
|
||||
def _set_glob(self, ipglob):
|
||||
(self._start, self._end) = glob_to_iptuple(ipglob)
|
||||
self._glob = iprange_to_globs(self._start, self._end)[0]
|
||||
|
||||
glob = property(_get_glob, _set_glob, None,
|
||||
'an arbitrary IP address range in glob format.')
|
||||
|
||||
def __str__(self):
|
||||
""":return: IP glob in common representational format."""
|
||||
return "%s" % self.glob
|
||||
|
||||
def __repr__(self):
|
||||
""":return: Python statement to create an equivalent object"""
|
||||
return "%s('%s')" % (self.__class__.__name__, self.glob)
|
448
vendor/netaddr/ip/iana.py
vendored
Normal file
448
vendor/netaddr/ip/iana.py
vendored
Normal file
@@ -0,0 +1,448 @@
|
||||
#!/usr/bin/env python
|
||||
#-----------------------------------------------------------------------------
|
||||
# Copyright (c) 2008 by David P. D. Moss. All rights reserved.
|
||||
#
|
||||
# Released under the BSD license. See the LICENSE file for details.
|
||||
#-----------------------------------------------------------------------------
|
||||
#
|
||||
# DISCLAIMER
|
||||
#
|
||||
# netaddr is not sponsored nor endorsed by IANA.
|
||||
#
|
||||
# Use of data from IANA (Internet Assigned Numbers Authority) is subject to
|
||||
# copyright and is provided with prior written permission.
|
||||
#
|
||||
# IANA data files included with netaddr are not modified in any way but are
|
||||
# parsed and made available to end users through an API.
|
||||
#
|
||||
# See README file and source code for URLs to latest copies of the relevant
|
||||
# files.
|
||||
#
|
||||
#-----------------------------------------------------------------------------
|
||||
"""
|
||||
Routines for accessing data published by IANA (Internet Assigned Numbers
|
||||
Authority).
|
||||
|
||||
More details can be found at the following URLs :-
|
||||
|
||||
- IANA Home Page - http://www.iana.org/
|
||||
- IEEE Protocols Information Home Page - http://www.iana.org/protocols/
|
||||
"""
|
||||
|
||||
import sys as _sys
|
||||
from xml.sax import make_parser, handler
|
||||
|
||||
from netaddr.core import Publisher, Subscriber
|
||||
from netaddr.ip import IPAddress, IPNetwork, IPRange, cidr_abbrev_to_verbose
|
||||
from netaddr.compat import _dict_items, _callable, _importlib_resources
|
||||
|
||||
|
||||
|
||||
#: Topic based lookup dictionary for IANA information.
|
||||
IANA_INFO = {
|
||||
'IPv4': {},
|
||||
'IPv6': {},
|
||||
'IPv6_unicast': {},
|
||||
'multicast': {},
|
||||
}
|
||||
|
||||
|
||||
class SaxRecordParser(handler.ContentHandler):
|
||||
def __init__(self, callback=None):
|
||||
self._level = 0
|
||||
self._is_active = False
|
||||
self._record = None
|
||||
self._tag_level = None
|
||||
self._tag_payload = None
|
||||
self._tag_feeding = None
|
||||
self._callback = callback
|
||||
|
||||
def startElement(self, name, attrs):
|
||||
self._level += 1
|
||||
|
||||
if self._is_active is False:
|
||||
if name == 'record':
|
||||
self._is_active = True
|
||||
self._tag_level = self._level
|
||||
self._record = {}
|
||||
if 'date' in attrs:
|
||||
self._record['date'] = attrs['date']
|
||||
elif self._level == self._tag_level + 1:
|
||||
if name == 'xref':
|
||||
if 'type' in attrs and 'data' in attrs:
|
||||
l = self._record.setdefault(attrs['type'], [])
|
||||
l.append(attrs['data'])
|
||||
else:
|
||||
self._tag_payload = []
|
||||
self._tag_feeding = True
|
||||
else:
|
||||
self._tag_feeding = False
|
||||
|
||||
def endElement(self, name):
|
||||
if self._is_active is True:
|
||||
if name == 'record' and self._tag_level == self._level:
|
||||
self._is_active = False
|
||||
self._tag_level = None
|
||||
if _callable(self._callback):
|
||||
self._callback(self._record)
|
||||
self._record = None
|
||||
elif self._level == self._tag_level + 1:
|
||||
if name != 'xref':
|
||||
self._record[name] = ''.join(self._tag_payload)
|
||||
self._tag_payload = None
|
||||
self._tag_feeding = False
|
||||
|
||||
self._level -= 1
|
||||
|
||||
def characters(self, content):
|
||||
if self._tag_feeding is True:
|
||||
self._tag_payload.append(content)
|
||||
|
||||
|
||||
class XMLRecordParser(Publisher):
|
||||
"""
|
||||
A configurable Parser that understands how to parse XML based records.
|
||||
"""
|
||||
|
||||
def __init__(self, fh, **kwargs):
|
||||
"""
|
||||
Constructor.
|
||||
|
||||
fh - a valid, open file handle to XML based record data.
|
||||
"""
|
||||
super(XMLRecordParser, self).__init__()
|
||||
|
||||
self.xmlparser = make_parser()
|
||||
self.xmlparser.setContentHandler(SaxRecordParser(self.consume_record))
|
||||
|
||||
self.fh = fh
|
||||
|
||||
self.__dict__.update(kwargs)
|
||||
|
||||
def process_record(self, rec):
|
||||
"""
|
||||
This is the callback method invoked for every record. It is usually
|
||||
over-ridden by base classes to provide specific record-based logic.
|
||||
|
||||
Any record can be vetoed (not passed to registered Subscriber objects)
|
||||
by simply returning None.
|
||||
"""
|
||||
return rec
|
||||
|
||||
def consume_record(self, rec):
|
||||
record = self.process_record(rec)
|
||||
if record is not None:
|
||||
self.notify(record)
|
||||
|
||||
def parse(self):
|
||||
"""
|
||||
Parse and normalises records, notifying registered subscribers with
|
||||
record data as it is encountered.
|
||||
"""
|
||||
self.xmlparser.parse(self.fh)
|
||||
|
||||
|
||||
class IPv4Parser(XMLRecordParser):
|
||||
"""
|
||||
A XMLRecordParser that understands how to parse and retrieve data records
|
||||
from the IANA IPv4 address space file.
|
||||
|
||||
It can be found online here :-
|
||||
|
||||
- http://www.iana.org/assignments/ipv4-address-space/ipv4-address-space.xml
|
||||
"""
|
||||
|
||||
def __init__(self, fh, **kwargs):
|
||||
"""
|
||||
Constructor.
|
||||
|
||||
fh - a valid, open file handle to an IANA IPv4 address space file.
|
||||
|
||||
kwargs - additional parser options.
|
||||
"""
|
||||
super(IPv4Parser, self).__init__(fh)
|
||||
|
||||
def process_record(self, rec):
|
||||
"""
|
||||
Callback method invoked for every record.
|
||||
|
||||
See base class method for more details.
|
||||
"""
|
||||
|
||||
record = {}
|
||||
for key in ('prefix', 'designation', 'date', 'whois', 'status'):
|
||||
record[key] = str(rec.get(key, '')).strip()
|
||||
|
||||
# Strip leading zeros from octet.
|
||||
if '/' in record['prefix']:
|
||||
(octet, prefix) = record['prefix'].split('/')
|
||||
record['prefix'] = '%d/%d' % (int(octet), int(prefix))
|
||||
|
||||
record['status'] = record['status'].capitalize()
|
||||
|
||||
return record
|
||||
|
||||
|
||||
class IPv6Parser(XMLRecordParser):
|
||||
"""
|
||||
A XMLRecordParser that understands how to parse and retrieve data records
|
||||
from the IANA IPv6 address space file.
|
||||
|
||||
It can be found online here :-
|
||||
|
||||
- http://www.iana.org/assignments/ipv6-address-space/ipv6-address-space.xml
|
||||
"""
|
||||
|
||||
def __init__(self, fh, **kwargs):
|
||||
"""
|
||||
Constructor.
|
||||
|
||||
fh - a valid, open file handle to an IANA IPv6 address space file.
|
||||
|
||||
kwargs - additional parser options.
|
||||
"""
|
||||
super(IPv6Parser, self).__init__(fh)
|
||||
|
||||
def process_record(self, rec):
|
||||
"""
|
||||
Callback method invoked for every record.
|
||||
|
||||
See base class method for more details.
|
||||
"""
|
||||
|
||||
record = {
|
||||
'prefix': str(rec.get('prefix', '')).strip(),
|
||||
'allocation': str(rec.get('description', '')).strip(),
|
||||
# HACK: -1 instead of 0 is a hacky hack to get 4291 instead of 3513 from
|
||||
#
|
||||
# <xref type="rfc" data="rfc3513"/> was later obsoleted by <xref type="rfc" data="rfc4291"/>
|
||||
#
|
||||
# I imagine there's no way to solve this in a general way, maybe we should start returning a list
|
||||
# of RFC-s here?
|
||||
'reference': str(rec.get('rfc', [''])[-1]).strip(),
|
||||
}
|
||||
|
||||
return record
|
||||
|
||||
|
||||
class IPv6UnicastParser(XMLRecordParser):
|
||||
"""
|
||||
A XMLRecordParser that understands how to parse and retrieve data records
|
||||
from the IANA IPv6 unicast address assignments file.
|
||||
|
||||
It can be found online here :-
|
||||
|
||||
- http://www.iana.org/assignments/ipv6-unicast-address-assignments/ipv6-unicast-address-assignments.xml
|
||||
"""
|
||||
def __init__(self, fh, **kwargs):
|
||||
"""
|
||||
Constructor.
|
||||
|
||||
fh - a valid, open file handle to an IANA IPv6 address space file.
|
||||
|
||||
kwargs - additional parser options.
|
||||
"""
|
||||
super(IPv6UnicastParser, self).__init__(fh)
|
||||
|
||||
def process_record(self, rec):
|
||||
"""
|
||||
Callback method invoked for every record.
|
||||
|
||||
See base class method for more details.
|
||||
"""
|
||||
record = {
|
||||
'status': str(rec.get('status', '')).strip(),
|
||||
'description': str(rec.get('description', '')).strip(),
|
||||
'prefix': str(rec.get('prefix', '')).strip(),
|
||||
'date': str(rec.get('date', '')).strip(),
|
||||
'whois': str(rec.get('whois', '')).strip(),
|
||||
}
|
||||
|
||||
return record
|
||||
|
||||
|
||||
class MulticastParser(XMLRecordParser):
|
||||
"""
|
||||
A XMLRecordParser that knows how to process the IANA IPv4 multicast address
|
||||
allocation file.
|
||||
|
||||
It can be found online here :-
|
||||
|
||||
- http://www.iana.org/assignments/multicast-addresses/multicast-addresses.xml
|
||||
"""
|
||||
|
||||
def __init__(self, fh, **kwargs):
|
||||
"""
|
||||
Constructor.
|
||||
|
||||
fh - a valid, open file handle to an IANA IPv4 multicast address
|
||||
allocation file.
|
||||
|
||||
kwargs - additional parser options.
|
||||
"""
|
||||
super(MulticastParser, self).__init__(fh)
|
||||
|
||||
def normalise_addr(self, addr):
|
||||
"""
|
||||
Removes variations from address entries found in this particular file.
|
||||
"""
|
||||
if '-' in addr:
|
||||
(a1, a2) = addr.split('-')
|
||||
o1 = a1.strip().split('.')
|
||||
o2 = a2.strip().split('.')
|
||||
return '%s-%s' % ('.'.join([str(int(i)) for i in o1]),
|
||||
'.'.join([str(int(i)) for i in o2]))
|
||||
else:
|
||||
o1 = addr.strip().split('.')
|
||||
return '.'.join([str(int(i)) for i in o1])
|
||||
|
||||
def process_record(self, rec):
|
||||
"""
|
||||
Callback method invoked for every record.
|
||||
|
||||
See base class method for more details.
|
||||
"""
|
||||
|
||||
if 'addr' in rec:
|
||||
record = {
|
||||
'address': self.normalise_addr(str(rec['addr'])),
|
||||
'descr': str(rec.get('description', '')),
|
||||
}
|
||||
return record
|
||||
|
||||
|
||||
class DictUpdater(Subscriber):
|
||||
"""
|
||||
Concrete Subscriber that inserts records received from a Publisher into a
|
||||
dictionary.
|
||||
"""
|
||||
|
||||
def __init__(self, dct, topic, unique_key):
|
||||
"""
|
||||
Constructor.
|
||||
|
||||
dct - lookup dict or dict like object to insert records into.
|
||||
|
||||
topic - high-level category name of data to be processed.
|
||||
|
||||
unique_key - key name in data dict that uniquely identifies it.
|
||||
"""
|
||||
self.dct = dct
|
||||
self.topic = topic
|
||||
self.unique_key = unique_key
|
||||
|
||||
def update(self, data):
|
||||
"""
|
||||
Callback function used by Publisher to notify this Subscriber about
|
||||
an update. Stores topic based information into dictionary passed to
|
||||
constructor.
|
||||
"""
|
||||
data_id = data[self.unique_key]
|
||||
|
||||
if self.topic == 'IPv4':
|
||||
cidr = IPNetwork(cidr_abbrev_to_verbose(data_id))
|
||||
self.dct[cidr] = data
|
||||
elif self.topic == 'IPv6':
|
||||
cidr = IPNetwork(cidr_abbrev_to_verbose(data_id))
|
||||
self.dct[cidr] = data
|
||||
elif self.topic == 'IPv6_unicast':
|
||||
cidr = IPNetwork(data_id)
|
||||
self.dct[cidr] = data
|
||||
elif self.topic == 'multicast':
|
||||
iprange = None
|
||||
if '-' in data_id:
|
||||
# See if we can manage a single CIDR.
|
||||
(first, last) = data_id.split('-')
|
||||
iprange = IPRange(first, last)
|
||||
cidrs = iprange.cidrs()
|
||||
if len(cidrs) == 1:
|
||||
iprange = cidrs[0]
|
||||
else:
|
||||
iprange = IPAddress(data_id)
|
||||
self.dct[iprange] = data
|
||||
|
||||
|
||||
def load_info():
|
||||
"""
|
||||
Parse and load internal IANA data lookups with the latest information from
|
||||
data files.
|
||||
"""
|
||||
ipv4 = IPv4Parser(_importlib_resources.open_binary(__package__, 'ipv4-address-space.xml'))
|
||||
ipv4.attach(DictUpdater(IANA_INFO['IPv4'], 'IPv4', 'prefix'))
|
||||
ipv4.parse()
|
||||
|
||||
ipv6 = IPv6Parser(_importlib_resources.open_binary(__package__, 'ipv6-address-space.xml'))
|
||||
ipv6.attach(DictUpdater(IANA_INFO['IPv6'], 'IPv6', 'prefix'))
|
||||
ipv6.parse()
|
||||
|
||||
ipv6ua = IPv6UnicastParser(
|
||||
_importlib_resources.open_binary(__package__, 'ipv6-unicast-address-assignments.xml'),
|
||||
)
|
||||
ipv6ua.attach(DictUpdater(IANA_INFO['IPv6_unicast'], 'IPv6_unicast', 'prefix'))
|
||||
ipv6ua.parse()
|
||||
|
||||
mcast = MulticastParser(_importlib_resources.open_binary(__package__, 'multicast-addresses.xml'))
|
||||
mcast.attach(DictUpdater(IANA_INFO['multicast'], 'multicast', 'address'))
|
||||
mcast.parse()
|
||||
|
||||
|
||||
def pprint_info(fh=None):
|
||||
"""
|
||||
Pretty prints IANA information to filehandle.
|
||||
"""
|
||||
if fh is None:
|
||||
fh = _sys.stdout
|
||||
|
||||
for category in sorted(IANA_INFO):
|
||||
fh.write('-' * len(category) + "\n")
|
||||
fh.write(category + "\n")
|
||||
fh.write('-' * len(category) + "\n")
|
||||
ipranges = IANA_INFO[category]
|
||||
for iprange in sorted(ipranges):
|
||||
details = ipranges[iprange]
|
||||
fh.write('%-45r' % (iprange) + details + "\n")
|
||||
|
||||
|
||||
def _within_bounds(ip, ip_range):
|
||||
# Boundary checking for multiple IP classes.
|
||||
if hasattr(ip_range, 'first'):
|
||||
# IP network or IP range.
|
||||
return ip in ip_range
|
||||
elif hasattr(ip_range, 'value'):
|
||||
# IP address.
|
||||
return ip == ip_range
|
||||
|
||||
raise Exception('Unsupported IP range or address: %r!' % (ip_range,))
|
||||
|
||||
|
||||
def query(ip_addr):
|
||||
"""Returns informational data specific to this IP address."""
|
||||
info = {}
|
||||
|
||||
if ip_addr.version == 4:
|
||||
for cidr, record in _dict_items(IANA_INFO['IPv4']):
|
||||
if _within_bounds(ip_addr, cidr):
|
||||
info.setdefault('IPv4', [])
|
||||
info['IPv4'].append(record)
|
||||
|
||||
if ip_addr.is_multicast():
|
||||
for iprange, record in _dict_items(IANA_INFO['multicast']):
|
||||
if _within_bounds(ip_addr, iprange):
|
||||
info.setdefault('Multicast', [])
|
||||
info['Multicast'].append(record)
|
||||
|
||||
elif ip_addr.version == 6:
|
||||
for cidr, record in _dict_items(IANA_INFO['IPv6']):
|
||||
if _within_bounds(ip_addr, cidr):
|
||||
info.setdefault('IPv6', [])
|
||||
info['IPv6'].append(record)
|
||||
|
||||
for cidr, record in _dict_items(IANA_INFO['IPv6_unicast']):
|
||||
if _within_bounds(ip_addr, cidr):
|
||||
info.setdefault('IPv6_unicast', [])
|
||||
info['IPv6_unicast'].append(record)
|
||||
|
||||
return info
|
||||
|
||||
# On module import, read IANA data files and populate lookups dict.
|
||||
load_info()
|
2644
vendor/netaddr/ip/ipv4-address-space.xml
vendored
Normal file
2644
vendor/netaddr/ip/ipv4-address-space.xml
vendored
Normal file
File diff suppressed because it is too large
Load Diff
198
vendor/netaddr/ip/ipv6-address-space.xml
vendored
Normal file
198
vendor/netaddr/ip/ipv6-address-space.xml
vendored
Normal file
@@ -0,0 +1,198 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<?xml-stylesheet type="text/xsl" href="ipv6-address-space.xsl"?>
|
||||
<?oxygen RNGSchema="ipv6-address-space.rng" type="xml"?>
|
||||
<registry xmlns="http://www.iana.org/assignments" id="ipv6-address-space">
|
||||
<title>Internet Protocol Version 6 Address Space</title>
|
||||
<updated>2019-09-13</updated>
|
||||
<note>The IPv6 address management function was formally delegated to
|
||||
IANA in December 1995 <xref type="rfc" data="rfc1881"/>. The registration procedure
|
||||
was confirmed with the IETF Chair in March 2010.
|
||||
|
||||
As stated in RFC3513, IANA should limit its allocation of IPv6-unicast
|
||||
address space to the range of addresses that start with binary value 001.
|
||||
The rest of the global unicast address space (approximately 85% of the IPv6
|
||||
address space) is reserved for future definition and use, and is not to be
|
||||
assigned by IANA at this time.
|
||||
|
||||
While <xref type="rfc" data="rfc3513"/> was obsoleted by <xref type="rfc" data="rfc4291"/>,
|
||||
the guidiance provided to IANA did not change regarding the allocation of IPv6
|
||||
unicast addresses.
|
||||
|
||||
</note>
|
||||
<registry id="ipv6-address-space-1">
|
||||
<registration_rule>IESG Approval</registration_rule>
|
||||
<record>
|
||||
<prefix>0000::/8</prefix>
|
||||
<description>Reserved by IETF</description>
|
||||
<xref type="rfc" data="rfc3513"/><xref type="rfc" data="rfc4291"/>
|
||||
<notes>
|
||||
<xref type="note" data="1"/>
|
||||
<xref type="note" data="2"/>
|
||||
<xref type="note" data="3"/>
|
||||
<xref type="note" data="4"/>
|
||||
<xref type="note" data="5"/>
|
||||
<xref type="note" data="6"/>
|
||||
</notes>
|
||||
</record>
|
||||
<record>
|
||||
<prefix>0100::/8</prefix>
|
||||
<description>Reserved by IETF</description>
|
||||
<xref type="rfc" data="rfc3513"/><xref type="rfc" data="rfc4291"/>
|
||||
<notes>0100::/64 reserved for Discard-Only Address Block <xref type="rfc" data="rfc6666"/>.
|
||||
Complete registration details are found in <xref type="registry" data="iana-ipv6-special-registry"/>.</notes>
|
||||
</record>
|
||||
<record>
|
||||
<prefix>0200::/7</prefix>
|
||||
<description>Reserved by IETF</description>
|
||||
<xref type="rfc" data="rfc4048"/>
|
||||
<notes>Deprecated as of December 2004 <xref type="rfc" data="rfc4048"/>.
|
||||
Formerly an OSI NSAP-mapped prefix set <xref type="rfc" data="rfc4548"/>.</notes>
|
||||
</record>
|
||||
<record>
|
||||
<prefix>0400::/6</prefix>
|
||||
<description>Reserved by IETF</description>
|
||||
<xref type="rfc" data="rfc3513"/><xref type="rfc" data="rfc4291"/>
|
||||
<notes/>
|
||||
</record>
|
||||
<record>
|
||||
<prefix>0800::/5</prefix>
|
||||
<description>Reserved by IETF</description>
|
||||
<xref type="rfc" data="rfc3513"/><xref type="rfc" data="rfc4291"/>
|
||||
<notes/>
|
||||
</record>
|
||||
<record>
|
||||
<prefix>1000::/4</prefix>
|
||||
<description>Reserved by IETF</description>
|
||||
<xref type="rfc" data="rfc3513"/><xref type="rfc" data="rfc4291"/>
|
||||
<notes/>
|
||||
</record>
|
||||
<record>
|
||||
<prefix>2000::/3</prefix>
|
||||
<description>Global Unicast</description>
|
||||
<xref type="rfc" data="rfc3513"/><xref type="rfc" data="rfc4291"/>
|
||||
<notes>The IPv6 Unicast space encompasses the entire IPv6 address range
|
||||
with the exception of ff00::/8, per <xref type="rfc" data="rfc4291"/>. IANA unicast address
|
||||
assignments are currently limited to the IPv6 unicast address
|
||||
range of 2000::/3. IANA assignments from this block are registered
|
||||
in <xref type="registry" data="ipv6-unicast-address-assignments"/>.
|
||||
<xref type="note" data="7"/>
|
||||
<xref type="note" data="8"/>
|
||||
<xref type="note" data="9"/>
|
||||
<xref type="note" data="10"/>
|
||||
<xref type="note" data="11"/>
|
||||
<xref type="note" data="12"/>
|
||||
<xref type="note" data="13"/>
|
||||
<xref type="note" data="14"/>
|
||||
<xref type="note" data="15"/>
|
||||
</notes>
|
||||
</record>
|
||||
<record>
|
||||
<prefix>4000::/3</prefix>
|
||||
<description>Reserved by IETF</description>
|
||||
<xref type="rfc" data="rfc3513"/><xref type="rfc" data="rfc4291"/>
|
||||
<notes/>
|
||||
</record>
|
||||
<record>
|
||||
<prefix>6000::/3</prefix>
|
||||
<description>Reserved by IETF</description>
|
||||
<xref type="rfc" data="rfc3513"/><xref type="rfc" data="rfc4291"/>
|
||||
<notes/>
|
||||
</record>
|
||||
<record>
|
||||
<prefix>8000::/3</prefix>
|
||||
<description>Reserved by IETF</description>
|
||||
<xref type="rfc" data="rfc3513"/><xref type="rfc" data="rfc4291"/>
|
||||
<notes/>
|
||||
</record>
|
||||
<record>
|
||||
<prefix>a000::/3</prefix>
|
||||
<description>Reserved by IETF</description>
|
||||
<xref type="rfc" data="rfc3513"/><xref type="rfc" data="rfc4291"/>
|
||||
<notes/>
|
||||
</record>
|
||||
<record>
|
||||
<prefix>c000::/3</prefix>
|
||||
<description>Reserved by IETF</description>
|
||||
<xref type="rfc" data="rfc3513"/><xref type="rfc" data="rfc4291"/>
|
||||
<notes/>
|
||||
</record>
|
||||
<record>
|
||||
<prefix>e000::/4</prefix>
|
||||
<description>Reserved by IETF</description>
|
||||
<xref type="rfc" data="rfc3513"/><xref type="rfc" data="rfc4291"/>
|
||||
<notes/>
|
||||
</record>
|
||||
<record>
|
||||
<prefix>f000::/5</prefix>
|
||||
<description>Reserved by IETF</description>
|
||||
<xref type="rfc" data="rfc3513"/><xref type="rfc" data="rfc4291"/>
|
||||
<notes/>
|
||||
</record>
|
||||
<record>
|
||||
<prefix>f800::/6</prefix>
|
||||
<description>Reserved by IETF</description>
|
||||
<xref type="rfc" data="rfc3513"/><xref type="rfc" data="rfc4291"/>
|
||||
<notes/>
|
||||
</record>
|
||||
<record>
|
||||
<prefix>fc00::/7</prefix>
|
||||
<description>Unique Local Unicast</description>
|
||||
<xref type="rfc" data="rfc4193"/>
|
||||
<notes>For complete registration details, see <xref type="registry" data="iana-ipv6-special-registry"/>.</notes>
|
||||
</record>
|
||||
<record>
|
||||
<prefix>fe00::/9</prefix>
|
||||
<description>Reserved by IETF</description>
|
||||
<xref type="rfc" data="rfc3513"/><xref type="rfc" data="rfc4291"/>
|
||||
<notes/>
|
||||
</record>
|
||||
<record>
|
||||
<prefix>fe80::/10</prefix>
|
||||
<description>Link-Scoped Unicast</description>
|
||||
<xref type="rfc" data="rfc3513"/><xref type="rfc" data="rfc4291"/>
|
||||
<notes>Reserved by protocol. For authoritative registration, see <xref type="registry" data="iana-ipv6-special-registry"/>.</notes>
|
||||
</record>
|
||||
<record>
|
||||
<prefix>fec0::/10</prefix>
|
||||
<description>Reserved by IETF</description>
|
||||
<xref type="rfc" data="rfc3879"/>
|
||||
<notes>Deprecated by <xref type="rfc" data="rfc3879"/> in September 2004. Formerly a Site-Local scoped address prefix.</notes>
|
||||
</record>
|
||||
<record>
|
||||
<prefix>ff00::/8</prefix>
|
||||
<description>Multicast</description>
|
||||
<xref type="rfc" data="rfc3513"/><xref type="rfc" data="rfc4291"/>
|
||||
<notes>IANA assignments from this block are registered in <xref type="registry" data="ipv6-multicast-addresses"/>.</notes>
|
||||
</record>
|
||||
<footnote anchor="1">::1/128 reserved for Loopback Address <xref type="rfc" data="rfc4291"/>.
|
||||
Reserved by protocol. For authoritative registration, see <xref type="registry" data="iana-ipv6-special-registry"/>.</footnote>
|
||||
<footnote anchor="2">::/128 reserved for Unspecified Address <xref type="rfc" data="rfc4291"/>.
|
||||
Reserved by protocol. For authoritative registration, see <xref type="registry" data="iana-ipv6-special-registry"/>.</footnote>
|
||||
<footnote anchor="3">::ffff:0:0/96 reserved for IPv4-mapped Address <xref type="rfc" data="rfc4291"/>.
|
||||
Reserved by protocol. For authoritative registration, see <xref type="registry" data="iana-ipv6-special-registry"/>.</footnote>
|
||||
<footnote anchor="4">0::/96 deprecated by <xref type="rfc" data="rfc4291"/>. Formerly defined as the "IPv4-compatible IPv6 address" prefix.</footnote>
|
||||
<footnote anchor="5">The "Well Known Prefix" 64:ff9b::/96 is used in an algorithmic mapping between IPv4 to IPv6 addresses <xref type="rfc" data="rfc6052"/>.</footnote>
|
||||
<footnote anchor="6">64:ff9b:1::/48 reserved for Local-Use IPv4/IPv6 Translation <xref type="rfc" data="rfc8215"/>.</footnote>
|
||||
<footnote anchor="7">2001:0::/23 reserved for IETF Protocol Assignments <xref type="rfc" data="rfc2928"/>.
|
||||
For complete registration details, see <xref type="registry" data="iana-ipv6-special-registry"/>.</footnote>
|
||||
<footnote anchor="8">2001:0::/32 reserved for TEREDO <xref type="rfc" data="rfc4380"/>.
|
||||
For complete registration details, see <xref type="registry" data="iana-ipv6-special-registry"/>.</footnote>
|
||||
<footnote anchor="9">2001:2::/48 reserved for Benchmarking <xref type="rfc" data="rfc5180"/><xref type="rfc-errata" data="1752"/>.
|
||||
For complete registration details, see <xref type="registry" data="iana-ipv6-special-registry"/>.</footnote>
|
||||
<footnote anchor="10">2001:3::/32 reserved for AMT <xref type="rfc" data="rfc7450"/>.
|
||||
For complete registration details, see <xref type="registry" data="iana-ipv6-special-registry"/>.</footnote>
|
||||
<footnote anchor="11">2001:4:112::/48 reserved for AS112-v6 <xref type="rfc" data="rfc7535"/>.
|
||||
For complete registration details, see <xref type="registry" data="iana-ipv6-special-registry"/>.</footnote>
|
||||
<footnote anchor="12">2001:10::/28 deprecated (formerly ORCHID) <xref type="rfc" data="rfc4843"/>.
|
||||
For complete registration details, see <xref type="registry" data="iana-ipv6-special-registry"/>.</footnote>
|
||||
<footnote anchor="13">2001:20::/28 reserved for ORCHIDv2 <xref type="rfc" data="rfc7343"/>.
|
||||
For complete registration details, see <xref type="registry" data="iana-ipv6-special-registry"/>.</footnote>
|
||||
<footnote anchor="14">2001:db8::/32 reserved for Documentation <xref type="rfc" data="rfc3849"/>.
|
||||
For complete registration details, see <xref type="registry" data="iana-ipv6-special-registry"/>.</footnote>
|
||||
<footnote anchor="15">2002::/16 reserved for 6to4 <xref type="rfc" data="rfc3056"/>.
|
||||
For complete registration details, see <xref type="registry" data="iana-ipv6-special-registry"/>.</footnote>
|
||||
|
||||
|
||||
<people/>
|
||||
</registry>
|
||||
</registry>
|
435
vendor/netaddr/ip/ipv6-unicast-address-assignments.xml
vendored
Normal file
435
vendor/netaddr/ip/ipv6-unicast-address-assignments.xml
vendored
Normal file
@@ -0,0 +1,435 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<?xml-stylesheet type="text/xsl" href="ipv6-unicast-address-assignments.xsl"?>
|
||||
<?oxygen RNGSchema="ipv6-unicast-address-assignments.rng" type="xml"?>
|
||||
<registry xmlns="http://www.iana.org/assignments" id="ipv6-unicast-address-assignments">
|
||||
<title>IPv6 Global Unicast Address Assignments</title>
|
||||
<category>Internet Protocol version 6 (IPv6) Global Unicast Allocations</category>
|
||||
<updated>2019-11-06</updated>
|
||||
<xref type="rfc" data="rfc7249"/>
|
||||
<registration_rule>Allocations to RIRs are made in line with the Global Policy published at
|
||||
<xref type="uri" data="http://www.icann.org/en/resources/policy/global-addressing"/>.
|
||||
All other assignments require IETF Review.</registration_rule>
|
||||
<description>The allocation of Internet Protocol version 6 (IPv6) unicast address space is listed
|
||||
here. References to the various other registries detailing the use of the IPv6 address
|
||||
space can be found in the <xref type="registry" data="ipv6-address-space">IPv6 Address Space registry</xref>.</description>
|
||||
<note>The assignable Global Unicast Address space is defined in <xref type="rfc" data="rfc3513"/> as the address block
|
||||
defined by the prefix 2000::/3. <xref type="rfc" data="rfc3513"/> was later obsoleted by <xref type="rfc" data="rfc4291"/>. All address
|
||||
space in this block not listed in the table below is reserved by IANA for future
|
||||
allocation.
|
||||
</note>
|
||||
<record date="1999-07-01">
|
||||
<prefix>2001:0000::/23</prefix>
|
||||
<description>IANA</description>
|
||||
<whois>whois.iana.org</whois>
|
||||
<status>ALLOCATED</status>
|
||||
<notes>2001:0000::/23 is reserved for IETF Protocol Assignments <xref type="rfc" data="rfc2928"/>.
|
||||
2001:0000::/32 is reserved for TEREDO <xref type="rfc" data="rfc4380"/>.
|
||||
2001:1::1/128 is reserved for Port Control Protocol Anycast <xref type="rfc" data="rfc7723"/>.
|
||||
2001:2::/48 is reserved for Benchmarking <xref type="rfc" data="rfc5180"/><xref type="rfc-errata" data="1752"/>.
|
||||
2001:3::/32 is reserved for AMT <xref type="rfc" data="rfc7450"/>.
|
||||
2001:4:112::/48 is reserved for AS112-v6 <xref type="rfc" data="rfc7535"/>.
|
||||
2001:10::/28 is deprecated (previously ORCHID) <xref type="rfc" data="rfc4843"/>.
|
||||
2001:20::/28 is reserved for ORCHIDv2 <xref type="rfc" data="rfc7343"/>.
|
||||
2001:db8::/32 is reserved for Documentation <xref type="rfc" data="rfc3849"/>.
|
||||
For complete registration details, see <xref type="registry" data="iana-ipv6-special-registry"/>.</notes>
|
||||
</record>
|
||||
<record date="1999-07-01">
|
||||
<prefix>2001:0200::/23</prefix>
|
||||
<description>APNIC</description>
|
||||
<whois>whois.apnic.net</whois>
|
||||
<status>ALLOCATED</status>
|
||||
<rdap>
|
||||
<server>https://rdap.apnic.net/</server>
|
||||
</rdap>
|
||||
<notes/>
|
||||
</record>
|
||||
<record date="1999-07-01">
|
||||
<prefix>2001:0400::/23</prefix>
|
||||
<description>ARIN</description>
|
||||
<whois>whois.arin.net</whois>
|
||||
<status>ALLOCATED</status>
|
||||
<rdap>
|
||||
<server>https://rdap.arin.net/registry</server>
|
||||
<server>http://rdap.arin.net/registry</server>
|
||||
</rdap>
|
||||
<notes/>
|
||||
</record>
|
||||
<record date="1999-07-01">
|
||||
<prefix>2001:0600::/23</prefix>
|
||||
<description>RIPE NCC</description>
|
||||
<whois>whois.ripe.net</whois>
|
||||
<status>ALLOCATED</status>
|
||||
<rdap>
|
||||
<server>https://rdap.db.ripe.net/</server>
|
||||
</rdap>
|
||||
<notes/>
|
||||
</record>
|
||||
|
||||
<record date="2002-11-02">
|
||||
<prefix>2001:0800::/22</prefix>
|
||||
<description>RIPE NCC</description>
|
||||
<whois>whois.ripe.net</whois>
|
||||
<status>ALLOCATED</status>
|
||||
<rdap>
|
||||
<server>https://rdap.db.ripe.net/</server>
|
||||
</rdap>
|
||||
<notes>2001:0800::/23 was allocated on 2002-05-02. The more recent
|
||||
allocation (2002-11-02) incorporates the previous allocation.</notes>
|
||||
</record>
|
||||
<record date="2002-05-02">
|
||||
<prefix>2001:0c00::/23</prefix>
|
||||
<description>APNIC</description>
|
||||
<whois>whois.apnic.net</whois>
|
||||
<status>ALLOCATED</status>
|
||||
<rdap>
|
||||
<server>https://rdap.apnic.net/</server>
|
||||
</rdap>
|
||||
<notes>2001:db8::/32 reserved for Documentation <xref type="rfc" data="rfc3849"/>.
|
||||
For complete registration details, see <xref type="registry" data="iana-ipv6-special-registry"/>.</notes>
|
||||
</record>
|
||||
<record date="2003-01-01">
|
||||
<prefix>2001:0e00::/23</prefix>
|
||||
<description>APNIC</description>
|
||||
<whois>whois.apnic.net</whois>
|
||||
<status>ALLOCATED</status>
|
||||
<rdap>
|
||||
<server>https://rdap.apnic.net/</server>
|
||||
</rdap>
|
||||
<notes/>
|
||||
</record>
|
||||
<record date="2002-11-01">
|
||||
<prefix>2001:1200::/23</prefix>
|
||||
<description>LACNIC</description>
|
||||
<whois>whois.lacnic.net</whois>
|
||||
<status>ALLOCATED</status>
|
||||
<rdap>
|
||||
<server>https://rdap.lacnic.net/rdap/</server>
|
||||
</rdap>
|
||||
<notes/>
|
||||
</record>
|
||||
|
||||
<record date="2003-07-01">
|
||||
<prefix>2001:1400::/22</prefix>
|
||||
<description>RIPE NCC</description>
|
||||
<whois>whois.ripe.net</whois>
|
||||
<status>ALLOCATED</status>
|
||||
<rdap>
|
||||
<server>https://rdap.db.ripe.net/</server>
|
||||
</rdap>
|
||||
<notes>2001:1400::/23 was allocated on 2003-02-01. The more recent
|
||||
allocation (2003-07-01) incorporates the previous allocation.</notes>
|
||||
</record>
|
||||
<record date="2003-04-01">
|
||||
<prefix>2001:1800::/23</prefix>
|
||||
<description>ARIN</description>
|
||||
<whois>whois.arin.net</whois>
|
||||
<status>ALLOCATED</status>
|
||||
<rdap>
|
||||
<server>https://rdap.arin.net/registry</server>
|
||||
<server>http://rdap.arin.net/registry</server>
|
||||
</rdap>
|
||||
<notes/>
|
||||
</record>
|
||||
<record date="2004-01-01">
|
||||
<prefix>2001:1a00::/23</prefix>
|
||||
<description>RIPE NCC</description>
|
||||
<whois>whois.ripe.net</whois>
|
||||
<status>ALLOCATED</status>
|
||||
<rdap>
|
||||
<server>https://rdap.db.ripe.net/</server>
|
||||
</rdap>
|
||||
<notes/>
|
||||
</record>
|
||||
<record date="2004-05-04">
|
||||
<prefix>2001:1c00::/22</prefix>
|
||||
<description>RIPE NCC</description>
|
||||
<whois>whois.ripe.net</whois>
|
||||
<status>ALLOCATED</status>
|
||||
<rdap>
|
||||
<server>https://rdap.db.ripe.net/</server>
|
||||
</rdap>
|
||||
<notes/>
|
||||
</record>
|
||||
|
||||
<record date="2019-03-12">
|
||||
<prefix>2001:2000::/19</prefix>
|
||||
<description>RIPE NCC</description>
|
||||
<whois>whois.ripe.net</whois>
|
||||
<status>ALLOCATED</status>
|
||||
<rdap>
|
||||
<server>https://rdap.db.ripe.net/</server>
|
||||
</rdap>
|
||||
<notes>2001:2000::/20, 2001:3000::/21, and 2001:3800::/22
|
||||
were allocated on 2004-05-04. The more recent allocation
|
||||
(2019-03-12) incorporates all these previous allocations.</notes>
|
||||
</record>
|
||||
<record date="2004-06-11">
|
||||
<prefix>2001:4000::/23</prefix>
|
||||
<description>RIPE NCC</description>
|
||||
<whois>whois.ripe.net</whois>
|
||||
<status>ALLOCATED</status>
|
||||
<rdap>
|
||||
<server>https://rdap.db.ripe.net/</server>
|
||||
</rdap>
|
||||
<notes/>
|
||||
</record>
|
||||
<record date="2004-06-01">
|
||||
<prefix>2001:4200::/23</prefix>
|
||||
<description>AFRINIC</description>
|
||||
<whois>whois.afrinic.net</whois>
|
||||
<status>ALLOCATED</status>
|
||||
<rdap>
|
||||
<server>https://rdap.afrinic.net/rdap/</server>
|
||||
<server>http://rdap.afrinic.net/rdap/</server>
|
||||
</rdap>
|
||||
<notes/>
|
||||
</record>
|
||||
<record date="2004-06-11">
|
||||
<prefix>2001:4400::/23</prefix>
|
||||
<description>APNIC</description>
|
||||
<whois>whois.apnic.net</whois>
|
||||
<status>ALLOCATED</status>
|
||||
<rdap>
|
||||
<server>https://rdap.apnic.net/</server>
|
||||
</rdap>
|
||||
<notes/>
|
||||
</record>
|
||||
<record date="2004-08-17">
|
||||
<prefix>2001:4600::/23</prefix>
|
||||
<description>RIPE NCC</description>
|
||||
<whois>whois.ripe.net</whois>
|
||||
<status>ALLOCATED</status>
|
||||
<rdap>
|
||||
<server>https://rdap.db.ripe.net/</server>
|
||||
</rdap>
|
||||
<notes/>
|
||||
</record>
|
||||
<record date="2004-08-24">
|
||||
<prefix>2001:4800::/23</prefix>
|
||||
<description>ARIN</description>
|
||||
<whois>whois.arin.net</whois>
|
||||
<status>ALLOCATED</status>
|
||||
<rdap>
|
||||
<server>https://rdap.arin.net/registry</server>
|
||||
<server>http://rdap.arin.net/registry</server>
|
||||
</rdap>
|
||||
<notes/>
|
||||
</record>
|
||||
<record date="2004-10-15">
|
||||
<prefix>2001:4a00::/23</prefix>
|
||||
<description>RIPE NCC</description>
|
||||
<whois>whois.ripe.net</whois>
|
||||
<status>ALLOCATED</status>
|
||||
<rdap>
|
||||
<server>https://rdap.db.ripe.net/</server>
|
||||
</rdap>
|
||||
<notes/>
|
||||
</record>
|
||||
<record date="2004-12-17">
|
||||
<prefix>2001:4c00::/23</prefix>
|
||||
<description>RIPE NCC</description>
|
||||
<whois>whois.ripe.net</whois>
|
||||
<status>ALLOCATED</status>
|
||||
<rdap>
|
||||
<server>https://rdap.db.ripe.net/</server>
|
||||
</rdap>
|
||||
<notes/>
|
||||
</record>
|
||||
<record date="2004-09-10">
|
||||
<prefix>2001:5000::/20</prefix>
|
||||
<description>RIPE NCC</description>
|
||||
<whois>whois.ripe.net</whois>
|
||||
<status>ALLOCATED</status>
|
||||
<rdap>
|
||||
<server>https://rdap.db.ripe.net/</server>
|
||||
</rdap>
|
||||
<notes/>
|
||||
</record>
|
||||
<record date="2004-11-30">
|
||||
<prefix>2001:8000::/19</prefix>
|
||||
<description>APNIC</description>
|
||||
<whois>whois.apnic.net</whois>
|
||||
<status>ALLOCATED</status>
|
||||
<rdap>
|
||||
<server>https://rdap.apnic.net/</server>
|
||||
</rdap>
|
||||
<notes/>
|
||||
</record>
|
||||
<record date="2004-11-30">
|
||||
<prefix>2001:a000::/20</prefix>
|
||||
<description>APNIC</description>
|
||||
<whois>whois.apnic.net</whois>
|
||||
<status>ALLOCATED</status>
|
||||
<rdap>
|
||||
<server>https://rdap.apnic.net/</server>
|
||||
</rdap>
|
||||
<notes/>
|
||||
</record>
|
||||
<record date="2006-03-08">
|
||||
<prefix>2001:b000::/20</prefix>
|
||||
<description>APNIC</description>
|
||||
<whois>whois.apnic.net</whois>
|
||||
<status>ALLOCATED</status>
|
||||
<rdap>
|
||||
<server>https://rdap.apnic.net/</server>
|
||||
</rdap>
|
||||
<notes/>
|
||||
</record>
|
||||
<record date="2001-02-01">
|
||||
<prefix>2002:0000::/16</prefix>
|
||||
<description>6to4</description>
|
||||
<whois/>
|
||||
<status>ALLOCATED</status>
|
||||
<notes>2002::/16 is reserved for 6to4 <xref type="rfc" data="rfc3056"/>.
|
||||
For complete registration details, see <xref type="registry" data="iana-ipv6-special-registry"/>.</notes>
|
||||
</record>
|
||||
<record date="2005-01-12">
|
||||
<prefix>2003:0000::/18</prefix>
|
||||
<description>RIPE NCC</description>
|
||||
<whois>whois.ripe.net</whois>
|
||||
<status>ALLOCATED</status>
|
||||
<rdap>
|
||||
<server>https://rdap.db.ripe.net/</server>
|
||||
</rdap>
|
||||
<notes/>
|
||||
</record>
|
||||
<record date="2006-10-03">
|
||||
<prefix>2400:0000::/12</prefix>
|
||||
<description>APNIC</description>
|
||||
<whois>whois.apnic.net</whois>
|
||||
<status>ALLOCATED</status>
|
||||
<rdap>
|
||||
<server>https://rdap.apnic.net/</server>
|
||||
</rdap>
|
||||
<notes>2400:0000::/19 was allocated on 2005-05-20. 2400:2000::/19 was allocated on 2005-07-08. 2400:4000::/21 was
|
||||
allocated on 2005-08-08. 2404:0000::/23 was allocated on 2006-01-19. The more recent allocation (2006-10-03)
|
||||
incorporates all these previous allocations.</notes>
|
||||
</record>
|
||||
<record date="2006-10-03">
|
||||
<prefix>2600:0000::/12</prefix>
|
||||
<description>ARIN</description>
|
||||
<whois>whois.arin.net</whois>
|
||||
<status>ALLOCATED</status>
|
||||
<rdap>
|
||||
<server>https://rdap.arin.net/registry</server>
|
||||
<server>http://rdap.arin.net/registry</server>
|
||||
</rdap>
|
||||
<notes>2600:0000::/22, 2604:0000::/22, 2608:0000::/22 and 260c:0000::/22 were allocated on 2005-04-19. The more
|
||||
recent allocation (2006-10-03) incorporates all these previous allocations.</notes>
|
||||
</record>
|
||||
<record date="2005-11-17">
|
||||
<prefix>2610:0000::/23</prefix>
|
||||
<description>ARIN</description>
|
||||
<whois>whois.arin.net</whois>
|
||||
<status>ALLOCATED</status>
|
||||
<rdap>
|
||||
<server>https://rdap.arin.net/registry</server>
|
||||
<server>http://rdap.arin.net/registry</server>
|
||||
</rdap>
|
||||
<notes/>
|
||||
</record>
|
||||
<record date="2006-09-12">
|
||||
<prefix>2620:0000::/23</prefix>
|
||||
<description>ARIN</description>
|
||||
<whois>whois.arin.net</whois>
|
||||
<status>ALLOCATED</status>
|
||||
<rdap>
|
||||
<server>https://rdap.arin.net/registry</server>
|
||||
<server>http://rdap.arin.net/registry</server>
|
||||
</rdap>
|
||||
<notes/>
|
||||
</record>
|
||||
<record date="2019-11-06">
|
||||
<prefix>2630:0000::/12</prefix>
|
||||
<description>ARIN</description>
|
||||
<whois>whois.arin.net</whois>
|
||||
<status>ALLOCATED</status>
|
||||
<rdap>
|
||||
<server>https://rdap.arin.net/registry</server>
|
||||
<server>http://rdap.arin.net/registry</server>
|
||||
</rdap>
|
||||
<notes/>
|
||||
</record>
|
||||
<record date="2006-10-03">
|
||||
<prefix>2800:0000::/12</prefix>
|
||||
<description>LACNIC</description>
|
||||
<whois>whois.lacnic.net</whois>
|
||||
<status>ALLOCATED</status>
|
||||
<rdap>
|
||||
<server>https://rdap.lacnic.net/rdap/</server>
|
||||
</rdap>
|
||||
<notes>2800:0000::/23 was allocated on 2005-11-17. The more recent allocation (2006-10-03) incorporates the
|
||||
previous allocation.</notes>
|
||||
</record>
|
||||
<record date="2006-10-03">
|
||||
<prefix>2a00:0000::/12</prefix>
|
||||
<description>RIPE NCC</description>
|
||||
<whois>whois.ripe.net</whois>
|
||||
<status>ALLOCATED</status>
|
||||
<rdap>
|
||||
<server>https://rdap.db.ripe.net/</server>
|
||||
</rdap>
|
||||
<notes>2a00:0000::/21 was originally allocated on 2005-04-19. 2a01:0000::/23 was allocated on 2005-07-14.
|
||||
2a01:0000::/16 (incorporating the 2a01:0000::/23) was allocated on 2005-12-15. The more recent allocation
|
||||
(2006-10-03) incorporates these previous allocations.</notes>
|
||||
</record>
|
||||
<record date="2019-06-05">
|
||||
<prefix>2a10:0000::/12</prefix>
|
||||
<description>RIPE NCC</description>
|
||||
<whois>whois.ripe.net</whois>
|
||||
<status>ALLOCATED</status>
|
||||
<rdap>
|
||||
<server>https://rdap.db.ripe.net/</server>
|
||||
</rdap>
|
||||
<notes/>
|
||||
</record>
|
||||
<record date="2006-10-03">
|
||||
<prefix>2c00:0000::/12</prefix>
|
||||
<description>AFRINIC</description>
|
||||
<whois>whois.afrinic.net</whois>
|
||||
<status>ALLOCATED</status>
|
||||
<rdap>
|
||||
<server>https://rdap.afrinic.net/rdap/</server>
|
||||
<server>http://rdap.afrinic.net/rdap/</server>
|
||||
</rdap>
|
||||
<notes/>
|
||||
</record>
|
||||
<record date="1999-07-01">
|
||||
<prefix>2d00:0000::/8</prefix>
|
||||
<description>IANA</description>
|
||||
<whois/>
|
||||
<status>RESERVED</status>
|
||||
<notes/>
|
||||
</record>
|
||||
<record date="1999-07-01">
|
||||
<prefix>2e00:0000::/7</prefix>
|
||||
<description>IANA</description>
|
||||
<whois/>
|
||||
<status>RESERVED</status>
|
||||
<notes/>
|
||||
</record>
|
||||
<record date="1999-07-01">
|
||||
<prefix>3000:0000::/4</prefix>
|
||||
<description>IANA</description>
|
||||
<whois/>
|
||||
<status>RESERVED</status>
|
||||
<notes/>
|
||||
</record>
|
||||
<record date="2008-04">
|
||||
<prefix>3ffe::/16</prefix>
|
||||
<description>IANA</description>
|
||||
<whois/>
|
||||
<status>RESERVED</status>
|
||||
<notes>3ffe:831f::/32 was used for Teredo in some old but widely distributed networking stacks. This usage is
|
||||
deprecated in favor of 2001::/32, which was allocated for the purpose in <xref type="rfc" data="rfc4380"/>.
|
||||
3ffe::/16 and 5f00::/8 were used for the 6bone but were returned. <xref type="rfc" data="rfc5156"/></notes>
|
||||
</record>
|
||||
<record date="2008-04">
|
||||
<prefix>5f00::/8</prefix>
|
||||
<description>IANA</description>
|
||||
<whois/>
|
||||
<status>RESERVED</status>
|
||||
<notes>3ffe::/16 and 5f00::/8 were used for the 6bone but were returned. <xref type="rfc" data="rfc5156"/></notes>
|
||||
</record>
|
||||
<people/>
|
||||
</registry>
|
4441
vendor/netaddr/ip/multicast-addresses.xml
vendored
Normal file
4441
vendor/netaddr/ip/multicast-addresses.xml
vendored
Normal file
File diff suppressed because it is too large
Load Diff
117
vendor/netaddr/ip/nmap.py
vendored
Normal file
117
vendor/netaddr/ip/nmap.py
vendored
Normal file
@@ -0,0 +1,117 @@
|
||||
#-----------------------------------------------------------------------------
|
||||
# Copyright (c) 2008 by David P. D. Moss. All rights reserved.
|
||||
#
|
||||
# Released under the BSD license. See the LICENSE file for details.
|
||||
#-----------------------------------------------------------------------------
|
||||
"""
|
||||
Routines for dealing with nmap-style IPv4 address ranges.
|
||||
|
||||
Based on nmap's Target Specification :-
|
||||
|
||||
http://nmap.org/book/man-target-specification.html
|
||||
"""
|
||||
|
||||
from netaddr.core import AddrFormatError
|
||||
from netaddr.ip import IPAddress, IPNetwork
|
||||
from netaddr.compat import _iter_range, _is_str, _iter_next
|
||||
|
||||
|
||||
def _nmap_octet_target_values(spec):
|
||||
# Generates sequence of values for an individual octet as defined in the
|
||||
# nmap Target Specification.
|
||||
values = set()
|
||||
|
||||
for element in spec.split(','):
|
||||
if '-' in element:
|
||||
left, right = element.split('-', 1)
|
||||
if not left:
|
||||
left = 0
|
||||
if not right:
|
||||
right = 255
|
||||
low = int(left)
|
||||
high = int(right)
|
||||
if not ((0 <= low <= 255) and (0 <= high <= 255)):
|
||||
raise ValueError('octet value overflow for spec %s!' % (spec,))
|
||||
if low > high:
|
||||
raise ValueError('left side of hyphen must be <= right %r' % (element,))
|
||||
for octet in _iter_range(low, high + 1):
|
||||
values.add(octet)
|
||||
else:
|
||||
octet = int(element)
|
||||
if not (0 <= octet <= 255):
|
||||
raise ValueError('octet value overflow for spec %s!' % (spec,))
|
||||
values.add(octet)
|
||||
|
||||
return sorted(values)
|
||||
|
||||
|
||||
def _generate_nmap_octet_ranges(nmap_target_spec):
|
||||
# Generate 4 lists containing all octets defined by a given nmap Target
|
||||
# specification.
|
||||
if not _is_str(nmap_target_spec):
|
||||
raise TypeError('string expected, not %s' % type(nmap_target_spec))
|
||||
|
||||
if not nmap_target_spec:
|
||||
raise ValueError('nmap target specification cannot be blank!')
|
||||
|
||||
tokens = nmap_target_spec.split('.')
|
||||
|
||||
if len(tokens) != 4:
|
||||
raise AddrFormatError('invalid nmap range: %s' % (nmap_target_spec,))
|
||||
|
||||
return (_nmap_octet_target_values(tokens[0]),
|
||||
_nmap_octet_target_values(tokens[1]),
|
||||
_nmap_octet_target_values(tokens[2]),
|
||||
_nmap_octet_target_values(tokens[3]))
|
||||
|
||||
|
||||
def _parse_nmap_target_spec(target_spec):
|
||||
if '/' in target_spec:
|
||||
_, prefix = target_spec.split('/', 1)
|
||||
if not (0 < int(prefix) < 33):
|
||||
raise AddrFormatError('CIDR prefix expected, not %s' % (prefix,))
|
||||
net = IPNetwork(target_spec)
|
||||
if net.version != 4:
|
||||
raise AddrFormatError('CIDR only support for IPv4!')
|
||||
for ip in net:
|
||||
yield ip
|
||||
elif ':' in target_spec:
|
||||
# nmap only currently supports IPv6 addresses without prefixes.
|
||||
yield IPAddress(target_spec)
|
||||
else:
|
||||
octet_ranges = _generate_nmap_octet_ranges(target_spec)
|
||||
for w in octet_ranges[0]:
|
||||
for x in octet_ranges[1]:
|
||||
for y in octet_ranges[2]:
|
||||
for z in octet_ranges[3]:
|
||||
yield IPAddress("%d.%d.%d.%d" % (w, x, y, z), 4)
|
||||
|
||||
|
||||
def valid_nmap_range(target_spec):
|
||||
"""
|
||||
:param target_spec: an nmap-style IP range target specification.
|
||||
|
||||
:return: ``True`` if IP range target spec is valid, ``False`` otherwise.
|
||||
"""
|
||||
try:
|
||||
_iter_next(_parse_nmap_target_spec(target_spec))
|
||||
return True
|
||||
except (TypeError, ValueError, AddrFormatError):
|
||||
pass
|
||||
return False
|
||||
|
||||
|
||||
def iter_nmap_range(*nmap_target_spec):
|
||||
"""
|
||||
An generator that yields IPAddress objects from defined by nmap target
|
||||
specifications.
|
||||
|
||||
See https://nmap.org/book/man-target-specification.html for details.
|
||||
|
||||
:param *nmap_target_spec: one or more nmap IP range target specification.
|
||||
|
||||
:return: an iterator producing IPAddress objects for each IP in the target spec(s).
|
||||
"""
|
||||
for target_spec in nmap_target_spec:
|
||||
for addr in _parse_nmap_target_spec(target_spec):
|
||||
yield addr
|
61
vendor/netaddr/ip/rfc1924.py
vendored
Normal file
61
vendor/netaddr/ip/rfc1924.py
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
#-----------------------------------------------------------------------------
|
||||
# Copyright (c) 2008 by David P. D. Moss. All rights reserved.
|
||||
#
|
||||
# Released under the BSD license. See the LICENSE file for details.
|
||||
#-----------------------------------------------------------------------------
|
||||
"""A basic implementation of RFC 1924 ;-)"""
|
||||
|
||||
from netaddr.core import AddrFormatError
|
||||
from netaddr.ip import IPAddress
|
||||
|
||||
from netaddr.compat import _zip
|
||||
|
||||
|
||||
def chr_range(low, high):
|
||||
"""Returns all characters between low and high chars."""
|
||||
return [chr(i) for i in range(ord(low), ord(high) + 1)]
|
||||
|
||||
#: Base 85 integer index to character lookup table.
|
||||
BASE_85 = (
|
||||
chr_range('0', '9') + chr_range('A', 'Z') +
|
||||
chr_range('a', 'z') +
|
||||
['!', '#', '$', '%', '&', '(', ')', '*', '+', '-', ';', '<', '=', '>',
|
||||
'?', '@', '^', '_', '`', '{', '|', '}', '~']
|
||||
)
|
||||
|
||||
#: Base 85 digit to integer lookup table.
|
||||
BASE_85_DICT = dict(_zip(BASE_85, range(0, 86)))
|
||||
|
||||
|
||||
def ipv6_to_base85(addr):
|
||||
"""Convert a regular IPv6 address to base 85."""
|
||||
ip = IPAddress(addr)
|
||||
int_val = int(ip)
|
||||
|
||||
remainder = []
|
||||
while int_val > 0:
|
||||
remainder.append(int_val % 85)
|
||||
int_val //= 85
|
||||
|
||||
encoded = ''.join([BASE_85[w] for w in reversed(remainder)])
|
||||
leading_zeroes = (20 - len(encoded)) * "0"
|
||||
return leading_zeroes + encoded
|
||||
|
||||
|
||||
def base85_to_ipv6(addr):
|
||||
"""
|
||||
Convert a base 85 IPv6 address to its hexadecimal format.
|
||||
"""
|
||||
tokens = list(addr)
|
||||
|
||||
if len(tokens) != 20:
|
||||
raise AddrFormatError('Invalid base 85 IPv6 address: %r' % (addr,))
|
||||
|
||||
result = 0
|
||||
for i, num in enumerate(reversed(tokens)):
|
||||
num = BASE_85_DICT[num]
|
||||
result += (num * 85 ** i)
|
||||
|
||||
ip = IPAddress(result, 6)
|
||||
|
||||
return str(ip)
|
748
vendor/netaddr/ip/sets.py
vendored
Normal file
748
vendor/netaddr/ip/sets.py
vendored
Normal file
@@ -0,0 +1,748 @@
|
||||
#-----------------------------------------------------------------------------
|
||||
# Copyright (c) 2008 by David P. D. Moss. All rights reserved.
|
||||
#
|
||||
# Released under the BSD license. See the LICENSE file for details.
|
||||
#-----------------------------------------------------------------------------
|
||||
"""Set based operations for IP addresses and subnets."""
|
||||
|
||||
import itertools as _itertools
|
||||
|
||||
from netaddr.ip import (IPNetwork, IPAddress, IPRange, cidr_merge,
|
||||
cidr_exclude, iprange_to_cidrs)
|
||||
|
||||
from netaddr.compat import _sys_maxint, _dict_keys, _int_type
|
||||
|
||||
|
||||
def _subtract(supernet, subnets, subnet_idx, ranges):
|
||||
"""Calculate IPSet([supernet]) - IPSet(subnets).
|
||||
|
||||
Assumptions: subnets is sorted, subnet_idx points to the first
|
||||
element in subnets that is a subnet of supernet.
|
||||
|
||||
Results are appended to the ranges parameter as tuples of in format
|
||||
(version, first, last). Return value is the first subnet_idx that
|
||||
does not point to a subnet of supernet (or len(subnets) if all
|
||||
subsequents items are a subnet of supernet).
|
||||
"""
|
||||
version = supernet._module.version
|
||||
subnet = subnets[subnet_idx]
|
||||
if subnet.first > supernet.first:
|
||||
ranges.append((version, supernet.first, subnet.first - 1))
|
||||
|
||||
subnet_idx += 1
|
||||
prev_subnet = subnet
|
||||
while subnet_idx < len(subnets):
|
||||
cur_subnet = subnets[subnet_idx]
|
||||
|
||||
if cur_subnet not in supernet:
|
||||
break
|
||||
if prev_subnet.last + 1 == cur_subnet.first:
|
||||
# two adjacent, non-mergable IPNetworks
|
||||
pass
|
||||
else:
|
||||
ranges.append((version, prev_subnet.last + 1, cur_subnet.first - 1))
|
||||
|
||||
subnet_idx += 1
|
||||
prev_subnet = cur_subnet
|
||||
|
||||
first = prev_subnet.last + 1
|
||||
last = supernet.last
|
||||
if first <= last:
|
||||
ranges.append((version, first, last))
|
||||
|
||||
return subnet_idx
|
||||
|
||||
|
||||
def _iter_merged_ranges(sorted_ranges):
|
||||
"""Iterate over sorted_ranges, merging where possible
|
||||
|
||||
Sorted ranges must be a sorted iterable of (version, first, last) tuples.
|
||||
Merging occurs for pairs like [(4, 10, 42), (4, 43, 100)] which is merged
|
||||
into (4, 10, 100), and leads to return value
|
||||
( IPAddress(10, 4), IPAddress(100, 4) ), which is suitable input for the
|
||||
iprange_to_cidrs function.
|
||||
"""
|
||||
if not sorted_ranges:
|
||||
return
|
||||
|
||||
current_version, current_start, current_stop = sorted_ranges[0]
|
||||
|
||||
for next_version, next_start, next_stop in sorted_ranges[1:]:
|
||||
if next_start == current_stop + 1 and next_version == current_version:
|
||||
# Can be merged.
|
||||
current_stop = next_stop
|
||||
continue
|
||||
# Cannot be merged.
|
||||
yield (IPAddress(current_start, current_version),
|
||||
IPAddress(current_stop, current_version))
|
||||
current_start = next_start
|
||||
current_stop = next_stop
|
||||
current_version = next_version
|
||||
yield (IPAddress(current_start, current_version),
|
||||
IPAddress(current_stop, current_version))
|
||||
|
||||
|
||||
class IPSet(object):
|
||||
"""
|
||||
Represents an unordered collection (set) of unique IP addresses and
|
||||
subnets.
|
||||
|
||||
"""
|
||||
__slots__ = ('_cidrs', '__weakref__')
|
||||
|
||||
def __init__(self, iterable=None, flags=0):
|
||||
"""
|
||||
Constructor.
|
||||
|
||||
:param iterable: (optional) an iterable containing IP addresses,
|
||||
subnets or ranges.
|
||||
|
||||
:param flags: decides which rules are applied to the interpretation
|
||||
of the addr value. See the netaddr.core namespace documentation
|
||||
for supported constant values.
|
||||
|
||||
"""
|
||||
if isinstance(iterable, IPNetwork):
|
||||
self._cidrs = {iterable.cidr: True}
|
||||
elif isinstance(iterable, IPRange):
|
||||
self._cidrs = dict.fromkeys(
|
||||
iprange_to_cidrs(iterable[0], iterable[-1]), True)
|
||||
elif isinstance(iterable, IPSet):
|
||||
self._cidrs = dict.fromkeys(iterable.iter_cidrs(), True)
|
||||
else:
|
||||
self._cidrs = {}
|
||||
if iterable is not None:
|
||||
mergeable = []
|
||||
for addr in iterable:
|
||||
if isinstance(addr, _int_type):
|
||||
addr = IPAddress(addr, flags=flags)
|
||||
mergeable.append(addr)
|
||||
|
||||
for cidr in cidr_merge(mergeable):
|
||||
self._cidrs[cidr] = True
|
||||
|
||||
def __getstate__(self):
|
||||
""":return: Pickled state of an ``IPSet`` object."""
|
||||
return tuple([cidr.__getstate__() for cidr in self._cidrs])
|
||||
|
||||
def __setstate__(self, state):
|
||||
"""
|
||||
:param state: data used to unpickle a pickled ``IPSet`` object.
|
||||
|
||||
"""
|
||||
self._cidrs = dict.fromkeys(
|
||||
(IPNetwork((value, prefixlen), version=version)
|
||||
for value, prefixlen, version in state),
|
||||
True)
|
||||
|
||||
def _compact_single_network(self, added_network):
|
||||
"""
|
||||
Same as compact(), but assume that added_network is the only change and
|
||||
that this IPSet was properly compacted before added_network was added.
|
||||
This allows to perform compaction much faster. added_network must
|
||||
already be present in self._cidrs.
|
||||
"""
|
||||
added_first = added_network.first
|
||||
added_last = added_network.last
|
||||
added_version = added_network.version
|
||||
|
||||
# Check for supernets and subnets of added_network.
|
||||
if added_network._prefixlen == added_network._module.width:
|
||||
# This is a single IP address, i.e. /32 for IPv4 or /128 for IPv6.
|
||||
# It does not have any subnets, so we only need to check for its
|
||||
# potential supernets.
|
||||
for potential_supernet in added_network.supernet():
|
||||
if potential_supernet in self._cidrs:
|
||||
del self._cidrs[added_network]
|
||||
return
|
||||
else:
|
||||
# IPNetworks from self._cidrs that are subnets of added_network.
|
||||
to_remove = []
|
||||
for cidr in self._cidrs:
|
||||
if (cidr._module.version != added_version or cidr == added_network):
|
||||
# We found added_network or some network of a different version.
|
||||
continue
|
||||
first = cidr.first
|
||||
last = cidr.last
|
||||
if first >= added_first and last <= added_last:
|
||||
# cidr is a subnet of added_network. Remember to remove it.
|
||||
to_remove.append(cidr)
|
||||
elif first <= added_first and last >= added_last:
|
||||
# cidr is a supernet of added_network. Remove added_network.
|
||||
del self._cidrs[added_network]
|
||||
# This IPSet was properly compacted before. Since added_network
|
||||
# is removed now, it must again be properly compacted -> done.
|
||||
assert (not to_remove)
|
||||
return
|
||||
for item in to_remove:
|
||||
del self._cidrs[item]
|
||||
|
||||
# Check if added_network can be merged with another network.
|
||||
|
||||
# Note that merging can only happen between networks of the same
|
||||
# prefixlen. This just leaves 2 candidates: The IPNetworks just before
|
||||
# and just after the added_network.
|
||||
# This can be reduced to 1 candidate: 10.0.0.0/24 and 10.0.1.0/24 can
|
||||
# be merged into into 10.0.0.0/23. But 10.0.1.0/24 and 10.0.2.0/24
|
||||
# cannot be merged. With only 1 candidate, we might as well make a
|
||||
# dictionary lookup.
|
||||
shift_width = added_network._module.width - added_network.prefixlen
|
||||
while added_network.prefixlen != 0:
|
||||
# figure out if the least significant bit of the network part is 0 or 1.
|
||||
the_bit = (added_network._value >> shift_width) & 1
|
||||
if the_bit:
|
||||
candidate = added_network.previous()
|
||||
else:
|
||||
candidate = added_network.next()
|
||||
|
||||
if candidate not in self._cidrs:
|
||||
# The only possible merge does not work -> merge done
|
||||
return
|
||||
# Remove added_network&candidate, add merged network.
|
||||
del self._cidrs[candidate]
|
||||
del self._cidrs[added_network]
|
||||
added_network.prefixlen -= 1
|
||||
# Be sure that we set the host bits to 0 when we move the prefixlen.
|
||||
# Otherwise, adding 255.255.255.255/32 will result in a merged
|
||||
# 255.255.255.255/24 network, but we want 255.255.255.0/24.
|
||||
shift_width += 1
|
||||
added_network._value = (added_network._value >> shift_width) << shift_width
|
||||
self._cidrs[added_network] = True
|
||||
|
||||
def compact(self):
|
||||
"""
|
||||
Compact internal list of `IPNetwork` objects using a CIDR merge.
|
||||
"""
|
||||
cidrs = cidr_merge(self._cidrs)
|
||||
self._cidrs = dict.fromkeys(cidrs, True)
|
||||
|
||||
def __hash__(self):
|
||||
"""
|
||||
Raises ``TypeError`` if this method is called.
|
||||
|
||||
.. note:: IPSet objects are not hashable and cannot be used as \
|
||||
dictionary keys or as members of other sets. \
|
||||
"""
|
||||
raise TypeError('IP sets are unhashable!')
|
||||
|
||||
def __contains__(self, ip):
|
||||
"""
|
||||
:param ip: An IP address or subnet.
|
||||
|
||||
:return: ``True`` if IP address or subnet is a member of this IP set.
|
||||
"""
|
||||
# Iterating over self._cidrs is an O(n) operation: 1000 items in
|
||||
# self._cidrs would mean 1000 loops. Iterating over all possible
|
||||
# supernets loops at most 32 times for IPv4 or 128 times for IPv6,
|
||||
# no matter how many CIDRs this object contains.
|
||||
supernet = IPNetwork(ip)
|
||||
if supernet in self._cidrs:
|
||||
return True
|
||||
while supernet._prefixlen:
|
||||
supernet._prefixlen -= 1
|
||||
if supernet in self._cidrs:
|
||||
return True
|
||||
return False
|
||||
|
||||
def __nonzero__(self):
|
||||
"""Return True if IPSet contains at least one IP, else False"""
|
||||
return bool(self._cidrs)
|
||||
|
||||
__bool__ = __nonzero__ # Python 3.x.
|
||||
|
||||
def __iter__(self):
|
||||
"""
|
||||
:return: an iterator over the IP addresses within this IP set.
|
||||
"""
|
||||
return _itertools.chain(*sorted(self._cidrs))
|
||||
|
||||
def iter_cidrs(self):
|
||||
"""
|
||||
:return: an iterator over individual IP subnets within this IP set.
|
||||
"""
|
||||
return sorted(self._cidrs)
|
||||
|
||||
def add(self, addr, flags=0):
|
||||
"""
|
||||
Adds an IP address or subnet or IPRange to this IP set. Has no effect if
|
||||
it is already present.
|
||||
|
||||
Note that where possible the IP address or subnet is merged with other
|
||||
members of the set to form more concise CIDR blocks.
|
||||
|
||||
:param addr: An IP address or subnet in either string or object form, or
|
||||
an IPRange object.
|
||||
|
||||
:param flags: decides which rules are applied to the interpretation
|
||||
of the addr value. See the netaddr.core namespace documentation
|
||||
for supported constant values.
|
||||
|
||||
"""
|
||||
if isinstance(addr, IPRange):
|
||||
new_cidrs = dict.fromkeys(
|
||||
iprange_to_cidrs(addr[0], addr[-1]), True)
|
||||
self._cidrs.update(new_cidrs)
|
||||
self.compact()
|
||||
return
|
||||
if isinstance(addr, IPNetwork):
|
||||
# Networks like 10.1.2.3/8 need to be normalized to 10.0.0.0/8
|
||||
addr = addr.cidr
|
||||
elif isinstance(addr, _int_type):
|
||||
addr = IPNetwork(IPAddress(addr, flags=flags))
|
||||
else:
|
||||
addr = IPNetwork(addr)
|
||||
|
||||
self._cidrs[addr] = True
|
||||
self._compact_single_network(addr)
|
||||
|
||||
def remove(self, addr, flags=0):
|
||||
"""
|
||||
Removes an IP address or subnet or IPRange from this IP set. Does
|
||||
nothing if it is not already a member.
|
||||
|
||||
Note that this method behaves more like discard() found in regular
|
||||
Python sets because it doesn't raise KeyError exceptions if the
|
||||
IP address or subnet is question does not exist. It doesn't make sense
|
||||
to fully emulate that behaviour here as IP sets contain groups of
|
||||
individual IP addresses as individual set members using IPNetwork
|
||||
objects.
|
||||
|
||||
:param addr: An IP address or subnet, or an IPRange.
|
||||
|
||||
:param flags: decides which rules are applied to the interpretation
|
||||
of the addr value. See the netaddr.core namespace documentation
|
||||
for supported constant values.
|
||||
|
||||
"""
|
||||
if isinstance(addr, IPRange):
|
||||
cidrs = iprange_to_cidrs(addr[0], addr[-1])
|
||||
for cidr in cidrs:
|
||||
self.remove(cidr)
|
||||
return
|
||||
|
||||
if isinstance(addr, _int_type):
|
||||
addr = IPAddress(addr, flags=flags)
|
||||
else:
|
||||
addr = IPNetwork(addr)
|
||||
|
||||
# This add() is required for address blocks provided that are larger
|
||||
# than blocks found within the set but have overlaps. e.g. :-
|
||||
#
|
||||
# >>> IPSet(['192.0.2.0/24']).remove('192.0.2.0/23')
|
||||
# IPSet([])
|
||||
#
|
||||
self.add(addr)
|
||||
|
||||
remainder = None
|
||||
matching_cidr = None
|
||||
|
||||
# Search for a matching CIDR and exclude IP from it.
|
||||
for cidr in self._cidrs:
|
||||
if addr in cidr:
|
||||
remainder = cidr_exclude(cidr, addr)
|
||||
matching_cidr = cidr
|
||||
break
|
||||
|
||||
# Replace matching CIDR with remaining CIDR elements.
|
||||
if remainder is not None:
|
||||
del self._cidrs[matching_cidr]
|
||||
for cidr in remainder:
|
||||
self._cidrs[cidr] = True
|
||||
# No call to self.compact() is needed. Removing an IPNetwork cannot
|
||||
# create mergable networks.
|
||||
|
||||
def pop(self):
|
||||
"""
|
||||
Removes and returns an arbitrary IP address or subnet from this IP
|
||||
set.
|
||||
|
||||
:return: An IP address or subnet.
|
||||
"""
|
||||
return self._cidrs.popitem()[0]
|
||||
|
||||
def isdisjoint(self, other):
|
||||
"""
|
||||
:param other: an IP set.
|
||||
|
||||
:return: ``True`` if this IP set has no elements (IP addresses
|
||||
or subnets) in common with other. Intersection *must* be an
|
||||
empty set.
|
||||
"""
|
||||
result = self.intersection(other)
|
||||
return not result
|
||||
|
||||
def copy(self):
|
||||
""":return: a shallow copy of this IP set."""
|
||||
obj_copy = self.__class__()
|
||||
obj_copy._cidrs.update(self._cidrs)
|
||||
return obj_copy
|
||||
|
||||
def update(self, iterable, flags=0):
|
||||
"""
|
||||
Update the contents of this IP set with the union of itself and
|
||||
other IP set.
|
||||
|
||||
:param iterable: an iterable containing IP addresses, subnets or ranges.
|
||||
|
||||
:param flags: decides which rules are applied to the interpretation
|
||||
of the addr value. See the netaddr.core namespace documentation
|
||||
for supported constant values.
|
||||
|
||||
"""
|
||||
if isinstance(iterable, IPSet):
|
||||
self._cidrs = dict.fromkeys(
|
||||
(ip for ip in cidr_merge(_dict_keys(self._cidrs)
|
||||
+ _dict_keys(iterable._cidrs))), True)
|
||||
return
|
||||
elif isinstance(iterable, (IPNetwork, IPRange)):
|
||||
self.add(iterable)
|
||||
return
|
||||
|
||||
if not hasattr(iterable, '__iter__'):
|
||||
raise TypeError('an iterable was expected!')
|
||||
# An iterable containing IP addresses or subnets.
|
||||
mergeable = []
|
||||
for addr in iterable:
|
||||
if isinstance(addr, _int_type):
|
||||
addr = IPAddress(addr, flags=flags)
|
||||
mergeable.append(addr)
|
||||
|
||||
for cidr in cidr_merge(_dict_keys(self._cidrs) + mergeable):
|
||||
self._cidrs[cidr] = True
|
||||
|
||||
self.compact()
|
||||
|
||||
def clear(self):
|
||||
"""Remove all IP addresses and subnets from this IP set."""
|
||||
self._cidrs = {}
|
||||
|
||||
def __eq__(self, other):
|
||||
"""
|
||||
:param other: an IP set
|
||||
|
||||
:return: ``True`` if this IP set is equivalent to the ``other`` IP set,
|
||||
``False`` otherwise.
|
||||
"""
|
||||
try:
|
||||
return self._cidrs == other._cidrs
|
||||
except AttributeError:
|
||||
return NotImplemented
|
||||
|
||||
def __ne__(self, other):
|
||||
"""
|
||||
:param other: an IP set
|
||||
|
||||
:return: ``False`` if this IP set is equivalent to the ``other`` IP set,
|
||||
``True`` otherwise.
|
||||
"""
|
||||
try:
|
||||
return self._cidrs != other._cidrs
|
||||
except AttributeError:
|
||||
return NotImplemented
|
||||
|
||||
def __lt__(self, other):
|
||||
"""
|
||||
:param other: an IP set
|
||||
|
||||
:return: ``True`` if this IP set is less than the ``other`` IP set,
|
||||
``False`` otherwise.
|
||||
"""
|
||||
if not hasattr(other, '_cidrs'):
|
||||
return NotImplemented
|
||||
|
||||
return self.size < other.size and self.issubset(other)
|
||||
|
||||
def issubset(self, other):
|
||||
"""
|
||||
:param other: an IP set.
|
||||
|
||||
:return: ``True`` if every IP address and subnet in this IP set
|
||||
is found within ``other``.
|
||||
"""
|
||||
for cidr in self._cidrs:
|
||||
if cidr not in other:
|
||||
return False
|
||||
return True
|
||||
|
||||
__le__ = issubset
|
||||
|
||||
def __gt__(self, other):
|
||||
"""
|
||||
:param other: an IP set.
|
||||
|
||||
:return: ``True`` if this IP set is greater than the ``other`` IP set,
|
||||
``False`` otherwise.
|
||||
"""
|
||||
if not hasattr(other, '_cidrs'):
|
||||
return NotImplemented
|
||||
|
||||
return self.size > other.size and self.issuperset(other)
|
||||
|
||||
def issuperset(self, other):
|
||||
"""
|
||||
:param other: an IP set.
|
||||
|
||||
:return: ``True`` if every IP address and subnet in other IP set
|
||||
is found within this one.
|
||||
"""
|
||||
if not hasattr(other, '_cidrs'):
|
||||
return NotImplemented
|
||||
|
||||
for cidr in other._cidrs:
|
||||
if cidr not in self:
|
||||
return False
|
||||
return True
|
||||
|
||||
__ge__ = issuperset
|
||||
|
||||
def union(self, other):
|
||||
"""
|
||||
:param other: an IP set.
|
||||
|
||||
:return: the union of this IP set and another as a new IP set
|
||||
(combines IP addresses and subnets from both sets).
|
||||
"""
|
||||
ip_set = self.copy()
|
||||
ip_set.update(other)
|
||||
return ip_set
|
||||
|
||||
__or__ = union
|
||||
|
||||
def intersection(self, other):
|
||||
"""
|
||||
:param other: an IP set.
|
||||
|
||||
:return: the intersection of this IP set and another as a new IP set.
|
||||
(IP addresses and subnets common to both sets).
|
||||
"""
|
||||
result_cidrs = {}
|
||||
|
||||
own_nets = sorted(self._cidrs)
|
||||
other_nets = sorted(other._cidrs)
|
||||
own_idx = 0
|
||||
other_idx = 0
|
||||
own_len = len(own_nets)
|
||||
other_len = len(other_nets)
|
||||
while own_idx < own_len and other_idx < other_len:
|
||||
own_cur = own_nets[own_idx]
|
||||
other_cur = other_nets[other_idx]
|
||||
|
||||
if own_cur == other_cur:
|
||||
result_cidrs[own_cur] = True
|
||||
own_idx += 1
|
||||
other_idx += 1
|
||||
elif own_cur in other_cur:
|
||||
result_cidrs[own_cur] = True
|
||||
own_idx += 1
|
||||
elif other_cur in own_cur:
|
||||
result_cidrs[other_cur] = True
|
||||
other_idx += 1
|
||||
else:
|
||||
# own_cur and other_cur have nothing in common
|
||||
if own_cur < other_cur:
|
||||
own_idx += 1
|
||||
else:
|
||||
other_idx += 1
|
||||
|
||||
# We ran out of networks in own_nets or other_nets. Either way, there
|
||||
# can be no further result_cidrs.
|
||||
result = IPSet()
|
||||
result._cidrs = result_cidrs
|
||||
return result
|
||||
|
||||
__and__ = intersection
|
||||
|
||||
def symmetric_difference(self, other):
|
||||
"""
|
||||
:param other: an IP set.
|
||||
|
||||
:return: the symmetric difference of this IP set and another as a new
|
||||
IP set (all IP addresses and subnets that are in exactly one
|
||||
of the sets).
|
||||
"""
|
||||
# In contrast to intersection() and difference(), we cannot construct
|
||||
# the result_cidrs easily. Some cidrs may have to be merged, e.g. for
|
||||
# IPSet(["10.0.0.0/32"]).symmetric_difference(IPSet(["10.0.0.1/32"])).
|
||||
result_ranges = []
|
||||
|
||||
own_nets = sorted(self._cidrs)
|
||||
other_nets = sorted(other._cidrs)
|
||||
own_idx = 0
|
||||
other_idx = 0
|
||||
own_len = len(own_nets)
|
||||
other_len = len(other_nets)
|
||||
while own_idx < own_len and other_idx < other_len:
|
||||
own_cur = own_nets[own_idx]
|
||||
other_cur = other_nets[other_idx]
|
||||
|
||||
if own_cur == other_cur:
|
||||
own_idx += 1
|
||||
other_idx += 1
|
||||
elif own_cur in other_cur:
|
||||
own_idx = _subtract(other_cur, own_nets, own_idx, result_ranges)
|
||||
other_idx += 1
|
||||
elif other_cur in own_cur:
|
||||
other_idx = _subtract(own_cur, other_nets, other_idx, result_ranges)
|
||||
own_idx += 1
|
||||
else:
|
||||
# own_cur and other_cur have nothing in common
|
||||
if own_cur < other_cur:
|
||||
result_ranges.append((own_cur._module.version,
|
||||
own_cur.first, own_cur.last))
|
||||
own_idx += 1
|
||||
else:
|
||||
result_ranges.append((other_cur._module.version,
|
||||
other_cur.first, other_cur.last))
|
||||
other_idx += 1
|
||||
|
||||
# If the above loop terminated because it processed all cidrs of
|
||||
# "other", then any remaining cidrs in self must be part of the result.
|
||||
while own_idx < own_len:
|
||||
own_cur = own_nets[own_idx]
|
||||
result_ranges.append((own_cur._module.version,
|
||||
own_cur.first, own_cur.last))
|
||||
own_idx += 1
|
||||
|
||||
# If the above loop terminated because it processed all cidrs of
|
||||
# self, then any remaining cidrs in "other" must be part of the result.
|
||||
while other_idx < other_len:
|
||||
other_cur = other_nets[other_idx]
|
||||
result_ranges.append((other_cur._module.version,
|
||||
other_cur.first, other_cur.last))
|
||||
other_idx += 1
|
||||
|
||||
result = IPSet()
|
||||
for start, stop in _iter_merged_ranges(result_ranges):
|
||||
cidrs = iprange_to_cidrs(start, stop)
|
||||
for cidr in cidrs:
|
||||
result._cidrs[cidr] = True
|
||||
return result
|
||||
|
||||
__xor__ = symmetric_difference
|
||||
|
||||
def difference(self, other):
|
||||
"""
|
||||
:param other: an IP set.
|
||||
|
||||
:return: the difference between this IP set and another as a new IP
|
||||
set (all IP addresses and subnets that are in this IP set but
|
||||
not found in the other.)
|
||||
"""
|
||||
result_ranges = []
|
||||
result_cidrs = {}
|
||||
|
||||
own_nets = sorted(self._cidrs)
|
||||
other_nets = sorted(other._cidrs)
|
||||
own_idx = 0
|
||||
other_idx = 0
|
||||
own_len = len(own_nets)
|
||||
other_len = len(other_nets)
|
||||
while own_idx < own_len and other_idx < other_len:
|
||||
own_cur = own_nets[own_idx]
|
||||
other_cur = other_nets[other_idx]
|
||||
|
||||
if own_cur == other_cur:
|
||||
own_idx += 1
|
||||
other_idx += 1
|
||||
elif own_cur in other_cur:
|
||||
own_idx += 1
|
||||
elif other_cur in own_cur:
|
||||
other_idx = _subtract(own_cur, other_nets, other_idx,
|
||||
result_ranges)
|
||||
own_idx += 1
|
||||
else:
|
||||
# own_cur and other_cur have nothing in common
|
||||
if own_cur < other_cur:
|
||||
result_cidrs[own_cur] = True
|
||||
own_idx += 1
|
||||
else:
|
||||
other_idx += 1
|
||||
|
||||
# If the above loop terminated because it processed all cidrs of
|
||||
# "other", then any remaining cidrs in self must be part of the result.
|
||||
while own_idx < own_len:
|
||||
result_cidrs[own_nets[own_idx]] = True
|
||||
own_idx += 1
|
||||
|
||||
for start, stop in _iter_merged_ranges(result_ranges):
|
||||
for cidr in iprange_to_cidrs(start, stop):
|
||||
result_cidrs[cidr] = True
|
||||
|
||||
result = IPSet()
|
||||
result._cidrs = result_cidrs
|
||||
return result
|
||||
|
||||
__sub__ = difference
|
||||
|
||||
def __len__(self):
|
||||
"""
|
||||
:return: the cardinality of this IP set (i.e. sum of individual IP \
|
||||
addresses). Raises ``IndexError`` if size > maxint (a Python \
|
||||
limitation). Use the .size property for subnets of any size.
|
||||
"""
|
||||
size = self.size
|
||||
if size > _sys_maxint:
|
||||
raise IndexError(
|
||||
"range contains more than %d (sys.maxint) IP addresses!"
|
||||
"Use the .size property instead." % _sys_maxint)
|
||||
return size
|
||||
|
||||
@property
|
||||
def size(self):
|
||||
"""
|
||||
The cardinality of this IP set (based on the number of individual IP
|
||||
addresses including those implicitly defined in subnets).
|
||||
"""
|
||||
return sum([cidr.size for cidr in self._cidrs])
|
||||
|
||||
def __repr__(self):
|
||||
""":return: Python statement to create an equivalent object"""
|
||||
return 'IPSet(%r)' % [str(c) for c in sorted(self._cidrs)]
|
||||
|
||||
__str__ = __repr__
|
||||
|
||||
def iscontiguous(self):
|
||||
"""
|
||||
Returns True if the members of the set form a contiguous IP
|
||||
address range (with no gaps), False otherwise.
|
||||
|
||||
:return: ``True`` if the ``IPSet`` object is contiguous.
|
||||
"""
|
||||
cidrs = self.iter_cidrs()
|
||||
if len(cidrs) > 1:
|
||||
previous = cidrs[0][0]
|
||||
for cidr in cidrs:
|
||||
if cidr[0] != previous:
|
||||
return False
|
||||
previous = cidr[-1] + 1
|
||||
return True
|
||||
|
||||
def iprange(self):
|
||||
"""
|
||||
Generates an IPRange for this IPSet, if all its members
|
||||
form a single contiguous sequence.
|
||||
|
||||
Raises ``ValueError`` if the set is not contiguous.
|
||||
|
||||
:return: An ``IPRange`` for all IPs in the IPSet.
|
||||
"""
|
||||
if self.iscontiguous():
|
||||
cidrs = self.iter_cidrs()
|
||||
if not cidrs:
|
||||
return None
|
||||
return IPRange(cidrs[0][0], cidrs[-1][-1])
|
||||
else:
|
||||
raise ValueError("IPSet is not contiguous")
|
||||
|
||||
def iter_ipranges(self):
|
||||
"""Generate the merged IPRanges for this IPSet.
|
||||
|
||||
In contrast to self.iprange(), this will work even when the IPSet is
|
||||
not contiguous. Adjacent IPRanges will be merged together, so you
|
||||
get the minimal number of IPRanges.
|
||||
"""
|
||||
sorted_ranges = [(cidr._module.version, cidr.first, cidr.last) for
|
||||
cidr in self.iter_cidrs()]
|
||||
|
||||
for start, stop in _iter_merged_ranges(sorted_ranges):
|
||||
yield IPRange(start, stop)
|
Reference in New Issue
Block a user