[tor-commits] [ooni-probe/master] OpenVPN connection circumvention test

art at torproject.org art at torproject.org
Fri Apr 29 09:42:23 UTC 2016


commit ed92669d1677ed0d180a358ef3f8d9603902154f
Author: srvetus <srvetus at users.noreply.github.com>
Date:   Thu Sep 24 01:43:14 2015 +0200

    OpenVPN connection circumvention test
    
    This test attempts to connect to an OpenVPN server specified in an
    OpenVPN config file. A HTTP request is sent if the VPN connection is
    successful.
    
    This basic test can be expanded to parse more of the output from the
    OpenVPN client. The output could provide more information about why
    a connection might be failing.
---
 ooni/nettests/third_party/openvpn.py | 117 +++++++++++++++++++++++++++++++++++
 1 file changed, 117 insertions(+)

diff --git a/ooni/nettests/third_party/openvpn.py b/ooni/nettests/third_party/openvpn.py
new file mode 100644
index 0000000..b681222
--- /dev/null
+++ b/ooni/nettests/third_party/openvpn.py
@@ -0,0 +1,117 @@
+from twisted.internet import defer, reactor
+from twisted.python import usage
+from twisted.web.client import Agent, readBody
+from ooni.templates.process import ProcessTest
+from ooni.utils import log
+from ooni.errors import handleAllFailures
+
+import distutils.spawn
+import re
+
+
+class UsageOptions(usage.Options):
+    optParameters = [
+        ['url', 'u', None, 'Specify a single URL on the OpenVPN subnet to test.'],
+        ['openvpn-config', 'c', None, 'Specify an OpenVPN configuration file.'],
+    ]
+
+
+class OpenVPNTest(ProcessTest):
+
+    """
+    This class tests OpenVPN connections.
+
+    test_openvpn_circumvent
+      Starts an OpenVPN client on Linux and determines
+      if it connects successfully to an OpenVPN server.
+      Then, it make a HTTP request for http://google.com
+      and records the response body or failure string.
+
+    """
+
+    name = "OpenVPN Client Test"
+    description = "Connects to an OpenVPN server and does a HTTP GET for the specified URL"
+    author = "srvetus "
+    version = "0.0.1"
+    timeout = 20
+    usageOptions = UsageOptions
+    requiredOptions = ['url', 'openvpn-config']
+    requiresRoot = True
+
+    def setUp(self):
+        self.bootstrapped = defer.Deferred()
+        self.command = [distutils.spawn.find_executable("openvpn")]
+        self.exited = False
+        self.url = self.localOptions.get('url')
+
+        if self.localOptions.get('openvpn-config'):
+            openvpn_config = self.localOptions.get('openvpn-config')
+            self.command.extend(['--config', openvpn_config])
+
+    def stop(self, reason=None):
+        """Stop the running OpenVPN process and close the connection"""
+        if not self.exited:
+            self.processDirector.close()
+
+            # OpenVPN needs to be sent SIGTERM to end cleanly
+            self.processDirector.transport.signalProcess('TERM')
+            self.exited = True
+
+    def handleRead(self, stdout=None, stderr=None):
+        """handleRead is called with each chunk of data from stdout and stderr
+
+        stdout only contains the latest data chunk, self.processDirector.stdout
+        contains the combined stdout data.
+        """
+
+        # Read OpenVPN output until bootstrapping succeeds or fails
+        if not self.bootstrapped.called:
+
+            # TODO: Determine other OpenVPN messages which indicate connection failure
+            if re.search(r'connect to .* failed', self.processDirector.stdout):
+                log.debug("OpenVPN connection failed")
+
+                # Bootstrapping failed
+                self.bootstrapped.errback(Exception("openvpn_connection_failed"))
+
+            # Check if OpenVPN has bootstrapped and connected successfully
+            elif "Initialization Sequence Completed" in self.processDirector.stdout:
+                log.debug("OpenVPN connection successful")
+                self.processDirector.cancelTimer()
+                self.bootstrapped.callback("bootstrapped")
+
+    def test_openvpn_circumvent(self):
+
+        def addResultToReport(result):
+            log.debug("request_successful")
+            self.report['body'] = result
+            self.report['success'] = True
+
+        def addFailureToReport(failure):
+            log.debug("failed: %s" % handleAllFailures(failure))
+            self.report['failure'] = handleAllFailures(failure)
+            self.report['success'] = False
+
+        def doRequest(noreason):
+            """Make a HTTP request over initialized VPN connection"""
+            agent = Agent(reactor)
+
+            log.debug("Doing HTTP request to the OpenVPN subnet: %s" % self.url)
+            request = agent.request("GET", self.url)
+            request.addCallback(readBody)
+            request.addCallback(addResultToReport)
+            request.addErrback(addFailureToReport)
+            return request
+
+        log.debug("Spawning OpenVPN")
+        self.d = self.run(self.command)
+
+        # Try to make a request when the OpenVPN connection successfully bootstraps
+        self.bootstrapped.addCallback(doRequest)
+
+        # Fire failure if OpenVPN connection fails
+        self.bootstrapped.addErrback(addFailureToReport)
+
+        # Close OpenVPN after each successful or failed test
+        self.bootstrapped.addBoth(self.stop)
+        return self.d





More information about the tor-commits mailing list