[tor-commits] [bridgedb/master] Add methods to bridgedb.bridges.Bridge for determining block status.
isis at torproject.org
isis at torproject.org
Sat Mar 21 02:02:58 UTC 2015
commit 444722265ad42617a051c8dd015aabb93a78a2cb
Author: Isis Lovecruft <isis at torproject.org>
Date: Sat Dec 6 01:48:43 2014 +0000
Add methods to bridgedb.bridges.Bridge for determining block status.
* ADD new methods:
addressIsBlockedIn()
transportIsBlockedIn()
isBlockedIn()
for determining which (if any) of a Bridge's pluggable transports and
addresses is blocked in a particular country.
---
lib/bridgedb/bridges.py | 158 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 158 insertions(+)
diff --git a/lib/bridgedb/bridges.py b/lib/bridgedb/bridges.py
index 640008f..17486e2 100644
--- a/lib/bridgedb/bridges.py
+++ b/lib/bridgedb/bridges.py
@@ -177,6 +177,7 @@ class PluggableTransport(object):
self.port = port
self.methodname = methodname
self.arguments = arguments
+ self._blockedIn = {}
# Because we can intitialise this class with the __init__()
# parameters, or use the ``updateFromStemTransport()`` method, we'll
@@ -432,6 +433,7 @@ class Bridge(object):
self.transports = []
self.flags = Flags()
self.hibernating = False
+ self._blockedIn = {}
self.bandwidth = None
self.bandwidthAverage = None
@@ -699,6 +701,25 @@ class Bridge(object):
return ' '.join(bridgeLine)
+ @classmethod
+ def _getBlockKey(cls, address, port):
+ """Format an **address**:**port** pair appropriately for use as a key
+ in the :data:`_blockedIn` dictionary.
+
+ :param address: An IP address of this :class:`Bridge` or one of its
+ :data:`transports`.
+ :param port: A port.
+ :rtype: str
+ :returns: A string in the form ``"ADDRESS:PORT"`` for IPv4 addresses,
+ and ``"[ADDRESS]:PORT`` for IPv6.
+ """
+ if isIPv6(str(address)):
+ key = "[%s]:%s" % (address, port)
+ else:
+ key = "%s:%s" % (address, port)
+
+ return key
+
def _getTransportForRequest(self, bridgeRequest):
"""If a transport was requested, return the correlated
:term:`Bridge Line` based upon the client identifier in the
@@ -891,6 +912,143 @@ class Bridge(object):
bridgePrefix)
return bridgeLine
+ def _addBlockByKey(self, key, countryCode):
+ """Create or append to the list of blocked countries for a **key**.
+
+ :param str key: The key to lookup in the :data:`Bridge._blockedIn`
+ dictionary. This should be in the form returned by
+ :classmethod:`_getBlockKey`.
+ :param str countryCode: A two-character country code specifier.
+ """
+ if self._blockedIn.has_key(key):
+ self._blockedIn[key].append(countryCode.lower())
+ else:
+ self._blockedIn[key] = [countryCode.lower(),]
+
+ def addressIsBlockedIn(self, countryCode, address, port):
+ """Determine if a specific (address, port) tuple is blocked in
+ **countryCode**.
+
+ :param str countryCode: A two-character country code specifier.
+ :param str address: An IP address (presumedly one used by this
+ bridge).
+ :param int port: A port.
+ :rtype: bool
+ :returns: ``True`` if the **address**:**port** pair is blocked in
+ **countryCode**, ``False`` otherwise.
+ """
+ key = self._getBlockKey(address, port)
+
+ try:
+ if countryCode.lower() in self._blockedIn[key]:
+ logging.info("Vanilla address %s of bridge %s blocked in %s."
+ % (key, self, countryCode.lower()))
+ return True
+ except KeyError:
+ return False # That address:port pair isn't blocked anywhere
+
+ return False
+
+ def transportIsBlockedIn(self, countryCode, methodname):
+ """Determine if any of a specific type of pluggable transport which
+ this bridge might be running is blocked in a specific country.
+
+ :param str countryCode: A two-character country code specifier.
+ :param str methodname: The type of pluggable transport to check,
+ i.e. ``'obfs3'``.
+ :rtype: bool
+ :returns: ``True`` if any address:port pair which this bridge is
+ running a :class:`PluggableTransport` on is blocked in
+ **countryCode**, ``False`` otherwise.
+ """
+ for pt in self.transports:
+ if pt.methodname.lower() == methodname.lower():
+ if self.addressIsBlockedIn(countryCode, pt.address, pt.port):
+ logging.info("Transport %s of bridge %s is blocked in %s."
+ % (pt.methodname, self, countryCode))
+ return True
+ return False
+
+ def isBlockedIn(self, countryCode):
+ """Determine, according to our stored bridge reachability reports, if
+ any of the address:port pairs used by this :class:`Bridge` or it's
+ :data:`transports` are blocked in **countryCode**.
+
+ :param str countryCode: A two-character country code specifier.
+ :rtype: bool
+ :returns: ``True`` if at least one address:port pair used by this
+ bridge is blocked in **countryCode**; ``False`` otherwise.
+ """
+ # Check all supported pluggable tranport types:
+ for methodname in self.supportedTransportTypes:
+ if self.transportIsBlockedIn(countryCode.lower(), methodname):
+ return True
+
+ for address, port, version in self.allVanillaAddresses:
+ if self.addressIsBlockedIn(countryCode.lower(), address, port):
+ return True
+
+ return False
+
+ def setBlockedIn(self, countryCode, address=None, port=None, methodname=None):
+ """Mark this :class:`Bridge` as being blocked in **countryCode**.
+
+ By default, if called with no parameters other than a **countryCode**,
+ we'll mark all this :class:`Bridge`'s :data:`allVanillaAddresses` and
+ :data:`transports` as being blocked.
+
+ Otherwise, we'll filter on any and all parameters given.
+
+ If only a **methodname** is given, then we assume that all
+ :data:`transports` with that **methodname** are blocked in
+ **countryCode**. If the methodname is ``"vanilla"``, then we assume
+ each address in data:`allVanillaAddresses` is blocked.
+
+ :param str countryCode: A two-character country code specifier.
+ :param address: An IP address of this Bridge or one of its
+ :data:`transports`.
+ :param port: A specific port that is blocked, if available. If the
+ **port** is ``None``, then any address this :class:`Bridge` or its
+ :class:`PluggableTransport`s has that matches the given **address**
+ will be marked as block, regardless of its port. This parameter
+ is ignored unless an **address** is given.
+ :param str methodname: A :data:`PluggableTransport.methodname` to
+ match. Any remaining :class:`PluggableTransport`s from
+ :data:`transports` which matched the other parameters and now also
+ match this **methodname** will be marked as being blocked in
+ **countryCode**.
+ """
+ vanillas = self.allVanillaAddresses
+ transports = self.transports
+
+ if methodname:
+ # Don't process the vanilla if we weren't told to do so:
+ if not (methodname == 'vanilla') and not (address or port):
+ vanillas = []
+
+ transports = filter(lambda pt: methodname == pt.methodname, transports)
+
+ if address:
+ vanillas = filter(lambda ip: str(address) == str(ip[0]), vanillas)
+ transports = filter(lambda pt: str(address) == str(pt.address), transports)
+
+ if port:
+ vanillas = filter(lambda ip: int(port) == int(ip[1]), vanillas)
+ transports = filter(lambda pt: int(port) == int(pt.port), transports)
+
+ for addr, port, _ in vanillas:
+ key = self._getBlockKey(addr, port)
+ logging.info("Vanilla address %s for bridge %s is now blocked in %s."
+ % (key, self, countryCode))
+ self._addBlockByKey(key, countryCode)
+
+ for transport in transports:
+ key = self._getBlockKey(transport.address, transport.port)
+ logging.info("Transport %s %s for bridge %s is now blocked in %s."
+ % (transport.methodname, key, self, countryCode))
+ self._addBlockByKey(key, countryCode)
+ transport._blockedIn[key] = self._blockedIn[key]
+
def getDescriptorLastPublished(self):
"""Get the timestamp for when this bridge's last known server
descriptor was published.
More information about the tor-commits
mailing list