[tor-commits] [ooni-probe/master] * Added documentation.
isis at torproject.org
isis at torproject.org
Thu Oct 4 14:41:15 UTC 2012
commit fa731c54c4c28764a345523ca3ab632e81fad884
Author: Isis Lovecruft <isis at torproject.org>
Date: Sun Sep 23 12:58:21 2012 +0000
* Added documentation.
* Moved general setup, circuit, and bootstrap, to utils/onion.py.
---
ooni/plugins/bridget.py | 323 +++++++++++++++++++----------------------------
ooni/utils/circuit.py | 109 ----------------
ooni/utils/onion.py | 260 ++++++++++++++++++++++++++++++++++++++
3 files changed, 389 insertions(+), 303 deletions(-)
diff --git a/ooni/plugins/bridget.py b/ooni/plugins/bridget.py
index d82f2e7..c4667e1 100644
--- a/ooni/plugins/bridget.py
+++ b/ooni/plugins/bridget.py
@@ -13,7 +13,6 @@
# :version: 0.1.0-alpha
from __future__ import with_statement
-from functools import wraps, partial
from random import randint
from twisted.python import usage
from twisted.plugin import IPlugin
@@ -25,11 +24,12 @@ from ooni.plugoo.tests import ITest, OONITest
from ooni.plugoo.assets import Asset
import os
-import signal
import sys
def timer(secs, e=None):
+ import signal
+ import functools.wraps
def decorator(func):
def _timer(signum, frame):
raise TimeoutError, e
@@ -41,7 +41,7 @@ def timer(secs, e=None):
finally:
signal.alarm(0)
return res
- return wraps(func)(wrapper)
+ return functools.wraps(func)(wrapper)
return decorator
@@ -297,27 +297,60 @@ class BridgetTest(OONITest):
def experiment(self, args):
"""
- XXX fill me in
+ We cannot use the Asset model, because that model calls
+ self.experiment() with the current Assets, which would be one relay
+ and one bridge, then it gives the defer.Deferred returned from
+ self.experiment() to self.control(), which means that, for each
+ (bridge, relay) pair, experiment gets called again, which instantiates
+ an additional Tor process that attempts to bind to the same
+ ports. Thus, additionally instantiated Tor processes return with
+ RuntimeErrors, which break the final defer.chainDeferred.callback(),
+ sending it into the errback chain.
+
+ if bridges:
+ 1. configure first bridge line
+ 2a. configure data_dir, if it doesn't exist
+ 2b. write torrc to a tempfile in data_dir
+ 3. start tor } if any of these
+ 4. remove bridges which are public relays } fail, add current
+ 5. SIGHUP for each bridge } bridge to unreach-
+ } able bridges.
+ if relays:
+ 1a. configure the data_dir, if it doesn't exist
+ 1b. write torrc to a tempfile in data_dir
+ 2. start tor
+ 3. remove any of our relays which are already part of current
+ circuits
+ 4a. attach CustomCircuit() to self.state
+ 4b. RELAY_EXTEND for each relay } if this fails, add
+ } current relay to list
+ } of unreachable relays
+ 5.
+ if bridges and relays:
+ 1. configure first bridge line
+ 2a. configure data_dir if it doesn't exist
+ 2b. write torrc to a tempfile in data_dir
+ 3. start tor
+ 4. remove bridges which are public relays
+ 5. remove any of our relays which are already part of current
+ circuits
+ 6a. attach CustomCircuit() to self.state
+ 6b. for each bridge, build three circuits, with three
+ relays each
+ 6c. RELAY_EXTEND for each relay } if this fails, add
+ } current relay to list
+ } of unreachable relays
:param args:
The :class:`BridgetAsset` line currently being used.
"""
try:
- from tempfile import mkstemp, mkdtemp
- from shutil import rmtree
-
- from twisted.internet.endpoints import TCP4ClientEndpoint
-
- from ooni.utils import circuit
- from ooni.lib.txtorcon import TorProcessProtocol
- from ooni.lib.txtorcon import TorProtocolFactory
+ from ooni.utils.onion import start_tor, CustomCircuit
from ooni.lib.txtorcon import TorConfig, TorState
-
except ImportError:
raise TxtorconImportError
-
- except TxtorconImportError:
- ## XXX is this something we should add to the reactor?
+ except TxtorconImportError, tie:
+ log.err(tie)
sys.exit()
def bootstrap(ctrl):
@@ -329,18 +362,6 @@ class BridgetTest(OONITest):
conf.post_bootstrap.addCallback(setup_done).addErrback(setup_fail)
log.msg("Tor process connected, bootstrapping ...")
- def delete_temp(delete_list):
- """
- Given a list of files or directories to delete, delete all and
- suppress all errors.
- """
- for temp in delete_list:
- try:
- os.unlink(temp)
- except OSError:
- rmtree(temp, ignore_errors=True)
-
- #@timer(self.circuit_timeout)
@defer.inlineCallbacks
def reconfigure_bridge(state, bridge, use_pt=False, pt_type=None):
"""
@@ -352,10 +373,11 @@ class BridgetTest(OONITest):
log.msg("Current Bridge: %s" % bridge)
try:
if use_pt is False:
- reset_tor = yield state.protocol.set_conf('Bridge', bridge)
- elif use_pt and pt_type is not None:
reset_tor = yield state.protocol.set_conf('Bridge',
- pt_type +' '+ bridge)
+ bridge)
+ elif use_pt and pt_type is not None:
+ reset_tor = yield state.protocol.set_conf(
+ 'Bridge', pt_type +' '+ bridge)
else:
raise PTNotFoundException
@@ -368,16 +390,17 @@ class BridgetTest(OONITest):
if controller_response == 'OK':
defer.returnValue(state.callback)
else:
- log.msg("TorControlProtocol responded with error:\n%s\n"
- % controller_response)
+ log.msg("TorControlProtocol responded with error:\n%s",
+ controller_response)
defer.returnValue(state.callback)
except Exception, e:
- log.msg("Reconfiguring torrc with Bridge line %s failed:\n%s\n"
- % (bridge, e))
+ log.msg("Reconfiguring torrc with Bridge line %s failed:\n%s",
+ bridge, e)
def reconfigure_fail(state, bridge, bad):
- log.msg("Reconfiguring TorConfig with parameters %s failed" % state)
+ log.msg("Reconfiguring TorConfig with parameters %s failed",
+ state)
bad.append(bridge)
@defer.inlineCallbacks
@@ -392,104 +415,34 @@ class BridgetTest(OONITest):
def __remove_line__(node, bridges=bridges):
for line in bridges:
if line.startswith(node):
- log.msg("Removing %s because it is a public relay" % node)
- bridges.remove(line)
+ try:
+ log.msg("Removing %s because it is a public relay",
+ node)
+ bridges.remove(line)
+ except ValueError, ve:
+ log.err(ve)
if len(both) > 0:
try:
- updated = yield map(lambda node: __remove_line__(node), both)
+ updated = yield map(lambda node:
+ __remove_line__(node), both)
if not updated:
## XXX do these need to be state.callback?
defer.returnValue(state)
else:
defer.returnValue(state)
except Exception, e:
- log.msg("Unable to remove public relays from bridge list:\n%s"
- % both)
+ log.msg("Removing public relays from bridge list failed:\n%s",
+ both)
log.err(e)
-
- def request_circuit_build(state, deferred, path=None):
- if path is None:
- if relays_remaining() > 0:
- first, middle,last = (state.relays.pop()
- for i in range(3))
- else:
- first = random.choice(state.entry_guards.values())
- middle, last = (random.choice(state.routers.values())
- for i in range(2))
- path = [first, middle, last]
- else:
- assert type(path) is list, "Circuit path must be a list of routers!"
- assert len(path) >= 3, "Circuits must be at least three hops!"
-
- log.msg("Requesting a circuit: %s"
- % '->'.join(map(lambda node: node, path)))
-
- class AppendWaiting:
- def __init__(self, attacher, deferred):
- self.attacher = attacher
- self.d = deferred
- def __call__(self, circ):
- """
- 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 ..." % circ.id)
- self.attacher.waiting_circuits.append((circ.id, self.d))
-
- return self.state.build_circuit(path).addCallback(
- AppendWaiting(self, deferred)).addErrback(
- log.err)
+ except ValueError, ve:
+ log.err(ve)
def attacher_extend_circuit(attacher, deferred, router):
-
-
- def setup_fail(proto, bridge_list, relay_list):
- log.err("Setup Failed: %s" % proto)
- log.err("We were given bridge list:\n%s\nAnd our relay list:\n%s\n"
- % (bridge_list, relay_list))
- report.update({'setup_fail': 'FAILED',
- 'proto': proto,
- 'bridge_list': bridge_list,
- 'relay_list': relay_list})
- reactor.stop()
-
- def setup_done(proto, bridge_list, relay_list):
- log.msg("Setup Complete: %s" % proto)
- state = TorState(proto.tor_protocol)
- state.post_bootstrap.addCallback(state_complete).addErrback(setup_fail)
- if bridge_list is not None:
- state.post_bootstrap.addCallback(remove_public_relays, bridge_list)
- if relay_list is not None:
- raise NotImplemented
- #report.update({'success': args})
-
- def start_tor(reactor, update, torrc, to_delete, control_port, tor_binary,
- data_directory, bridge_list=None, relay_list=None):
- """
- Create a TCP4ClientEndpoint at our control_port, and connect
- it to our reactor and a spawned Tor process. Compare with
- :meth:`txtorcon.launch_tor` for differences.
- """
- end_point = TCP4ClientEndpoint(reactor, 'localhost', control_port)
- connection_creator = partial(end_point.connect, TorProtocolFactory())
- process_protocol = TorProcessProtocol(connection_creator, updates)
- process_protocol.to_delete = to_delete
- reactor.addSystemEventTrigger('before', 'shutdown',
- partial(delete_temp, to_delete))
- try:
- transport = reactor.spawnProcess(process_protocol,
- tor_binary,
- args=(tor_binary,'-f',torrc),
- env={'HOME': data_directory},
- path=data_directory)
- transport.closeStdin()
- except RuntimeError, e:
- process_protocol.connected_cb.errback(e)
- finally:
- return process_protocol.connected_cb, bridge_list, relay_list
+ ## XXX todo write me
+ ## state.attacher.extend_circuit
+ raise NotImplemented
+ #attacher.extend_circuit
def state_complete(state, bridge_list=None, relay_list=None):
"""Called when we've got a TorState."""
@@ -508,12 +461,14 @@ class BridgetTest(OONITest):
else:
return state, None
- def state_attach(state, relay_list):
+ def state_attach(state, path):
log.msg("Setting up custom circuit builder...")
attacher = CustomCircuit(state)
state.set_attacher(attacher, reactor)
state.add_circuit_listener(attacher)
+ return state
+ ## OLD
#for circ in state.circuits.values():
# for relay in circ.path:
# try:
@@ -526,80 +481,47 @@ class BridgetTest(OONITest):
return d
def state_attach_fail(state):
- log.msg("Attaching custom circuit builder failed.")
+ log.err("Attaching custom circuit builder failed: %s", state)
- def updates(prog, tag, summary):
- log.msg("%d%%: %s" % (prog, summary))
-
- def write_torrc(conf, data_dir=None):
- """
- Create a torrc in our data_directory. If we don't yet have a
- data_directory, create a temporary one. Any temporary files or
- folders are added to delete_list.
-
- :return: delete_list, data_dir, torrc
- """
- delete_list = []
-
- if data_dir is None:
- data_dir = mkdtemp(prefix='bridget-tordata')
- delete_list.append(data_dir)
- conf.DataDirectory = data_dir
-
- (fd, torrc) = mkstemp(dir=data_dir)
- delete_list.append(torrc)
- os.write(fd, conf.create_torrc())
- os.close(fd)
- return torrc, data_dir, delete_list
-
log.msg("Bridget: initiating test ... ")
- while self.bridges_remaining() > 0:
+ if self.bridges_remaining() > 0:
+ for self.current_bridge in self.bridges:
+ #self.current_bridge = bridge
+
+ if not self.config.config.has_key('Bridge'):
- self.current_bridge = self.bridges.pop()
- #self.current_bridge = args['bridge']
+ self.config.Bridge = self.current_bridge
- try:
- self.bridges.remove(self.current_bridge)
- except ValueError, ve:
- log.err(ve)
-
- if not self.config.config.has_key('Bridge'):
- self.config.Bridge = self.current_bridge
- (torrc, self.data_directory, to_delete) = write_torrc(
- self.config, self.data_directory)
-
- log.msg("Starting Tor ...")
- log.msg("Using the following as our torrc:\n%s"
- % self.config.create_torrc())
- report = {'tor_config': self.config.create_torrc()}
-
- state = start_tor(reactor, updates, torrc, to_delete,
- self.control_port,
- self.tor_binary,
- self.data_directory,
- bridge_list=self.bridges)
- state.addCallback(setup_done)
- state.addErrback(setup_fail)
- state.addCallback(remove_public_relays, self.bridges)
+ state = start_tor(reactor,
+ self.config,
+ self.control_port,
+ self.tor_binary,
+ self.data_directory)
+ state.addCallback(remove_public_relays, self.bridges)
+
+ else:
+ log.msg("We now have %d untested bridges..."
+ % self.bridges_remaining())
+ reconf = defer.Deferred()
+ reconf.addCallback(reconfigure_bridge, state,
+ self.current_bridge, self.use_pt,
+ self.pt_type)
+ reconf.addErrback(reconfigure_fail, state,
+ self.current_bridge, self.bridges_down)
+ state.chainDeferred(reconf)
+ state.callback()
+ #all = []
+ #reconf = reconfigure_bridge(state, self.current_bridge,
+ # self.use_pt, self.pt_type)
+ #reconf.addCallback(reconfigure_done)
+ #reconf.addErrback(reconfigure_fail)
+ #state.DeferredList(all)
+
+ if self.relays_remaining() != 0:
+ state.chainDeferred()
- else:
- log.msg("We now have %d untested bridges..."
- % self.bridges_remaining())
- reconf = defer.Deferred()
- reconf.addCallback(reconfigure_bridge, state,
- self.current_bridge, self.use_pt,
- self.pt_type)
- reconf.addErrback(reconfigure_fail, state,
- self.current_bridge, self.bridges_down)
- state.chainDeferred(reconf)
- #all = []
- #reconf = reconfigure_bridge(state, self.current_bridge,
- # self.use_pt, self.pt_type)
- #reconf.addCallback(reconfigure_done)
- #reconf.addErrback(reconfigure_fail)
- #state.DeferredList(all)
while self.relays_remaining() >= 3:
#path = list(self.relays.pop() for i in range(3))
@@ -609,10 +531,19 @@ class BridgetTest(OONITest):
for node in circ.path:
if node == self.current_relay:
self.relays_up.append(self.current_relay)
- try:
- ext = attacher_extend_circuit(state.attacher, circ,
- self.current_relay)
-
+
+ if len(circ.path) < 3:
+ try:
+ parameters = (state.attacher, circ,
+ self.current_relay)
+ ext = attacher_extend_circuit(parameters)
+ ext.addCallback(attacher_extend_circuit_done,
+ parameters)
+ except Exception, e:
+ log.msg("Extend circuit failed: %s %s"
+ % (e, parameters))
+ else:
+ continue
return state
## still need to attach attacher to state
@@ -647,6 +578,10 @@ bridget = BridgetTest(None, None, None)
## ISIS' NOTES
## -----------
+## nickm points out that the format operator '%', when used in log.LEVEL(),
+## forces string generation even when LEVEL is not used, increasing overhead;
+## we should do 'log.err("string with stuff %s", stuff)' instead.
+##
## TODO:
## o cleanup documentation
## x add DataDirectory option
diff --git a/ooni/utils/circuit.py b/ooni/utils/circuit.py
deleted file mode 100644
index d725879..0000000
--- a/ooni/utils/circuit.py
+++ /dev/null
@@ -1,109 +0,0 @@
-#
-# circuit.py
-# ----------
-# Utilities for working with Tor circuits.
-#
-# This code is largely taken from attach_streams_by_country.py in the txtorcon
-# documentation, and as such any and all credit should go to meejah. Minor
-# adjustments have been made to use OONI's logging system, and to build custom
-# circuits without actually attaching streams.
-#
-# :author: Meejah, Isis Lovecruft
-# :license: see included LICENSE file
-# :version: 0.1.0-alpha
-#
-
-import random
-
-from ooni.lib.txtorcon import CircuitListenerMixin, IStreamAttacher
-from ooni.lib.txtorcon import TorInfo
-from ooni.utils import log
-from zope.interface import implements
-
-
-class CustomCircuit(CircuitListenerMixin):
- implements(IStreamAttacher)
-
- def __init__(self, state, relays=None):
- self.state = state
- self.waiting_circuits = []
- self.relays = relays
-
- 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("Circuit %s failed for reason %s"
- % (circuit.id, reason))
- circid, d = None, None
- for c in self.waiting_circuits:
- if c[0] == circuit.id:
- circid, d = c
- 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, path=None):
- if path is None:
- if self.state.relays_remaining() > 0:
- first, middle,last = (self.state.relays.pop()
- for i in range(3))
- else:
- first = random.choice(self.state.entry_guards.values())
- middle, last = (random.choice(self.state.routers.values())
- for i in range(2))
- path = [first, middle, last]
- else:
- assert type(path) is list, "Circuit path must be a list of routers!"
- assert len(path) >= 3, "Circuits must be at least three hops!"
-
- log.msg("Requesting a circuit: %s"
- % '->'.join(map(lambda node: node, path)))
-
- class AppendWaiting:
- def __init__(self, attacher, deferred):
- self.attacher = attacher
- self.d = deferred
- def __call__(self, circ):
- """
- 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 ..." % circ.id)
- self.attacher.waiting_circuits.append((circ.id, self.d))
-
- return self.state.build_circuit(path).addCallback(
- AppendWaiting(self, deferred)).addErrback(
- log.err)
diff --git a/ooni/utils/onion.py b/ooni/utils/onion.py
new file mode 100644
index 0000000..76ec909
--- /dev/null
+++ b/ooni/utils/onion.py
@@ -0,0 +1,260 @@
+#
+# onion.py
+# ----------
+# Utilities for working with Tor.
+#
+# This code is largely taken from txtorcon and its documentation, and as such
+# any and all credit should go to Meejah. Minor adjustments have been made to
+# use OONI's logging system, and to build custom circuits without actually
+# attaching streams.
+#
+# :author: Meejah, Isis Lovecruft
+# :license: see included LICENSE file
+# :copyright: copyright (c) 2012 The Tor Project, Inc.
+# :version: 0.1.0-alpha
+#
+
+import random
+
+from ooni.lib.txtorcon import CircuitListenerMixin, IStreamAttacher
+from ooni.utils import log
+from zope.interface import implements
+
+
+def __setup_done__(proto):
+ log.msg("Setup Complete: %s" % proto)
+ state = TorState(proto.tor_protocol)
+ state.post_bootstrap.addCallback(state_complete).addErrback(__setup_fail__)
+
+def __setup_fail__(proto):
+ log.err("Setup Failed: %s" % proto)
+ report.update({'setup_fail': proto})
+ reactor.stop()
+
+def __updates__(_progress, _tag, _summary):
+ log.msg("%d%%: %s", _progress, _summary)
+
+def write_torrc(conf, data_dir=None):
+ """
+ Create a torrc in our data_dir. If we don't yet have a data_dir, create a
+ temporary one. Any temporary files or folders are added to delete_list.
+
+ :return: torrc, data_dir, delete_list
+ """
+ try:
+ from os import write, close
+ from tempfile import mkstemp, mkdtemp
+ except ImportError, ie:
+ log.err(ie)
+
+ delete_list = []
+
+ if data_dir is None:
+ data_dir = mkdtemp(prefix='bridget-tordata')
+ delete_list.append(data_dir)
+ conf.DataDirectory = data_dir
+
+ (fd, torrc) = mkstemp(dir=data_dir)
+ delete_list.append(torrc)
+ write(fd, conf.create_torrc())
+ close(fd)
+ return torrc, data_dir, delete_list
+
+def delete_files_or_dirs(delete_list):
+ """
+ Given a list of files or directories to delete, delete all and suppress
+ all errors.
+
+ :param delete_list:
+ A list of files or directories to delete.
+ """
+ try:
+ from os import unlink
+ from shutil import rmtree
+ except ImportError, ie:
+ log.err(ie)
+
+ for temp in delete_list:
+ try:
+ unlink(temp)
+ except OSError:
+ rmtree(temp, ignore_errors=True)
+
+def start_tor(reactor, config, control_port, tor_binary, data_dir,
+ report=None, progress=__updates__, process_cb=__setup_done__,
+ process_eb=__setup_fail__):
+ """
+ Use a txtorcon.TorConfig() instance, config, to write a torrc to a
+ tempfile in our DataDirectory, data_dir. If data_dir is None, a temp
+ directory will be created. Finally, create a TCP4ClientEndpoint at our
+ control_port, and connect it to our reactor and a spawned Tor
+ process. Compare with :meth:`txtorcon.launch_tor` for differences.
+
+ :param reactor:
+ An instance of class:`twisted.internet.reactor`.
+ :param config:
+ An instance of class:`txtorcon.TorConfig` with all torrc options
+ already configured. ivar:`config.ControlPort`,
+ ivar:`config.SocksPort`, ivar:`config.CookieAuthentication`, should
+ already be set, as well as ivar:`config.UseBridges` and
+ ivar:`config.Bridge` if bridges are to be used.
+ ivar:`txtorcon.DataDirectory` does not need to be set.
+ :param control_port:
+ The port number to use for Tor's ControlPort.
+ :param tor_binary:
+ The full path to the Tor binary to use.
+ :param data_dir:
+ The directory to use as Tor's DataDirectory.
+ :param report:
+ The class:`ooni.plugoo.reports.Report` instance to .update().
+ :param progress:
+ A non-blocking function to handle bootstrapping updates, which takes
+ three parameters: _progress, _tag, and _summary.
+ :param process_cb:
+ The function to callback to after
+ class:`ooni.lib.txtorcon.TorProcessProtocol` returns with the fully
+ bootstrapped Tor process.
+ :param process_eb:
+ The function to errback to if
+ class:`ooni.lib.txtorcon.TorProcessProtocol` fails.
+ :return:
+ A class:`ooni.lib.txtorcon.TorProcessProtocol` which callbacks with a
+ class:`txtorcon.TorControlProtocol` as .protocol.
+ """
+ try:
+ from functools import partial
+ from twisted.internet.endpoints import TCP4ClientEndpoint
+ from ooni.lib.txtorcon import TorProtocolFactory
+ from ooni.lib.txtorcon import TorProcessProtocol
+ except ImportError, ie:
+ log.err(ie)
+
+ ## TODO: add option to specify an already existing torrc, which
+ ## will require prior parsing to enforce necessary lines
+ (torrc, data_dir, to_delete) = write_torrc(config, data_dir)
+
+ log.msg("Starting Tor ...")
+ log.msg("Using the following as our torrc:\n%s", config.create_torrc())
+ if report is None:
+ report = {'torrc': config.create_torrc()}
+ else:
+ report.update({'torrc': config.create_torrc()})
+
+ end_point = TCP4ClientEndpoint(reactor, 'localhost', control_port)
+ connection_creator = partial(end_point.connect, TorProtocolFactory())
+ process_protocol = TorProcessProtocol(connection_creator, progress)
+ process_protocol.to_delete = to_delete
+
+ reactor.addSystemEventTrigger('before', 'shutdown',
+ partial(delete_files_or_dirs, to_delete))
+ try:
+ transport = reactor.spawnProcess(process_protocol,
+ tor_binary,
+ args=(tor_binary,'-f',torrc),
+ env={'HOME': data_dir},
+ path=data_dir)
+ transport.closeStdin()
+ except RuntimeError, e:
+ log.err("Starting Tor failed: %s", e)
+ process_protocol.connected_cb.errback(e)
+ except NotImplementedError, e:
+ url = "http://starship.python.net/crew/mhammond/win32/Downloads.html"
+ log.err("Running bridget on Windows requires pywin32: %s", url)
+ process_protocol.connected_cb.errback(e)
+
+ proto = process_protocol.connected_cb ## new defer.Deferred()
+ proto.addCallback(process_cb)
+ proto.addErrback(process_eb)
+ return proto
+
+
+class CustomCircuit(CircuitListenerMixin):
+ implements(IStreamAttacher)
+
+ def __init__(self, state, relays=None):
+ self.state = state
+ self.waiting_circuits = []
+ self.relays = relays
+
+ 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("Circuit %s failed for reason %s", circuit.id, reason)
+ circid, d = None, None
+ for c in self.waiting_circuits:
+ if c[0] == circuit.id:
+ circid, d = c
+ 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, router):
+ """
+ Check if a relay is a hop in one of our already built circuits.
+
+ """
+ for circ in self.state.circuits.values():
+ if router in circuit.path:
+ #router.update() ## XXX can i use without args? no.
+ TorInfo.dump(self)
+
+ def request_circuit_build(self, deferred, path=None):
+ if path is None:
+ if self.state.relays_remaining() > 0:
+ first, middle,last = (self.state.relays.pop()
+ for i in range(3))
+ else:
+ first = random.choice(self.state.entry_guards.values())
+ middle, last = (random.choice(self.state.routers.values())
+ for i in range(2))
+ path = [first, middle, last]
+ else:
+ assert type(path) is list, "Circuit path must be a list of relays!"
+ assert len(path) >= 3, "Circuits must be at least three hops!"
+
+ log.msg("Requesting a circuit: %s"
+ % '->'.join(map(lambda node: node, path)))
+
+ class AppendWaiting:
+ def __init__(self, attacher, deferred):
+ self.attacher = attacher
+ self.d = deferred
+ def __call__(self, circ):
+ """
+ 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 ...", circ.id)
+ self.attacher.waiting_circuits.append((circ.id, self.d))
+
+ return self.state.build_circuit(path).addCallback(
+ AppendWaiting(self, deferred)).addErrback(
+ log.err)
More information about the tor-commits
mailing list