tor_manager: expose errors when --tor-control-port= is provided

If you pass --tor-control-port= and we can't use it, throw an error that will
kill the whole process, instead of falling back to the default SOCKS port.

If you omit --tor-control-port=, then if all default control port connections
fail, we'll fall back to the default SOCKS port.

Also, test each combination separately, and improve the status messages.
This commit is contained in:
Brian Warner 2017-09-14 16:46:26 -07:00
parent ed420e0001
commit 0aeae9ce10
2 changed files with 58 additions and 18 deletions

View File

@ -50,7 +50,48 @@ class Tor(unittest.TestCase):
def test_connect(self):
reactor = object()
my_tor = X() # object() didn't like providedBy()
tcp = "port"
connect_d = defer.Deferred()
stderr = io.StringIO()
with mock.patch("wormhole.tor_manager.txtorcon.connect",
side_effect=connect_d) as connect:
with mock.patch("wormhole.tor_manager.clientFromString",
side_effect=["foo"]) as sfs:
d = get_tor(reactor, stderr=stderr)
self.assertEqual(sfs.mock_calls, [])
self.assertNoResult(d)
self.assertEqual(connect.mock_calls, [mock.call(reactor)])
connect_d.callback(my_tor)
tor = self.successResultOf(d)
self.assertIs(tor, my_tor)
self.assert_(ITorManager.providedBy(tor))
self.assertEqual(stderr.getvalue(),
" using Tor via default control port\n")
def test_connect_fails(self):
reactor = object()
connect_d = defer.Deferred()
stderr = io.StringIO()
with mock.patch("wormhole.tor_manager.txtorcon.connect",
side_effect=connect_d) as connect:
with mock.patch("wormhole.tor_manager.clientFromString",
side_effect=["foo"]) as sfs:
d = get_tor(reactor, stderr=stderr)
self.assertEqual(sfs.mock_calls, [])
self.assertNoResult(d)
self.assertEqual(connect.mock_calls, [mock.call(reactor)])
connect_d.errback(ConnectError())
tor = self.successResultOf(d)
self.assertIsInstance(tor, SocksOnlyTor)
self.assert_(ITorManager.providedBy(tor))
self.assertEqual(tor._reactor, reactor)
self.assertEqual(stderr.getvalue(),
" unable to find default Tor control port, using SOCKS\n")
def test_connect_custom_control_port(self):
reactor = object()
my_tor = X() # object() didn't like providedBy()
tcp = "PORT"
ep = object()
connect_d = defer.Deferred()
stderr = io.StringIO()
@ -66,9 +107,10 @@ class Tor(unittest.TestCase):
tor = self.successResultOf(d)
self.assertIs(tor, my_tor)
self.assert_(ITorManager.providedBy(tor))
self.assertEqual(stderr.getvalue(), " using Tor via control port\n")
self.assertEqual(stderr.getvalue(),
" using Tor via control port at PORT\n")
def test_connect_fails(self):
def test_connect_custom_control_port_fails(self):
reactor = object()
tcp = "port"
ep = object()
@ -84,12 +126,8 @@ class Tor(unittest.TestCase):
self.assertEqual(connect.mock_calls, [mock.call(reactor, ep)])
connect_d.errback(ConnectError())
tor = self.successResultOf(d)
self.assertIsInstance(tor, SocksOnlyTor)
self.assert_(ITorManager.providedBy(tor))
self.assertEqual(tor._reactor, reactor)
self.assertEqual(stderr.getvalue(),
" unable to find Tor control port, using SOCKS\n")
self.failureResultOf(d, ConnectError)
self.assertEqual(stderr.getvalue(), "")
class SocksOnly(unittest.TestCase):
def test_tor(self):

View File

@ -81,22 +81,24 @@ def get_tor(reactor, launch_tor=False, tor_control_port=None,
#data_directory=,
#tor_binary=,
)
elif tor_control_port:
with timing.add("find tor"):
control_ep = clientFromString(reactor, tor_control_port)
tor = yield txtorcon.connect(reactor, control_ep) # might raise
print(" using Tor via control port at %s" % tor_control_port,
file=stderr)
else:
# Let txtorcon look through a list of usual places. If that fails,
# we'll arrange to attempt the default SOCKS port
with timing.add("find tor"):
try:
# If tor_control_port is None (the default), txtorcon
# will look through a list of usual places. If it is set,
# it will look only in the place we tell it to.
if tor_control_port is not None:
tor_control_port = clientFromString(reactor,
tor_control_port)
tor = yield txtorcon.connect(reactor, tor_control_port)
print(" using Tor via control port", file=stderr)
tor = yield txtorcon.connect(reactor)
print(" using Tor via default control port", file=stderr)
except Exception:
# TODO: make this more specific. I think connect() is
# likely to throw a reactor.connectTCP -type error, like
# ConnectionFailed or ConnectionRefused or something
print(" unable to find Tor control port, using SOCKS",
print(" unable to find default Tor control port, using SOCKS",
file=stderr)
tor = SocksOnlyTor(reactor)
directlyProvides(tor, _interfaces.ITorManager)