[Pkg-privacy-commits] [txtorcon] 16/96: Honour ControlPort=0 in launch_tor() (issue 80)
Jérémy Bobbio
lunar at moszumanska.debian.org
Sun Sep 6 18:33:33 UTC 2015
This is an automated email from the git hooks/post-receive script.
lunar pushed a commit to branch master
in repository txtorcon.
commit b8d11390e1e108260c849531e9a78c03f240bb13
Author: mike warren <mike.warren at chaordix.com>
Date: Sun Sep 21 22:59:28 2014 -0600
Honour ControlPort=0 in launch_tor() (issue 80)
This allows you to pass in a ControlPort of 0 to the launch_tor
method, which causes no control connection to be established. In this
case, you must kill the tor off yourself and need to figure out when
it's bootstrapped "somehow".
---
test/test_torconfig.py | 31 +++++++++++++++
txtorcon/torconfig.py | 101 +++++++++++++++++++++++++++++++++----------------
2 files changed, 99 insertions(+), 33 deletions(-)
diff --git a/test/test_torconfig.py b/test/test_torconfig.py
index 8ec9f07..cdbbeda 100644
--- a/test/test_torconfig.py
+++ b/test/test_torconfig.py
@@ -702,6 +702,11 @@ class FakeProcessTransportNeverBootstraps(FakeProcessTransport):
self.protocol.dataReceived('650 STATUS_CLIENT NOTICE BOOTSTRAP PROGRESS=90 TAG=circuit_create SUMMARY="Establishing a Tor circuit"\r\n')
+class FakeProcessTransportNoProtocol(FakeProcessTransport):
+ def closeStdin(self):
+ pass
+
+
class LaunchTorTests(unittest.TestCase):
def setUp(self):
@@ -1086,6 +1091,32 @@ class LaunchTorTests(unittest.TestCase):
process.processEnded(Status())
self.assertEquals(len(self.flushLoggedErrors(RuntimeError)), 1)
+ def test_launch_tor_no_control_port(self):
+ '''
+ See Issue #80. This allows you to launch tor with a TorConfig
+ with ControlPort=0 in case you don't want a control connection
+ at all. In this case you get back a TorProcessProtocol and you
+ own both pieces. (i.e. you have to kill it yourself).
+ '''
+
+ config = TorConfig()
+ config.ControlPort = 0
+ trans = FakeProcessTransportNoProtocol()
+ trans.protocol = self.protocol
+
+ def creator(*args, **kw):
+ print "Bad: connection creator called"
+ self.fail()
+
+ def on_protocol(proto):
+ self.process_proto = proto
+ pp = launch_tor(config,
+ FakeReactor(self, trans, on_protocol),
+ connection_creator=creator, tor_binary='/bin/echo')
+ self.assertTrue(pp.called)
+ self.assertEqual(pp.result, self.process_proto)
+ return pp
+
class ErrorTests(unittest.TestCase):
def test_no_tor_binary(self):
diff --git a/txtorcon/torconfig.py b/txtorcon/torconfig.py
index fe5ef67..4aef8d8 100644
--- a/txtorcon/torconfig.py
+++ b/txtorcon/torconfig.py
@@ -53,7 +53,8 @@ class TorProcessProtocol(protocol.ProcessProtocol):
:param connection_creator: A no-parameter callable which
returns a Deferred which promises a
:api:`twisted.internet.interfaces.IStreamClientEndpoint
- <IStreamClientEndpoint>`
+ <IStreamClientEndpoint>`. If this is None, we do NOT
+ attempt to connect to the underlying Tor process.
:param progress_updates: A callback which received progress
updates with three args: percent, tag, summary
@@ -92,10 +93,14 @@ class TorProcessProtocol(protocol.ProcessProtocol):
self.config = config
self.tor_protocol = None
- self.connection_creator = connection_creator
self.progress_updates = progress_updates
- self.connected_cb = defer.Deferred()
+ if connection_creator:
+ self.connection_creator = connection_creator
+ self.connected_cb = defer.Deferred()
+ else:
+ self.connection_creator = None
+ self.connected_cb = None
self.attempted_connect = False
self.to_delete = []
@@ -132,7 +137,8 @@ class TorProcessProtocol(protocol.ProcessProtocol):
## tor_connection_failed)
txtorlog.msg(data)
- if not self.attempted_connect and 'Bootstrap' in data:
+ if not self.attempted_connect and self.connection_creator \
+ and 'Bootstrap' in data:
self.attempted_connect = True
d = self.connection_creator()
d.addCallback(self.tor_connected)
@@ -265,17 +271,39 @@ def launch_tor(config, reactor,
stdout=None, stderr=None):
"""launches a new Tor process with the given config.
- :param kill_on_stderr:
- When True (the default), if Tor prints anything on stderr we
- kill off the process, close the TorControlProtocol and raise
- an exception.
+ There may seem to be a ton of options, but don't panic: this
+ method should be easy to use and most options can be ignored
+ except for advanced use-cases. Calling with a completely empty
+ TorConfig should Just Work::
+
+ config = TorConfig()
+ d = launch_tor(config, reactor)
+ d.addCallback(...)
+
+ Note that the incoming TorConfig instance is examined and several
+ config options are acted upon appropriately:
+
+ ``DataDirectory``: if supplied, a tempdir is not created, and the
+ one supplied is not deleted.
- :param config: an instance of :class:`txtorcon.TorConfig` with any
- configuration values you want. :meth:`txtorcon.TorConfig.save`
- should have been called already (anything unsaved won't make
- it into the torrc produced). If ControlPort isn't set, 9052 is
- used; if DataDirectory isn't set, tempdir is used to create
- one.
+ ``ControlPort``: if 0 (zero), a control connection is NOT
+ established (and ``connection_creator`` is ignored). In this case
+ we can't wait for Tor to bootstrap, and **you must kill the tor**
+ yourself.
+
+ ``User``: if this exists, we attempt to set ownership of the tempdir
+ to this user (but only if our effective UID is 0).
+
+ This method may set the following options on the supplied
+ TorConfig object: ``DataDirectory, ControlPort,
+ CookieAuthentication, __OwningControllerProcess`` and WILL call
+ :meth:`txtorcon.TorConfig.save`
+
+ :param config:
+ an instance of :class:`txtorcon.TorConfig` with any
+ configuration values you want. If ``ControlPort`` isn't set,
+ 9052 is used; if ``DataDirectory`` isn't set, tempdir is used
+ to create one (in this case, it will be deleted upon exit).
:param reactor: a Twisted IReactorCore implementation (usually
twisted.internet.reactor)
@@ -286,12 +314,10 @@ def launch_tor(config, reactor,
:param progress_updates: a callback which gets progress updates; gets as
args: percent, tag, summary (FIXME make an interface for this).
- :param connection_creator: is mostly available to ease testing, so
- you probably don't want to supply this. If supplied, it is a
- callable that should return a Deferred that delivers an
- :api:`twisted.internet.interfaces.IProtocol <IProtocol>` or
- ConnectError.
- See :api:`twisted.internet.interfaces.IStreamClientEndpoint`.connect
+ :param kill_on_stderr:
+ When True (the default), if Tor prints anything on stderr we
+ kill off the process, close the TorControlProtocol and raise
+ an exception.
:param stdout: a file-like object to which we write anything that
Tor prints on stdout (just needs to support write()).
@@ -301,6 +327,14 @@ def launch_tor(config, reactor,
off by default if anything appears on stderr; pass "no_kill=True"
if you don't like the behavior.
+ :param connection_creator: is mostly available to ease testing, so
+ you probably don't want to supply this. If supplied, it is a
+ callable that should return a Deferred that delivers an
+ :api:`twisted.internet.interfaces.IProtocol <IProtocol>` or
+ ConnectError.
+ See :api:`twisted.internet.interfaces.IStreamClientEndpoint`.connect
+ Note that this parameter is ignored if config.ControlPort == 0
+
:return: a Deferred which callbacks with a TorProcessProtocol
connected to the fully-bootstrapped Tor; this has a
:class:`txtorcon.TorControlProtocol` instance as `.tor_protocol`. In
@@ -308,7 +342,9 @@ def launch_tor(config, reactor,
have been called, so if you close the TorControlProtocol the Tor should
exit also (see `control-spec
<https://gitweb.torproject.org/torspec.git/blob/HEAD:/control-spec.txt>`_
- 3.23).
+ 3.23). Note that if ControlPort was 0, we don't connect at all
+ and therefore don't wait for Tor to be bootstrapped. In this case, it's
+ up to you to kill off the Tor you created.
HACKS:
@@ -317,7 +353,6 @@ def launch_tor(config, reactor,
port. It seems that waiting for the first 'bootstrap' message on
stdout is sufficient. Seems fragile...and doesn't work 100% of
the time, so FIXME look at Tor source.
-
"""
## We have a slight problem with the approach: we need to pass a
@@ -340,10 +375,6 @@ def launch_tor(config, reactor,
# We fail right here instead of waiting for the reactor to start
raise TorNotFound('Tor binary could not be found')
- if config.needs_save():
- log.msg("Config was unsaved when launch_tor() called; calling save().")
- config.save()
-
# make sure we got things that have write() for stderr, stdout
# kwargs
for arg in [stderr, stdout]:
@@ -372,11 +403,15 @@ def launch_tor(config, reactor,
try:
control_port = config.ControlPort
except KeyError:
- control_port = 9052
+ control_port = 9052 # FIXME choose a random, unoccupied one?
config.ControlPort = control_port
- config.CookieAuthentication = 1
- config.__OwningControllerProcess = os.getpid()
+ if control_port != 0:
+ config.CookieAuthentication = 1
+ config.__OwningControllerProcess = os.getpid()
+ else:
+ connection_creator = None
+
config.save()
(fd, torrc) = tempfile.mkstemp(prefix='tortmp')
@@ -385,7 +420,7 @@ def launch_tor(config, reactor,
# txtorlog.msg('Running with config:\n', open(torrc, 'r').read())
- if connection_creator is None:
+ if connection_creator is None and control_port > 0:
connection_creator = functools.partial(
TCP4ClientEndpoint(reactor, 'localhost', control_port).connect,
TorProtocolFactory())
@@ -398,10 +433,10 @@ def launch_tor(config, reactor,
# process might be shut down way before the reactor, but if the
# reactor bombs out without the subprocess getting closed cleanly,
# we'll want the system shutdown events triggered so the temporary
- # files get cleaned up
+ # files get cleaned up either way
- # we don't want to delete the user's directories, just our
- # temporary ones
+ # we don't want to delete the user's directories, just temporary
+ # ones this method created.
if user_set_data_directory:
process_protocol.to_delete = [torrc]
reactor.addSystemEventTrigger('before', 'shutdown',
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-privacy/packages/txtorcon.git
More information about the Pkg-privacy-commits
mailing list