Commit 859f9399 authored by Isis Lovecruft's avatar Isis Lovecruft
Browse files

There's still a problem with Tor's DataDirectory not being found, but it only...

There's still a problem with Tor's DataDirectory not being found, but it only asks for the consensus once per list of bridges/relays, i.e. once for every time bridget is run. This is still not optimal and should be fixed.

The CustomCircuit class needs to be called as path=[] in reconf_controller(), but this should only be relevant when testing relays.
parent b49b70eb
Loading
Loading
Loading
Loading
+80 −69
Changes for ooni/plugins/bridget.py: 80 added lines, 69 removed lines.
Original line number Original line Diff line number Diff line
@@ -16,9 +16,10 @@ from __future__ import with_statement
from zope.interface         import implements
from zope.interface         import implements
from twisted.python         import usage
from twisted.python         import usage
from twisted.plugin         import IPlugin
from twisted.plugin         import IPlugin
from twisted.internet       import reactor, error
from twisted.internet       import defer, error, reactor


import random
import random
import sys


try:
try:
    from ooni.lib.txtorcon  import CircuitListenerMixin, IStreamAttacher
    from ooni.lib.txtorcon  import CircuitListenerMixin, IStreamAttacher
@@ -61,9 +62,11 @@ class BridgetArgs(usage.Options):
    optFlags = [['random', 'x', 'Randomize control and socks ports']]
    optFlags = [['random', 'x', 'Randomize control and socks ports']]


    def postOptions(self):
    def postOptions(self):
        ## We can't test pluggable transports without bridges
        if self['transport'] and not self['bridges']:
        if self['transport'] and not self['bridges']:
            e = "Pluggable transport requires the bridges option"
            e = "Pluggable transport requires the bridges option"
            raise usage.UsageError, e
            raise usage.UsageError, e
        ## We can't use random and static port simultaneously
        if self['socks'] and self['control']:
        if self['socks'] and self['control']:
            if self['random']:
            if self['random']:
                e = "Unable to use random and specific ports simultaneously"
                e = "Unable to use random and specific ports simultaneously"
@@ -199,43 +202,6 @@ class BridgetTest(OONITest):
    options = BridgetArgs
    options = BridgetArgs
    blocking = False
    blocking = False


    def load_assets(self):
        """
        Load bridges and/or relays from files given in user options. Bridges
        should be given in the form IP:ORport. We don't want to load these as
        assets, because it's inefficient to start a Tor process for each one.
        """
        assets           = {}
        self.bridge_list = []
        self.relay_list  = []

        ## XXX fix me
        ## we should probably find a more memory nice way to load addresses,
        ## in case the files are really large
        if self.local_options:
            if self.local_options['bridges']:
                log.msg("Loading bridge information from %s ..." 
                        % self.local_options['bridges'])
                with open(self.local_options['bridges']) as bridge_file:
                    for line in bridge_file.readlines():
                        if line.startswith('#'):
                            continue
                        else:
                            self.bridge_list.append(line.replace('\n',''))
                assets.update({'bridges': self.bridge_list})

            if self.local_options['relays']:
                log.msg("Loading relay information from %s  ..."
                        % self.local_options['relays'])
                with open(options['relays']) as relay_file:
                    for line in relay_file.readlines():
                        if line.startswith('#'):
                            continue
                        else:
                            self.relay_list.append(line.replace('\n',''))
                assets.update({'relays': self.relay_list})
        return assets

    def initialize(self):
    def initialize(self):
        """
        """
        Extra initialization steps. We only want one child Tor process
        Extra initialization steps. We only want one child Tor process
@@ -257,6 +223,10 @@ class BridgetTest(OONITest):
            options             = self.local_options
            options             = self.local_options
            self.config         = TorConfig()
            self.config         = TorConfig()


            ## Don't run the experiment if we don't have anything to test
            if not options['bridges'] and not options['relays']:
                self.suicide = True

            if options['bridges']:
            if options['bridges']:
                self.config.UseBridges = 1
                self.config.UseBridges = 1
                
                
@@ -300,8 +270,42 @@ class BridgetTest(OONITest):
            self.config.ControlPort = self.control_port
            self.config.ControlPort = self.control_port
            self.config.save()
            self.config.save()


            print self.config.create_torrc()
    def load_assets(self):
            report = {'tor_config': self.config.config}
        """
        Load bridges and/or relays from files given in user options. Bridges
        should be given in the form IP:ORport. We don't want to load these as
        assets, because it's inefficient to start a Tor process for each one.
        """
        assets           = {}
        self.bridge_list = []
        self.relay_list  = []

        ## XXX fix me
        ## we should probably find a more memory nice way to load addresses,
        ## in case the files are really large
        if self.local_options:
            if self.local_options['bridges']:
                log.msg("Loading bridge information from %s ..." 
                        % self.local_options['bridges'])
                with open(self.local_options['bridges']) as bridge_file:
                    for line in bridge_file.readlines():
                        if line.startswith('#'):
                            continue
                        else:
                            self.bridge_list.append(line.replace('\n',''))
                assets.update({'bridges': self.bridge_list})

            if self.local_options['relays']:
                log.msg("Loading relay information from %s  ..."
                        % self.local_options['relays'])
                with open(options['relays']) as relay_file:
                    for line in relay_file.readlines():
                        if line.startswith('#'):
                            continue
                        else:
                            self.relay_list.append(line.replace('\n',''))
                assets.update({'relays': self.relay_list})
        return assets


    def experiment(self, args):
    def experiment(self, args):
        """
        """
@@ -317,61 +321,69 @@ class BridgetTest(OONITest):
            this has a :class:`ooni.lib.txtorcon.torcontol.TorControlProtocol
            this has a :class:`ooni.lib.txtorcon.torcontol.TorControlProtocol
            <TorControlProtocol>` instance as .protocol.
            <TorControlProtocol>` instance as .protocol.
        """
        """
        log.msg("Bridget: initiating test ... ")

        from ooni.lib.txtorcon import TorProtocolFactory, TorConfig, TorState
        from ooni.lib.txtorcon import TorProtocolFactory, TorConfig, TorState
        from ooni.lib.txtorcon import DEFAULT_VALUE, launch_tor
        from ooni.lib.txtorcon import DEFAULT_VALUE, launch_tor


        log.msg("Starting Tor ...")        

        def setup_failed(args):
            log.msg("Setup Failed.")
            report.update({'failed': args})
            reactor.stop()

        def setup_complete(proto):
            log.msg("Setup Complete: %s" % proto)
            state = TorState(proto.tor_protocol)
            state.post_bootstrap.addCallback(state_complete).addErrback(setup_failed)
            report.update({'success': args})

        def bootstrap(ctrl):
        def bootstrap(ctrl):
            """
            """
            Launch a Tor process with the TorConfig instance returned from
            Launch a Tor process with the TorConfig instance returned from
            initialize().
            initialize().
            """
            """
            conf = TorConfig(ctrl)
            conf = TorConfig(ctrl)
            conf.post_bootstrap.addCallback(setup_complete).addErrback(setup_failed)
            conf.post_bootstrap.addCallback(setup_done).addErrback(setup_fail)
            log.msg("Tor process connected, bootstrapping ...")
            log.msg("Tor process connected, bootstrapping ...")


        def updates(prog, tag, summary):
        def reconf_controller(conf, bridge):
            log.msg("%d%%: %s" % (prog, summary))

        def reconfigure_controller(conf, bridge):
            ## if bridges and relays, use one bridge then build a circuit 
            ## if bridges and relays, use one bridge then build a circuit 
            ## from three relays
            ## from three relays
            conf.Bridge = bridge
            conf.Bridge = bridge
            ## XXX do we need a SIGHUP to restart?                
            ## XXX do we need a SIGHUP to restart?                


        ## XXX see txtorcon.TorControlProtocol.add_event_listener we may not
            ## XXX see txtorcon.TorControlProtocol.add_event_listener we
        ## need full CustomCircuit class
            ## may not need full CustomCircuit class


            ## if bridges only, try one bridge at a time, but don't build
            ## if bridges only, try one bridge at a time, but don't build
            ## circuits, just return
            ## circuits, just return

            ## if relays only, build circuits from relays
            ## if relays only, build circuits from relays


        def reconfigure_failed(args):
        def reconf_fail(args):
            log.msg("Reconfiguring Tor config with args %s failed" % args)
            log.msg("Reconfiguring Tor config with args %s failed" % args)
            reactor.stop()
            reactor.stop()


        def setup_fail(args):
            log.msg("Setup Failed.")
            report.update({'failed': args})
            reactor.stop()

        def setup_done(proto):
            log.msg("Setup Complete: %s" % proto)
            state = TorState(proto.tor_protocol)
            state.post_bootstrap.addCallback(state_complete).addErrback(setup_fail)
            report.update({'success': args})

        def updates(prog, tag, summary):
            log.msg("%d%%: %s" % (prog, summary))

        if len(args) == 0:
            log.msg("Bridget needs lists of bridges and/or relays to test!")
            log.msg("Exiting ...")
            d = sys.exit()
            return d

        else:
            if len(self.bridge_list) >= 1:
            if len(self.bridge_list) >= 1:
                for bridge in self.bridge_list:
                for bridge in self.bridge_list:
                    try:
                    try:
                        print "BRIDGE IS %s" % bridge
                        print "BRIDGE IS %s" % bridge
                    reconfigure_controller(self.config, bridge)
                        reconf_controller(self.config, bridge)
                    except:
                    except:
                    reconfigure_failed(bridge)
                        reconf_fail(bridge)

            log.msg("Bridget: initiating test ... ")
            log.msg("Using the following as our torrc:\n%s" 
                    % self.config.create_torrc())
            report = {'tor_config': self.config.config}
            log.msg("Starting Tor ...")        


            ## :return: a Deferred which callbacks with a TorProcessProtocol
            ## :return: a Deferred which callbacks with a TorProcessProtocol
            ##          connected to the fully-bootstrapped Tor; this has a 
            ##          connected to the fully-bootstrapped Tor; this has a 
@@ -381,8 +393,8 @@ class BridgetTest(OONITest):
                           progress_updates=updates,
                           progress_updates=updates,
                           tor_binary=self.tor_binary)
                           tor_binary=self.tor_binary)
            d.addCallback(bootstrap, self.config)
            d.addCallback(bootstrap, self.config)
        d.addErrback(setup_failed)
            d.addErrback(setup_fail)
        ## 4 build circuits
            ## now build circuits


            #print "Tor process ID: %s" % d.transport.pid
            #print "Tor process ID: %s" % d.transport.pid
            return d
            return d
@@ -399,7 +411,6 @@ bridget = BridgetTest(None, None, None)
##
##
## TODO:
## TODO:
##       o  add option for any kwarg=arg self.config setting
##       o  add option for any kwarg=arg self.config setting
##       o  add DataDirectory option?
##       o  cleanup documentation
##       o  cleanup documentation
##       o  check if bridges are public relays
##       o  check if bridges are public relays
##       o  take bridge_desc file as input, also be able to give same
##       o  take bridge_desc file as input, also be able to give same