118 lines
4.0 KiB
Python
118 lines
4.0 KiB
Python
#-----------------------------------------------------------------------------
|
|
# 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
|