Open Table Of Contents

Source code for bridgedb.filters

# -*- coding: utf-8 ; test-case-name: bridgedb.test.test_filters ; -*-
#_____________________________________________________________________________
#
# This file is part of BridgeDB, a Tor bridge distribution system.
#
# :authors: Nick Mathewson <nickm@torproject.org>
#           Isis Lovecruft 0xA3ADB67A2CDB8B35 <isis@torproject.org>
#           please also see AUTHORS file
# :copyright: (c) 2007-2015, The Tor Project, Inc.
#             (c) 2013-2015, Isis Lovecruft
# :license: see LICENSE for licensing information
#_____________________________________________________________________________

"""Functions for filtering :class:`Bridges <bridgedb.bridges.Bridge>`."""

import logging

from ipaddr import IPv4Address
from ipaddr import IPv6Address

from bridgedb.parse.addr import isIPv


_cache = {}


[docs]def bySubring(hmac, assigned, total): """Create a filter function which filters for only the bridges which fall into the same **assigned** subhashring (based on the results of an **hmac** function). :type hmac: callable :param hmac: An HMAC function, i.e. as returned from :func:`bridgedb.crypto.getHMACFunc`. :param int assigned: The subring number that we wish to draw bridges from. For example, if a user is assigned to subring 2of3 based on their IP address, then this function should only return bridges which would also be assigned to subring 2of3. :param int total: The total number of subrings. :rtype: callable :returns: A filter function for :class:`Bridges <bridgedb.bridges.Bridge>`. """ logging.debug(("Creating a filter for assigning bridges to subhashring " "%s-of-%s...") % (assigned, total)) name = "-".join([str(hmac("")[:8]).encode('hex'), str(assigned), "of", str(total)]) try: return _cache[name] except KeyError: def _bySubring(bridge): position = int(hmac(bridge.identity)[:8], 16) which = (position % total) + 1 return True if which == assigned else False # The `description` attribute must contain an `=`, or else # dumpAssignments() will not work correctly. setattr(_bySubring, "description", "ring=%d" % assigned) _bySubring.__name__ = ("bySubring%sof%s" % (assigned, total)) _bySubring.name = name _cache[name] = _bySubring return _bySubring
[docs]def byFilters(filtres): """Returns a filter which filters by multiple **filtres**. :param list filtres: A list (or other iterable) of callables which some :class:`Bridges <bridgedb.bridges.Bridge>` should be filtered according to. :rtype: callable :returns: A filter function for :class:`Bridges <bridgedb.bridges.Bridge>`. """ name = [] for filtre in filtres: name.extend(filtre.name.split(" ")) name = " ".join(set(name)) try: return _cache[name] except KeyError: def _byFilters(bridge): results = [f(bridge) for f in filtres] if False in results: return False return True setattr(_byFilters, "description", " ".join([getattr(f, "description", "") for f in filtres])) _byFilters.name = name _cache[name] = _byFilters return _byFilters
[docs]def byIPv(ipVersion=None): """Return ``True`` if at least one of the **bridge**'s addresses has the specified **ipVersion**. :param int ipVersion: Either ``4`` or ``6``. """ if not ipVersion in (4, 6): ipVersion = 4 name = "ipv%d" % ipVersion try: return _cache[name] except KeyError: def _byIPv(bridge): """Determine if the **bridge** has an IPv{0} address. :type bridge: :class:`bridgedb.bridges.Bridge` :param bridge: A bridge to filter. :rtype: bool :returns: ``True`` if the **bridge** has an address with the correct IP version; ``False`` otherwise. """ if isIPv(ipVersion, bridge.address): return True else: for address, port, version in bridge.allVanillaAddresses: if version == ipVersion or isIPv(ipVersion, address): return True return False setattr(_byIPv, "description", "ip=%d" % ipVersion) _byIPv.__name__ = "byIPv%d()" % ipVersion _byIPv.func_doc = _byIPv.func_doc.format(ipVersion) _byIPv.name = name _cache[name] = _byIPv return _byIPv
byIPv4 = byIPv(4) byIPv6 = byIPv(6)
[docs]def byTransport(methodname=None, ipVersion=None): """Returns a filter function for a :class:`~bridgedb.bridges.Bridge`. The returned filter function should be called on a :class:`~bridgedb.bridges.Bridge`. It returns ``True`` if the :class:`~bridgedb.bridges.Bridge` has a :class:`~bridgedb.bridges.PluggableTransport` such that: 1. The :data:`methodname <bridgedb.bridges.PluggableTransport.methodname>` matches **methodname**, and, 2. The :attr:`bridgedb.bridges.PluggableTransport.address.version` equals the **ipVersion**. :param str methodname: A Pluggable Transport :data:`~bridgedb.bridges.PluggableTransport.methodname`. :param int ipVersion: Either ``4`` or ``6``. The IP version that the ``Bridge``'s ``PluggableTransport`` :attr:`address <bridgedb.bridges.PluggableTransport.address>` should have. :rtype: callable :returns: A filter function for :class:`Bridges <bridgedb.bridges.Bridge>`. """ if not ipVersion in (4, 6): ipVersion = 4 if not methodname: return byIPv(ipVersion) methodname = methodname.lower() name = "transport-%s ipv%d" % (methodname, ipVersion) try: return _cache[name] except KeyError: def _byTransport(bridge): for transport in bridge.transports: if transport.methodname == methodname: if transport.address.version == ipVersion: return True return False setattr(_byTransport, "description", "transport=%s" % methodname) _byTransport.__name__ = "byTransport(%s,%s)" % (methodname, ipVersion) _byTransport.name = name _cache[name] = _byTransport return _byTransport
[docs]def byNotBlockedIn(countryCode=None, methodname=None, ipVersion=4): """Returns a filter function for :class:`Bridges <bridgedb.bridges.Bridge>`. If a Pluggable Transport **methodname** was not specified, the returned filter function returns ``True`` if any of the ``Bridge``'s addresses or :class:`~bridgedb.bridges.PluggableTransport` addresses aren't blocked in **countryCode**. See :meth:`~bridgedb.bridges.Bridge.isBlockedIn`. Otherwise, if a Pluggable Transport **methodname** was specified, it returns ``True`` if the :class:`~bridgedb.bridges.Bridge` has a :class:`~bridgedb.bridges.PluggableTransport` such that: 1. The :data:`methodname <bridgedb.bridges.PluggableTransport.methodname>` matches **methodname**, 2. The :attr:`bridgedb.bridges.PluggableTransport.address.version` equals the **ipVersion**, and, 3. The :class:`~bridgedb.bridges.PluggableTransport`. :attr:`address <bridgedb.bridges.PluggableTransport.address>` isn't known to be blocked in **countryCode**. :type countryCode: str or ``None`` :param countryCode: A two-letter country code which the filtered :class:`PluggableTransports <bridgedb.bridges.PluggableTransport>` should not be blocked in. :param str methodname: A Pluggable Transport :data:`methodname <bridgedb.bridges.PluggableTransport.methodname>`. :param int ipVersion: Either ``4`` or ``6``. The IP version that the ``PluggableTransports``'s addresses should have. :rtype: callable :returns: A filter function for :class:`Bridges <bridgedb.bridges.Bridge>`. """ if not ipVersion in (4, 6): ipVersion = 4 if not countryCode: return byTransport(methodname, ipVersion) methodname = methodname.lower() if methodname else methodname countryCode = countryCode.lower() name = [] if methodname: name.append("transport-%s" % methodname) name.append("ipv%d" % ipVersion) name.append("not-blocked-in-%s" % countryCode) name = " ".join(name) try: return _cache[name] except KeyError: def _byNotBlockedIn(bridge): if not methodname: return not bridge.isBlockedIn(countryCode) elif methodname == "vanilla": if bridge.address.version == ipVersion: if not bridge.addressIsBlockedIn(countryCode, bridge.address, bridge.orPort): return True else: # Since bridge.transportIsBlockedIn() will return True if the # bridge has that type of transport AND that transport is # blocked, we can "fail fast" here by doing this faster check # before iterating over all the transports testing for the # other conditions. if bridge.transportIsBlockedIn(countryCode, methodname): return False else: for transport in bridge.transports: if transport.methodname == methodname: if transport.address.version == ipVersion: return True return False setattr(_byNotBlockedIn, "description", "unblocked=%s" % countryCode) _byNotBlockedIn.__name__ = ("byTransportNotBlockedIn(%s,%s,%s)" % (methodname, countryCode, ipVersion)) _byNotBlockedIn.name = name _cache[name] = _byNotBlockedIn return _byNotBlockedIn