commit eccaec864777ea75dfea5f48db63468cc378a6c4
Author: Arturo Filastò <art(a)fuffa.org>
Date: Mon Mar 24 15:21:13 2014 +0100
Add support for adding tor version to the bridge_reachability report.
Fail with a good error code if the version is unsupported for the given bridge.
---
ooni/director.py | 4 ++-
ooni/managers.py | 7 ++--
ooni/nettests/blocking/bridge_reachability.py | 33 +++++++++----------
ooni/tests/test_onion.py | 8 +++++
ooni/utils/__init__.py | 3 --
ooni/utils/onion.py | 43 +++++++++++++++++++++++++
6 files changed, 74 insertions(+), 24 deletions(-)
diff --git a/ooni/director.py b/ooni/director.py
index ada1b82..b95639a 100644
--- a/ooni/director.py
+++ b/ooni/director.py
@@ -208,7 +208,6 @@ class Director(object):
self.activeNetTests.remove(net_test)
if len(self.activeNetTests) == 0:
self.allTestsDone.callback(None)
- self.allTestsDone = defer.Deferred()
@defer.inlineCallbacks
def startNetTest(self, net_test_loader, reporters):
@@ -219,6 +218,9 @@ class Director(object):
net_test_loader:
an instance of :class:ooni.nettest.NetTestLoader
"""
+ if self.allTestsDone.called:
+ self.allTestsDone = defer.Deferred()
+
if config.privacy.includepcap:
if not config.reports.pcap:
config.reports.pcap = config.generate_pcap_filename(net_test_loader.testDetails)
diff --git a/ooni/managers.py b/ooni/managers.py
index f6af282..cd05d42 100644
--- a/ooni/managers.py
+++ b/ooni/managers.py
@@ -30,6 +30,8 @@ class TaskManager(object):
to be re-run once all the currently scheduled tasks have run.
"""
log.err("Task %s has failed %s times" % (task, task.failures))
+ if config.advanced.debug:
+ log.exception(failure)
self._active_tasks.remove(task)
self.failures = self.failures + 1
@@ -81,10 +83,11 @@ class TaskManager(object):
"""
self._active_tasks.remove(task)
- self._fillSlots()
-
# Fires the done deferred when the task has completed
task.done.callback(result)
+
+ self._fillSlots()
+
self.succeeded(result, task)
@property
diff --git a/ooni/nettests/blocking/bridge_reachability.py b/ooni/nettests/blocking/bridge_reachability.py
index 892ed02..9e6bb9e 100644
--- a/ooni/nettests/blocking/bridge_reachability.py
+++ b/ooni/nettests/blocking/bridge_reachability.py
@@ -9,7 +9,7 @@ from twisted.internet import defer, reactor, error
import txtorcon
-from ooni.utils import log
+from ooni.utils import log, onion
from ooni import nettest
class UsageOptions(usage.Options):
@@ -35,8 +35,11 @@ class BridgeReachability(nettest.NetTestCase):
self.tor_progress = 0
self.timeout = int(self.localOptions['timeout'])
+ self.report['error'] = None
+ self.report['success'] = None
self.report['timeout'] = self.timeout
self.report['transport_name'] = 'vanilla'
+ self.report['tor_version'] = str(onion.tor_details['version'])
self.report['tor_progress'] = 0
self.report['tor_progress_tag'] = None
self.report['tor_progress_summary'] = None
@@ -90,36 +93,30 @@ class BridgeReachability(nettest.NetTestCase):
print "Failing bridges: %s" % failing_bridges
def test_full_tor_connection(self):
- def getTransport(address):
- """
- If the address of the bridge starts with a valid c identifier then
- we consider it to be a bridge.
- Returns:
- The transport_name if it's a transport.
- None if it's not a obfsproxy bridge.
- """
- transport_name = address.split(' ')[0]
- transport_name_chars = string.ascii_letters + string.digits
- if all(c in transport_name_chars for c in transport_name):
- return transport_name
- else:
- return None
-
config = txtorcon.TorConfig()
config.ControlPort = random.randint(2**14, 2**16)
config.SocksPort = random.randint(2**14, 2**16)
- transport_name = getTransport(self.bridge)
+ transport_name = onion.transport_name(self.bridge)
if transport_name and self.pyobfsproxy_bin:
config.ClientTransportPlugin = "%s exec %s managed" % (transport_name, self.pyobfsproxy_bin)
self.report['transport_name'] = transport_name
self.report['bridge_address'] = self.bridge.split(' ')[1]
elif transport_name and not self.pyobfsproxy_bin:
log.err("Unable to test bridge because pyobfsproxy is not installed")
- self.report['success'] = None
+ self.report['error'] = 'missing-pyobfsproxy'
return
else:
self.report['bridge_address'] = self.bridge.split(' ')[0]
+
+ if transport_name and transport_name == 'scramblesuit' and \
+ onion.TorVersion('0.2.5') > onion.tor_details['version']:
+ self.report['error'] = 'unsupported-tor-version'
+ return
+ elif transport_name and \
+ onion.TorVersion('0.2.4.1') > onion.tor_details['version']:
+ self.report['error'] = 'unsupported-tor-version'
+ return
config.Bridge = self.bridge
config.UseBridges = 1
diff --git a/ooni/tests/test_onion.py b/ooni/tests/test_onion.py
new file mode 100644
index 0000000..9830235
--- /dev/null
+++ b/ooni/tests/test_onion.py
@@ -0,0 +1,8 @@
+from twisted.trial import unittest
+from ooni.utils import onion
+
+class TestOnion(unittest.TestCase):
+ def test_tor_details(self):
+ assert isinstance(onion.tor_details, dict)
+ assert onion.tor_details['version']
+ assert onion.tor_details['binary']
diff --git a/ooni/utils/__init__.py b/ooni/utils/__init__.py
index 1b6a8e9..3f47bd1 100644
--- a/ooni/utils/__init__.py
+++ b/ooni/utils/__init__.py
@@ -82,7 +82,6 @@ def randomStr(length, num=True):
chars += string.digits
return ''.join(random.choice(chars) for x in range(length))
-
def pushFilenameStack(filename):
"""
Takes as input a target filename and checks to see if a file by such name
@@ -103,5 +102,3 @@ def pushFilenameStack(filename):
new_filename = "%s.%s" % (c_filename, new_idx)
os.rename(f, new_filename)
os.rename(filename, filename+".1")
-
-
diff --git a/ooni/utils/onion.py b/ooni/utils/onion.py
new file mode 100644
index 0000000..91d8a2d
--- /dev/null
+++ b/ooni/utils/onion.py
@@ -0,0 +1,43 @@
+import string
+import subprocess
+from distutils.version import LooseVersion
+
+from txtorcon.util import find_tor_binary
+
+class TorVersion(LooseVersion):
+ pass
+
+def tor_version():
+ tor_binary = find_tor_binary()
+ if not tor_binary:
+ return None
+ try:
+ proc = subprocess.Popen((tor_binary, '--version'),
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ except OSError:
+ pass
+ else:
+ stdout, _ = proc.communicate()
+ if proc.poll() == 0 and stdout != '':
+ return TorVersion(stdout.strip().split(' ')[2])
+ return None
+
+def transport_name(address):
+ """
+ If the address of the bridge starts with a valid c identifier then
+ we consider it to be a bridge.
+ Returns:
+ The transport_name if it's a transport.
+ None if it's not a obfsproxy bridge.
+ """
+ transport_name = address.split(' ')[0]
+ transport_name_chars = string.ascii_letters + string.digits
+ if all(c in transport_name_chars for c in transport_name):
+ return transport_name
+ else:
+ return None
+
+tor_details = {
+ 'binary': find_tor_binary(),
+ 'version': tor_version()
+}