[tor-commits] [bridgedb/master] Add bridgedb.parse.addr module for parsing IPs and ports.
isis at torproject.org
isis at torproject.org
Sun Jan 12 06:06:32 UTC 2014
commit 1f111e56428e89b731d832e20b7c5876af736943
Author: Isis Lovecruft <isis at torproject.org>
Date: Fri Nov 15 15:26:32 2013 +0000
Add bridgedb.parse.addr module for parsing IPs and ports.
* MOVE class bridgedb.Bridges.PortList to bridgedb.parse.addr.
---
lib/bridgedb/Bridges.py | 64 --------------
lib/bridgedb/parse/addr.py | 208 ++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 208 insertions(+), 64 deletions(-)
diff --git a/lib/bridgedb/Bridges.py b/lib/bridgedb/Bridges.py
index a11cd48..723d3c9 100644
--- a/lib/bridgedb/Bridges.py
+++ b/lib/bridgedb/Bridges.py
@@ -361,54 +361,6 @@ def parseDescFile(f, bridge_purpose='bridge'):
yield b
nickname = ip = orport = fingerprint = purpose = None
-class PortList:
- """ container class for port ranges
- """
-
- def __init__(self, *args, **kwargs):
- self.ports = set()
- self.add(*args)
-
- def _sanitycheck(self, val):
- #XXX: if debug=False this is disabled. bad!
- assert type(val) is int
- assert(0 < val <= 65535)
-
- def __contains__(self, val1):
- return val1 in self.ports
-
- def add(self, *args):
- for arg in args:
- try:
- if type(arg) is str:
- ports = set([int(p) for p in arg.split(',')][:PORTSPEC_LEN])
- [self._sanitycheck(p) for p in ports]
- self.ports.update(ports)
- if type(arg) is int:
- self._sanitycheck(arg)
- self.ports.update([arg])
- if type(arg) is PortList:
- self.add(list(arg.ports))
- except AssertionError: raise ValueError
- except ValueError: raise
-
- def __iter__(self):
- return self.ports.__iter__()
-
- def __str__(self):
- s = ""
- for p in self.ports:
- s += "".join(",%s"%p)
- return s.lstrip(",")
-
- def __repr__(self):
- return "PortList('%s')" % self.__str__()
-
- def __len__(self):
- return len(self.ports)
-
- def __getitem__(self, x):
- return list(self.ports)[x]
class ParseORAddressError(Exception):
def __init__(self):
@@ -418,22 +370,6 @@ class ParseORAddressError(Exception):
re_ipv6 = re.compile("\[([a-fA-F0-9:]+)\]:(.*$)")
re_ipv4 = re.compile("((?:\d{1,3}\.?){4}):(.*$)")
-def parseORAddressLine(line):
- address = None
- portlist = None
- # try regexp to discover ip version
- for regex in [re_ipv4, re_ipv6]:
- m = regex.match(line)
- if m:
- # get an address and portspec, or raise ParseError
- try:
- address = ipaddr.IPAddress(m.group(1))
- portlist = PortList(m.group(2))
- except (IndexError, ValueError): raise ParseORAddressError
-
- # return a valid address, portlist or raise ParseORAddressError
- if address and portlist and len(portlist): return address,portlist
- raise ParseORAddressError
class PluggableTransport:
"""
diff --git a/lib/bridgedb/parse/addr.py b/lib/bridgedb/parse/addr.py
new file mode 100644
index 0000000..6e5daa6
--- /dev/null
+++ b/lib/bridgedb/parse/addr.py
@@ -0,0 +1,208 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of BridgeDB, a Tor bridge distribution system.
+#
+# :authors: Isis Lovecruft 0xA3ADB67A2CDB8B35 <isis at torproject.org>
+# please also see AUTHORS file
+# :copyright: (c) 2013 Isis Lovecruft
+# (c) 2007-2013, The Tor Project, Inc.
+# (c) 2007-2013, all entities within the AUTHORS file
+# :license: 3-clause BSD, see included LICENSE for information
+
+"""Utilities for parsing IP addresses.
+
+** Module Overview: **
+
+..
+ parse
+ ||_ parse.addr
+ | |_ isIPAddress - Check if an arbitrary string is an IP address.
+ | |_ isIPv4 - Check if an arbitrary string is an IPv4 address.
+ | |_ isIPv6 - Check if an arbitrary string is an IPv6 address.
+ | \_ isValidIP - Check that an IP address is valid.
+ |
+ |__ :mod:`bridgedbparse.headers`
+ |__ :mod:`bridgedb.parse.options`
+ \__ :mod:`bridgedb.parse.versions`
+"""
+
+import logging
+
+import ipaddr
+
+
+class InvalidPort(ValueError):
+ """Raised when a given port number is invalid."""
+
+
+def isIPAddress(ip, compressed=True):
+ """Check if an arbitrary string is an IP address, and that it's valid.
+
+ :type ip: basestring or int
+ :param ip: The IP address to check.
+ :param boolean compressed: If True, return a string representing the
+ compressed form of the address. Otherwise, return an
+ :class:`ipaddr.IPAddress` instance.
+ :rtype: A :class:`ipaddr.IPAddress`, or a string, or False
+ :returns: The IP, as a string or a class, if it passed the
+ checks. Otherwise, returns False.
+ """
+ try:
+ ip = ipaddr.IPAddress(ip)
+ except ValueError:
+ return False
+ except Exception as error:
+ logging.exception(error)
+ else:
+ if isValidIP(ip):
+ if compressed:
+ return ip.compressed
+ else:
+ return ip
+ return False
+
+def _isIPv(version, ip):
+ """Check if an address is a certain ``version``, either IPv4 or IPv6.
+
+ :param integer version: The IPv[4|6] version to check; must be either
+ ``4`` or ``6``.
+ :type ip: basestring or int
+ :param ip: The IP address to check.
+ :rtype: boolean
+ :returns: True if the address is an IPv4 address.
+ """
+ ip = isIPAddress(ip, compressed=False)
+ if ip and (ip.version == version):
+ return True
+ return False
+
+def isIPv4(ip):
+ """Check if an address is IPv4.
+
+ :type ip: basestring or int
+ :param ip: The IP address to check.
+ :rtype: boolean
+ :returns: True if the address is an IPv4 address.
+ """
+ return _isIPv(4, ip)
+
+def isIPv6(ip):
+ """Check if an address is IPv6.
+
+ :type ip: basestring or int
+ :param ip: The IP address to check.
+ :rtype: boolean
+ :returns: True if the address is an IPv6 address.
+ """
+ return _isIPv(6, ip)
+
+def isValidIP(ipaddress):
+ """Check that an IP (v4 or v6) is public and not reserved.
+
+ The IP address, ``ip``, must not be any of the following:
+ * A link-local address, such as ``169.254.0.0/16`` or ``fe80::/64``.
+ * The address of a loopback interface, i.e. ``127.0.0.1`` or ``::1``.
+ * A multicast address, for example, ``255.255.255.0``.
+ * An unspecified address, for example ``0.0.0.0/32`` in IPv4 or
+ ``::/128`` in IPv6.
+ * A default route address, for example ``0.0.0.0/0`` or ``::/0``.
+ * Any other address within a private networks, such as the IANA
+ reserved Shared Address Space, defined in RFC6598_ as
+ ``100.64.0.0/10``.
+
+ If it is an IPv4 address, it also must not be:
+ * A reserved address vis-á-vis RFC1918_
+
+ If it is an IPv6 address, it also must not be:
+ * A "site-local", or Unique Local Address (ULA_), address vis-á-vis
+ RFC4193_ (i.e. within the ``fc00::/7`` netblock)
+
+ .. _RFC6598: https://tools.ietf.org/htmÅrfc6598
+ .. _RFC1918: https://tools.ietf.org/html/rfc1918
+ .. _ULA: https://en.wikipedia.org/wiki/Unique_local_address
+ .. _RFC4193: https://tools.ietf.org/html/rfc4193
+
+ :type ipaddress: An :class:`ipaddr.IPAddress`,
+ :class:`ipaddr.IPv4Address`, or :class:`ipaddr.IPv6Address`.
+ :param ipaddress: An IPAddress class.
+ :rtype: boolean
+ :returns: True if the address passes the checks, False otherwise.
+ """
+ if not (ipaddress.is_link_local or ipaddress.is_loopback
+ or ipaddress.is_multicast or ipaddress.is_private
+ or ipaddress.is_unspecified):
+ if (ipaddress.version == 6) and (not ipaddress.is_site_local):
+ return True
+ elif (ipaddress.version == 4) and (not ipaddress.is_reserved):
+ return True
+ return False
+
+
+class PortList(object):
+ """ container class for port ranges
+ """
+
+ #: The maximum number of allowed ports per IP address.
+ PORTSPEC_LEN = 16
+
+ def __init__(self, *args, **kwargs):
+ self.ports = set()
+ self.add(*args)
+
+ def _sanitycheck(self, port):
+ if (not isinstance(port, int)) or not (0 < port <= 65535):
+ raise InvalidPort("%s is not a valid port number!" % port)
+
+ def __contains__(self, port):
+ return port in self.ports
+
+ def add(self, *args):
+ """Add a port (or ports) to this PortList."""
+ for arg in args:
+ portlist = []
+ try:
+ if isinstance(arg, basestring):
+ ports = set([int(p)
+ for p in arg.split(',')][:self.PORTSPEC_LEN])
+ portlist.extend([p for p in ports])
+ if isinstance(arg, int):
+ portlist.extend(arg)
+ if isinstance(arg, PortList):
+ self.add(list(arg.ports))
+ except ValueError:
+ raise InvalidPort("%s is not a valid port number!" % arg)
+ except InvalidPort:
+ raise
+
+ [self._sanitycheck(port) for port in portlist]
+ self.ports.update(portlist)
+
+ def __iter__(self):
+ """Iterate through all ports in this PortList."""
+ return self.ports.__iter__()
+
+ def __str__(self):
+ """Returns a pretty string representation of this PortList."""
+ ret = []
+ for port in self.ports:
+ ret.append(',%s' % port)
+ ret = ''.join([piece for piece in ret])
+ return ret.lstrip(",")
+
+ def __repr__(self):
+ """Returns a raw depiction of this PortList."""
+ return "PortList('%s')" % self.__str__()
+
+ def __len__(self):
+ """Returns the total number of ports in this PortList."""
+ return len(self.ports)
+
+ def __getitem__(self, x):
+ """Get a port if it is in this PortList.
+
+ :raises: ValueError if ``x`` isn't in this PortList.
+ :rtype: integer
+ :returns: The port ``x``, if it is in this PortList.
+ """
+ portlist = list(self.ports)
+ return portlist[portlist.index(x)]
More information about the tor-commits
mailing list