[tor-commits] [oonib/master] Refactor oonib's usage of twisted application API

art at torproject.org art at torproject.org
Wed Sep 3 13:56:01 UTC 2014


commit 4d76c781a67b77d0c4115b011ee6257cf933c4ce
Author: aagbsn <aagbsn at extc.org>
Date:   Wed Aug 27 14:13:48 2014 +0000

    Refactor oonib's usage of twisted application API
---
 bin/oonib            |   16 +++---
 oonib/onion.py       |   96 ++++++++++++++++++++++++++++++++++
 oonib/oonibackend.py |   73 +++++++++++++++++++++-----
 oonib/runner.py      |  140 --------------------------------------------------
 4 files changed, 166 insertions(+), 159 deletions(-)

diff --git a/bin/oonib b/bin/oonib
index 7fb1c35..fcb0c72 100755
--- a/bin/oonib
+++ b/bin/oonib
@@ -12,6 +12,7 @@ sys.path.insert(0, os.path.abspath(os.getcwd()))
 
 from oonib import errors as e
 from oonib.config import config
+from oonib.log import LoggerFactory
 
 try:
     config.load()
@@ -36,9 +37,6 @@ except e.InvalidDeckDirectory, path:
     print "Invalid deck directory: %s" % path
     sys.exit(6)
 
-from oonib import runner
-from oonib.oonibackend import application
-
 if config.main.chroot:
     sys.argv.append('--chroot')
     sys.argv.append(config.chroot)
@@ -46,6 +44,12 @@ if config.main.chroot:
 if not config.main.nodaemon:
     sys.argv.append('-y')
 
-def runApp(config):
-    runner.OBaseRunner(config).run()
-runApp(config.main)
+from oonib.oonibackend import application
+
+from twisted.scripts._twistd_unix import UnixApplicationRunner
+class OBaseRunner(UnixApplicationRunner):
+    temporary_data_dir = None
+    def createOrGetApplication(self):
+        return application
+OBaseRunner.loggerFactory = LoggerFactory
+OBaseRunner(config.main).run()
diff --git a/oonib/onion.py b/oonib/onion.py
new file mode 100644
index 0000000..1950a41
--- /dev/null
+++ b/oonib/onion.py
@@ -0,0 +1,96 @@
+import tempfile
+from oonib import log
+from oonib.config import config
+from twisted.internet import reactor, endpoints
+import os
+
+from random import randint
+import socket
+
+from txtorcon import TCPHiddenServiceEndpoint, TorConfig
+from txtorcon import launch_tor
+
+from txtorcon import __version__ as txtorcon_version
+if tuple(map(int, txtorcon_version.split('.'))) < (0, 9, 0):
+    """
+    Fix for bug in txtorcon versions < 0.9.0 where TCPHiddenServiceEndpoint
+    listens on all interfaces by default.
+    """
+    def create_listener(self, proto):
+        self._update_onion(self.hiddenservice.dir)
+        self.tcp_endpoint = endpoints.TCP4ServerEndpoint(self.reactor,
+                                                         self.listen_port,
+                                                         interface='127.0.0.1')
+        d = self.tcp_endpoint.listen(self.protocolfactory)
+        d.addCallback(self._add_attributes).addErrback(self._retry_local_port)
+        return d
+    TCPHiddenServiceEndpoint._create_listener =  create_listener
+
+def randomFreePort(addr="127.0.0.1"):
+    """
+    Args:
+
+        addr (str): the IP address to attempt to bind to.
+
+    Returns an int representing the free port number at the moment of calling
+
+    Note: there is no guarantee that some other application will attempt to
+    bind to this port once this function has been called.
+    """
+    free = False
+    while not free:
+        port = randint(1024, 65535)
+        s = socket.socket()
+        try:
+            s.bind((addr, port))
+            free = True
+        except:
+            pass
+        s.close()
+    return port
+
+def txSetupFailed(failure):
+    log.err("Setup failed")
+    log.exception(failure)
+
+def startTor(torconfig):
+    def updates(prog, tag, summary):
+        print("%d%%: %s" % (prog, summary))
+
+    if config.main.socks_port:
+        torconfig.SocksPort = config.main.socks_port
+    if config.main.control_port:
+        torconfig.ControlPort = config.main.control_port
+    if config.main.tor2webmode:
+        torconfig.Tor2webMode = 1
+        torconfig.CircuitBuildTimeout = 60
+    if config.main.tor_datadir is None:
+        temporary_data_dir = tempfile.mkdtemp()
+        log.warn("Option 'tor_datadir' in oonib.conf is unspecified!")
+        log.warn("Using %s" % temporary_data_dir)
+        torconfig.DataDirectory = temporary_data_dir
+    else:
+        if os.path.exists(config.main.tor_datadir):
+            torconfig.DataDirectory = os.path.abspath(config.main.tor_datadir)
+        else:
+            raise Exception
+    torconfig.save()
+    if not hasattr(torconfig, 'ControlPort'):
+        control_port = int(randomFreePort())
+        torconfig.ControlPort = control_port
+        config.main.control_port = control_port
+
+    if not hasattr(torconfig, 'SocksPort'):
+        socks_port = int(randomFreePort())
+        torconfig.SocksPort = socks_port
+        config.main.socks_port = socks_port
+
+    torconfig.save()
+
+    if config.main.tor_binary is not None:
+        d = launch_tor(torconfig, reactor,
+                       tor_binary=config.main.tor_binary,
+                       progress_updates=updates)
+    else:
+        d = launch_tor(torconfig, reactor, progress_updates=updates)
+    return d
diff --git a/oonib/oonibackend.py b/oonib/oonibackend.py
index 2a9fa7d..ca51f45 100644
--- a/oonib/oonibackend.py
+++ b/oonib/oonibackend.py
@@ -7,13 +7,19 @@
 # In here we start all the test helpers that are required by ooniprobe and
 # start the report collector
 
-from twisted.application import internet, service
-from twisted.names import dns
-
+from oonib.api import ooniBackend, ooniBouncer
+from oonib.config import config
+from oonib.onion import startTor
 from oonib.testhelpers import dns_helpers, ssl_helpers
 from oonib.testhelpers import http_helpers, tcp_helpers
 
-from oonib.config import config
+import os
+
+from twisted.application import internet, service
+from twisted.internet import reactor
+from twisted.names import dns
+
+from txtorcon import TCPHiddenServiceEndpoint, TorConfig
 
 if config.main.uid and config.main.gid:
     application = service.Application('oonibackend', uid=config.main.uid,
@@ -21,28 +27,28 @@ if config.main.uid and config.main.gid:
 else:
     application = service.Application('oonibackend')
 
-serviceCollection = service.IServiceCollection(application)
+multiService = service.MultiService()
 
 if config.helpers['ssl'].port:
     print "Starting SSL helper on %s" % config.helpers['ssl'].port
     ssl_helper = internet.SSLServer(int(config.helpers['ssl'].port),
                                     http_helpers.HTTPReturnJSONHeadersHelper(),
                                     ssl_helpers.SSLContext(config))
-    ssl_helper.setServiceParent(serviceCollection)
+    multiService.addService(ssl_helper)
 
 # Start the DNS Server related services
 if config.helpers['dns'].tcp_port:
     print "Starting TCP DNS Helper on %s" % config.helpers['dns'].tcp_port
     tcp_dns_helper = internet.TCPServer(int(config.helpers['dns'].tcp_port),
                                         dns_helpers.DNSTestHelper())
-    tcp_dns_helper.setServiceParent(serviceCollection)
+    multiService.addService(tcp_dns_helper)
 
 if config.helpers['dns'].udp_port:
     print "Starting UDP DNS Helper on %s" % config.helpers['dns'].udp_port
     udp_dns_factory = dns.DNSDatagramProtocol(dns_helpers.DNSTestHelper())
     udp_dns_helper = internet.UDPServer(int(config.helpers['dns'].udp_port),
                                         udp_dns_factory)
-    udp_dns_helper.setServiceParent(serviceCollection)
+    multiService.addService(udp_dns_helper)
 
 if config.helpers['dns_discovery'].udp_port:
     print ("Starting UDP DNS Discovery Helper on %s" %
@@ -51,14 +57,14 @@ if config.helpers['dns_discovery'].udp_port:
                                            dns.DNSDatagramProtocol(
                                                dns_helpers.DNSResolverDiscovery()
                                            ))
-    udp_dns_discovery.setServiceParent(serviceCollection)
+    multiService.addService(udp_dns_discovery)
 
 if config.helpers['dns_discovery'].tcp_port:
     print ("Starting TCP DNS Discovery Helper on %s" %
            config.helpers['dns_discovery'].tcp_port)
     tcp_dns_discovery = internet.TCPServer(int(config.helpers['dns_discovery'].tcp_port),
                                            dns_helpers.DNSResolverDiscovery())
-    tcp_dns_discovery.setServiceParent(serviceCollection)
+    multiService.addService(tcp_dns_discovery)
 
 
 # XXX this needs to be ported
@@ -67,18 +73,59 @@ if config.helpers['daphn3'].port:
     print "Starting Daphn3 helper on %s" % config.helpers['daphn3'].port
     daphn3_helper = internet.TCPServer(int(config.helpers['daphn3'].port),
                                        tcp_helpers.Daphn3Server())
-    daphn3_helper.setServiceParent(serviceCollection)
+    multiService.addService(daphn3_helper)
 
 
 if config.helpers['tcp-echo'].port:
     print "Starting TCP echo helper on %s" % config.helpers['tcp-echo'].port
     tcp_echo_helper = internet.TCPServer(int(config.helpers['tcp-echo'].port),
                                          tcp_helpers.TCPEchoHelper())
-    tcp_echo_helper.setServiceParent(serviceCollection)
+    multiService.addService(tcp_echo_helper)
 
 if config.helpers['http-return-json-headers'].port:
     print "Starting HTTP return request helper on %s" % config.helpers['http-return-json-headers'].port
     http_return_request_helper = internet.TCPServer(
         int(config.helpers['http-return-json-headers'].port),
         http_helpers.HTTPReturnJSONHeadersHelper())
-    http_return_request_helper.setServiceParent(serviceCollection)
+multiService.addService(http_return_request_helper)
+
+# add the tor collector service here
+if config.main.tor_hidden_service:
+    torconfig = TorConfig()
+    d = startTor(torconfig)
+
+    def addCollector(torControlProtocol):
+        data_dir = os.path.join(torconfig.DataDirectory, 'collector')
+        collector_service = internet.StreamServerEndpointService(
+                TCPHiddenServiceEndpoint(reactor,
+                    torconfig, 80,
+                    hidden_service_dir=data_dir),
+                ooniBackend)
+        multiService.addService(collector_service)
+        collector_service.startService()
+        return torControlProtocol
+
+    d.addCallback(addCollector)
+
+    if ooniBouncer:
+        def addBouncer(torControlProtocol):
+            data_dir = os.path.join(torconfig.DataDirectory, 'bouncer')
+
+            bouncer_service = internet.StreamServerEndpointService(
+                    TCPHiddenServiceEndpoint(reactor,
+                        torconfig, 80,
+                        hidden_service_dir=data_dir),
+                    ooniBouncer)
+            multiService.addService(bouncer_service)
+            bouncer_service.startService()
+            return torControlProtocol
+
+        d.addCallback(addBouncer)
+else:
+    if ooniBouncer:
+        bouncer_service = internet.TCPServer(8888, ooniBouncer, interface="127.0.0.1")
+        multiService.addService(bouncer_service)
+        bouncer_service.startService()
+    collector_service = internet.TCPServer(8889, ooniBackend, interface="127.0.0.1")
+    multiService.addService(collector_service)
+    collector_service.startService()
diff --git a/oonib/runner.py b/oonib/runner.py
deleted file mode 100644
index 47ddc35..0000000
--- a/oonib/runner.py
+++ /dev/null
@@ -1,140 +0,0 @@
-"""
-In here we define a runner for the oonib backend system.
-"""
-
-from __future__ import print_function
-
-from distutils.version import LooseVersion
-import tempfile
-import os
-
-from shutil import rmtree
-
-from twisted.internet import reactor, endpoints
-from twisted.python.runtime import platformType
-
-from txtorcon import TCPHiddenServiceEndpoint, TorConfig
-from txtorcon import launch_tor
-
-from oonib.api import ooniBackend, ooniBouncer
-from oonib.config import config
-
-from oonib import oonibackend
-from oonib import log
-
-from txtorcon import __version__ as txtorcon_version
-if tuple(map(int, txtorcon_version.split('.'))) < (0, 9, 0):
-    """
-    Fix for bug in txtorcon versions < 0.9.0 where TCPHiddenServiceEndpoint
-    listens on all interfaces by default.
-    """
-    def create_listener(self, proto):
-        self._update_onion(self.hiddenservice.dir)
-        self.tcp_endpoint = endpoints.TCP4ServerEndpoint(self.reactor,
-                                                         self.listen_port,
-                                                         interface='127.0.0.1')
-        d = self.tcp_endpoint.listen(self.protocolfactory)
-        d.addCallback(self._add_attributes).addErrback(self._retry_local_port)
-        return d
-    TCPHiddenServiceEndpoint._create_listener =  create_listener
-
-class OBaseRunner(object):
-    pass
-
-if platformType == "win32":
-    from twisted.scripts._twistw import WindowsApplicationRunner
-
-    OBaseRunner = WindowsApplicationRunner
-    # XXX Currently we don't support windows for starting a Tor Hidden Service
-    log.warn(
-        "Apologies! We don't support starting a Tor Hidden Service on Windows.")
-
-else:
-    from twisted.scripts._twistd_unix import UnixApplicationRunner
-    class OBaseRunner(UnixApplicationRunner):
-        temporary_data_dir = None
-
-        def txSetupFailed(self, failure):
-            log.err("Setup failed")
-            log.exception(failure)
-
-        def setupHSEndpoint(self, tor_process_protocol, torconfig, endpoint):
-            endpointName = endpoint.settings['name']
-
-            def setup_complete(port):
-                if LooseVersion(txtorcon_version) >= LooseVersion('0.10.0'):
-                    onion_uri = port.address.onion_uri
-                else:
-                    onion_uri = port.onion_uri
-                print("Exposed %s Tor hidden service "
-                      "on httpo://%s" % (endpointName, onion_uri))
-
-            public_port = 80
-            data_dir = os.path.join(torconfig.DataDirectory, endpointName)
-            if LooseVersion(txtorcon_version) >= LooseVersion('0.10.0'):
-                hs_endpoint = TCPHiddenServiceEndpoint(reactor,
-                                                       torconfig,
-                                                       public_port,
-                                                       hidden_service_dir=data_dir)
-            else:
-                hs_endpoint = TCPHiddenServiceEndpoint(reactor,
-                                                       torconfig,
-                                                       public_port,
-                                                       data_dir=data_dir)
-            d = hs_endpoint.listen(endpoint)
-            d.addCallback(setup_complete)
-            d.addErrback(self.txSetupFailed)
-            return d
-
-        def startTor(self, torconfig):
-            def updates(prog, tag, summary):
-                print("%d%%: %s" % (prog, summary))
-
-            torconfig.SocksPort = config.main.socks_port
-            if config.main.tor2webmode:
-                torconfig.Tor2webMode = 1
-                torconfig.CircuitBuildTimeout = 60
-            if config.main.tor_datadir is None:
-                self.temporary_data_dir = tempfile.mkdtemp()
-                log.warn("Option 'tor_datadir' in oonib.conf is unspecified!")
-                log.warn("Using %s" % self.temporary_data_dir)
-                torconfig.DataDirectory = self.temporary_data_dir
-            else:
-                torconfig.DataDirectory = config.main.tor_datadir
-            torconfig.save()
-            if config.main.tor_binary is not None:
-                d = launch_tor(torconfig, reactor,
-                               tor_binary=config.main.tor_binary,
-                               progress_updates=updates)
-            else:
-                d = launch_tor(torconfig, reactor, progress_updates=updates)
-            return d
-
-        def postApplication(self):
-            """After the application is created, start the application and run
-            the reactor. After the reactor stops, clean up PID files and such.
-            """
-            self.startApplication(self.application)
-            # This is our addition. The rest is taken from
-            # twisted/scripts/_twistd_unix.py 12.2.0
-            if config.main.tor_hidden_service:
-                torconfig = TorConfig()
-                d = self.startTor(torconfig)
-                d.addCallback(self.setupHSEndpoint, torconfig, ooniBackend)
-                if ooniBouncer:
-                    d.addCallback(self.setupHSEndpoint, torconfig, ooniBouncer)
-            else:
-                if ooniBouncer:
-                    reactor.listenTCP(8888, ooniBouncer, interface="127.0.0.1")
-                reactor.listenTCP(8889, ooniBackend, interface="127.0.0.1")
-            self.startReactor(None, self.oldstdout, self.oldstderr)
-            self.removePID(self.config['pidfile'])
-            if self.temporary_data_dir:
-                log.msg("Removing temporary directory: %s"
-                        % self.temporary_data_dir)
-                rmtree(self.temporary_data_dir, onerror=log.err)
-
-        def createOrGetApplication(self):
-            return oonibackend.application
-
-OBaseRunner.loggerFactory = log.LoggerFactory





More information about the tor-commits mailing list