[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [ooni-probe/master] Moving bootstrap_tor() functions to experiment()
commit 0b892accffdd5a09c5bba87b9c75adfcdc50eea2
Author: Isis Lovecruft <isis@xxxxxxxxxxxxxx>
Date: Tue Sep 11 11:38:45 2012 +0000
Moving bootstrap_tor() functions to experiment()
---
ooni/assets/bridgetests.txt | 8 +-
ooni/plugins/bridget.py | 362 ++++++++++++++++++++++++++++++++++++------
2 files changed, 315 insertions(+), 55 deletions(-)
diff --git a/ooni/assets/bridgetests.txt b/ooni/assets/bridgetests.txt
index c837988..7bba841 100644
--- a/ooni/assets/bridgetests.txt
+++ b/ooni/assets/bridgetests.txt
@@ -1,3 +1,5 @@
-213.151.89.102:9001
-108.166.106.156:443
-217.150.224.213:443
+#213.151.89.102:9001
+#108.166.106.156:443
+#217.150.224.213:443
+85.193.252.111:443
+#68.98.41.14:9001
diff --git a/ooni/plugins/bridget.py b/ooni/plugins/bridget.py
index 5562fb6..f665290 100644
--- a/ooni/plugins/bridget.py
+++ b/ooni/plugins/bridget.py
@@ -8,92 +8,350 @@
# | connection to a list of bridges or relays. |
# +----------------------------------------------+
#
-# :authors: Arturo Filasto, Isis Lovecruft, Jacob Appelbaum
+# :authors: Arturo Filasto, Isis Lovecruft
# :licence: see included LICENSE
# :version: 0.1.0-alpha
-from zope.interface import implements
-from twisted.python import usage
-from twisted.plugin import IPlugin
-from twisted.internet import reactor
+from __future__ import with_statement
+from zope.interface import implements
+from twisted.python import usage
+from twisted.plugin import IPlugin
+from twisted.internet import reactor, error
-from ooni.utils import log
-from ooni.plugoo.tests import ITest, OONITest
-from ooni.plugoo.assets import Asset
+import random
-class bridgetArgs(usage.Options):
- optParameters = [['bridges', 'b', None, 'List of bridges to scan'],
- ['relays', 'f', None, 'List of relays to scan'],
- ['resume', 'r', 0, 'Resume at this index']]
+try:
+ from ooni.lib.txtorcon import CircuitListenerMixin, IStreamAttacher
+except:
+ print "BridgeT requires txtorcon: https://github.com/meejah/txtorcon.git"
+ print "Your copy of OONI should have it included, if you're seeing this"
+ print "message, please file a bug report."
+ log.msg ("Bridget: Unable to import from ooni.lib.txtorcon")
-class bridgetTest(OONITest):
+from ooni.utils import log
+from ooni.plugoo.tests import ITest, OONITest
+from ooni.plugoo.assets import Asset
+
+
+class BridgetArgs(usage.Options):
+ optParameters = [
+ ['bridges', 'b', None,
+ 'List of bridges to scan (IP:ORport)'],
+ ['relays', 'f', None,
+ 'List of relays to scan (IP)'],
+ ['socks', 's', 9049,
+ 'Tor SocksPort to use'],
+ ['control', 'c', 9052,
+ 'Tor ControlPort to use'],
+ ['random', 'x', False,
+ 'Randomize control and socks ports'],
+ ['tor-path', 'p', '/usr/sbin/tor',
+ 'Path to the Tor binary to use'],
+ ['transport', 't', None,
+ 'Tor ClientTransportPlugin string. Requires -b.'],
+ ['resume', 'r', 0,
+ 'Resume at this index']]
+
+class CustomCircuit(CircuitListenerMixin):
+ implements(IStreamAttacher)
+
+ from txtorcon.interface import IRouterContainer, ICircuitContainer
+
+ def __init__(self, state):
+ self.state = state
+ self.waiting_circuits = []
+
+ def waiting_on(self, circuit):
+ for (circid, d) in self.waiting_circuits:
+ if circuit.id == circid:
+ return true
+ return False
+
+ def circuit_extend(self, circuit, router):
+ "ICircuitListener"
+ if circuit.purpose != 'GENERAL':
+ return
+ if self.waiting_on(circuit):
+ log.msg("Circuit %d (%s)" % (circuit.id, router.id_hex))
+
+ def circuit_built(self, circuit):
+ "ICircuitListener"
+ if circuit.purpose != 'GENERAL':
+ return
+
+ log.msg("Circuit %s built ..." % circuit.id)
+ log.msg("Full path of %s: %s" % (circuit.id, circuit.path))
+
+ for (circid, d) in self.waiting_circuits:
+ if circid == circuit.id:
+ self.waiting_circuits.remove(circid, d)
+ d.callback(circuit)
+
+ def circuit_failed(self, circuit, reason):
+ if self.waiting_on(circuit):
+ log.msg("A circuit we requested %s failed for reason %s" %
+ (circuit.id, reason))
+ circid, d = None, None
+ for x in self.waiting_circuits:
+ if x[0] == circuit.id:
+ circid, d, stream_cc = x
+ if d is None:
+ raise Exception("Expected to find circuit.")
+
+ self.waiting_circuits.remove((circid, d))
+ log.msg("Trying to build a circuit for %s" % circid)
+ self.request_circuit_build(d)
+
+ def check_circuit_route(self, circuit, router):
+ if router in circuit.path:
+ #router.update() ## XXX can i use without args? no.
+ TorInfo.dump(self)
+
+ def request_circuit_build(self, deferred):
+ entries = self.state.entry_guards.value()
+ relays = self.state.routers.values()
+
+ log.msg("We have these nodes listed as entry guards:")
+ log.msg("%s" % entries)
+ log.msg("We have these nodes listed as relays:")
+ log.msg("%s" % relays)
+
+ path = [random.choice(entries),
+ random.choice(relays),
+ random.choice(relays)]
+
+ log.msg("Requesting a circuit: %s"
+ % '-->'.join(map(lambda x: x.location.countrycode, path)))
+
+ class AppendWaiting:
+ def __init__(self, attacher, deferred):
+ self.attacher = attacher
+ self.d = deferred
+
+ def __call__(self, circuit):
+ """
+ Return from build_circuit is a Circuit, however, we want to
+ wait until it is built before we can issue an attach on it and
+ callback to the Deferred we issue here.
+ """
+ log.msg("Circuit %s is in progress ..." % circuit.id)
+ self.attacher.waiting_circuits.append((circuit.id, self.d))
+
+ return self.state.build_circuit(path).addCallback(AppendWaiting(self, deferred_to_callback)).addErrback(log.err)
+
+class BridgetTest(OONITest):
+ """
+ XXX fill me in
+
+ :ivar config:
+ An :class:`ooni.lib.txtorcon.TorConfig` instance.
+ :ivar use_bridges:
+ A boolean integer [0|1].
+ :ivar entry_nodes:
+ A string of all provided relays to test. We have to do this
+ because txtorcon.TorState().entry_guards won't build a custom
+ circuit if the first hop isn't in the torrc's EntryNodes.
+ :ivar relay_list:
+ The same as :ivar entry_nodes: but in list form.
+ :ivar socks_port:
+ Integer for Tor's SocksPort.
+ :ivar control_port:
+ Integer for Tor's ControlPort.
+ :ivar plug_transport:
+ String defining the Tor's ClientTransportPlugin, for testing
+ a bridge's pluggable transport functionality.
+ :ivar tor_binary:
+ Path to the Tor binary to use, e.g. \'/usr/sbin/tor\'
+ """
implements(IPlugin, ITest)
shortName = "bridget"
- description = "Use a slave Tor process to test RELAY_EXTEND to bridges/relays"
+ description = "Use a Tor process to test connecting to bridges/relays"
requirements = None
- options = bridgetArgs
+ options = BridgetArgs
blocking = False
- def experiment(self, args):
- log.msg("BridgeT: initiating test ... ")
+ def load_assets(self):
+ """
+ Load bridges from file given in user options. Bridges should be given
+ in the form IP:ORport. We don't want to load relays as assets, because
+ it's inefficient to test them one at a time.
+ """
+ assets = {}
+ if self.local_options:
+ if self.local_options['bridges']:
+ assets.update({'bridge': Asset(self.local_options['bridges'])})
+ return assets
+ def initialize(self):
+ """
+ Extra initialization steps. We only want one child Tor process
+ running, so we need to deal with the creation of the torrc only once,
+ before the experiment runs.
+ """
+ self.relay_list = []
+ ## XXX why doesn't the default set in the options work?
+ self.socks_port = 9049
+ self.control_port = 9052
+
+ if self.local_options:
+ try:
+ from ooni.lib.txtorcon import TorConfig
+ except:
+ log.msg("Could not import TorConfig class from txtorcon")
+ raise
+
+ options = self.local_options
+ self.config = TorConfig()
+ self.socks_port = options['socks']
+ self.control_port = options['control']
+
+ if options['bridges']:
+ log.msg("Using Bridges ...")
+ self.config.UseBridges = 1
+
+ if options['relays']:
+ '''
+ Stupid hack for when testing only relays (and not bridges):
+ Tor doesn't use EntryNodes when UseBridges is enabled, but
+ txtorcon requires config.state.entry_guards to make a custom
+ circuit, so we should list them as EntryNodes anyway.
+ '''
+ log.msg("Using relays ...")
+
+ with open(options['relays']) as relay_file:
+ for line in relay_file.readlines():
+ if line.startswith('#'):
+ continue
+ else:
+ relay = line.replace('\n', '') ## not assets because
+ self.relay_list.append(relay) ## we don't want to
+ ## test one at a time
+ self.config.EntryNodes = ','
+ self.config.EntryNodes.join(relay_list)
+
+ if options['random']:
+ log.msg("Using randomized ControlPort and SocksPort ...")
+ self.socks_port = random.randint(1024, 2**16)
+ self.control_port = random.randint(1024, 2**16)
+
+ if options['tor-path']:
+ self.tor_binary = options['tor-path']
+
+ if options['transport']:
+ '''
+ ClientTransportPlugin transport socks4|socks5 IP:PORT
+ ClientTransportPlugin transport exec path-to-binary [options]
+ '''
+ if not options['bridges']:
+ e = "To test pluggable transports, you must provide a file"
+ e = e+"with a list of bridge IP:ORPorts. See \'-b' option."
+ raise usage.UsageError("%s" % e)
+
+ log.msg("Using pluggable transport ...")
+ ## XXX fixme there's got to be a better way to check the exec
+ assert type(options['transport']) is str
+ self.config.ClientTransportPlugin = options['transport']
+
+ self.config.SocksPort = self.socks_port
+ self.config.ControlPort = self.control_port
+ self.config.save()
+
+ print self.config.create_torrc()
+ report = {'tor_config': self.config.config}
+ #log.msg("Starting Tor")
+ #
+ #self.tor_process_protocol = self.bootstrap_tor(self.config)
+ #self.d = self.bootstrap_tor(self.d, self.config,
+ # self.reactor, self.report)
+ #return self.d
+ return self.config
+ else:
+ return None
+
+ def bootstrap_tor(self, config, args):
+ """
+ Launch a Tor process with the TorConfig instance returned from
+ initialize().
+
+ Returns a Deferred which callbacks with a TorProcessProtocol connected
+ to the fully-bootstrapped Tor; this has a txtorcon.TorControlProtocol
+ instance as .protocol.
+ """
from ooni.lib.txtorcon import TorProtocolFactory, TorConfig, TorState
from ooni.lib.txtorcon import DEFAULT_VALUE, launch_tor
- reactor = self.reactor
+ log.msg("Tor config: %s" % config)
+ log.msg("Starting Tor ...")
def setup_failed(args):
log.msg("Setup Failed.")
report.update({'failed': args})
reactor.stop()
- #return report
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})
- #return report
def bootstrap(c):
conf = TorConfig(c)
conf.post_bootstrap.addCallback(setup_complete).addErrback(setup_failed)
- log.msg("Slave Tor process connected, bootstrapping ...")
-
- config = TorConfig()
- import random
- config.SocksPort = random.randint(1024, 2**16)
- config.ControlPort = random.randint(1024, 2**16)
- #config.SocksPort = 12345
- #config.ControlPort = 12346
-
- if 'bridge' in args:
- config.UseBridges = 1
- config.Bridge = args['bridge']
- config.save()
- print config.create_torrc()
- report = {'tor_config': config.config}
- log.msg("Starting Tor")
+ log.msg("Tor process connected, bootstrapping ...")
def updates(prog, tag, summary):
log.msg("%d%%: %s" % (prog, summary))
- #return
- d = launch_tor(config, self.reactor, progress_updates=updates)
- d.addCallback(setup_complete)
- d.addErrback(setup_failed)
- return d
+ ## :return: a Deferred which callbacks with a TorProcessProtocol
+ ## connected to the fully-bootstrapped Tor; this has a
+ ## txtorcon.TorControlProtocol instance as .protocol.
+ deferred = launch_tor(config, reactor, progress_updates=updates,
+ tor_binary=self.tor_binary)
+ deferred.addCallback(bootstrap, config)
+ deferred.addErrback(setup_failed)
- def load_assets(self):
- assets = {}
- if self.local_options:
- if self.local_options['bridges']:
- assets.update({'bridge': Asset(self.local_options['bridges'])})
- elif self.local_options['relays']:
- assets.update({'relay': Asset(self.local_options['relays'])})
- return assets
+ #print "Tor process ID: %s" % d.transport.pid
+ return deferred
+
+ def experiment(self, args):
+ """
+ XXX fill me in
+ """
+ log.msg("BridgeT: initiating test ... ")
+
+ def reconfigure_failed(args):
+
+ def reconfigure_controller(proto, args):
+ ## if bridges and relays, use one bridge then build a circuit
+ ## from the relays
+ if args['bridge']:
+ print args
+ print args['bridge']
+ #d.addCallback(CustomCircuit(state))
+ proto.set_conf('Bridge', args['bridge'])
+ ## if bridges only, try one bridge at a time, but don't build
+ ## circuits, just return
+ ## if relays only, build circuits from relays
+
+ ## XXX see txtorcon.TorControlProtocol.add_event_listener
+ ## we may not need full CustomCircuit class
+
+ d = defer.Deferred() ## 1 make deferred
+ d.addCallback(self.bootstrap_tor, self.config) ## 2 blastoff
+ ## 3 reconfigure
+ ## 4 build circuits
+
+ #d = self.bootstrap_tor(self.config, args).addCallback(configure,
+ ##c = CustomCircuit(state)
+ #d.addCallback(configure, d.protocol)
+ #d.addErrback(err)
+ #return d
+
+## So that getPlugins() can register the Test:
+bridget = BridgetTest(None, None, None)
-# We need to instantiate it otherwise getPlugins does not detect it
-# XXX Find a way to load plugins without instantiating them.
-bridget = bridgetTest(None, None, None)
+## ISIS' NOTES
+##
+##
+##
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits