summaryrefslogtreecommitdiffstats
path: root/netaddr/ip/nmap.py
diff options
context:
space:
mode:
Diffstat (limited to 'netaddr/ip/nmap.py')
-rw-r--r--netaddr/ip/nmap.py117
1 files changed, 117 insertions, 0 deletions
diff --git a/netaddr/ip/nmap.py b/netaddr/ip/nmap.py
new file mode 100644
index 0000000..8e9e0c1
--- /dev/null
+++ b/netaddr/ip/nmap.py
@@ -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