Merge branch '252-tor-control-port'

closes #252
This commit is contained in:
Brian Warner 2017-09-14 16:51:56 -07:00
commit 7ad006950a
2 changed files with 72 additions and 20 deletions

View File

@ -50,30 +50,35 @@ class Tor(unittest.TestCase):
def test_connect(self): def test_connect(self):
reactor = object() reactor = object()
my_tor = X() # object() didn't like providedBy() my_tor = X() # object() didn't like providedBy()
tcp = "port"
connect_d = defer.Deferred() connect_d = defer.Deferred()
stderr = io.StringIO() stderr = io.StringIO()
with mock.patch("wormhole.tor_manager.txtorcon.connect", with mock.patch("wormhole.tor_manager.txtorcon.connect",
side_effect=connect_d) as connect: side_effect=connect_d) as connect:
d = get_tor(reactor, tor_control_port=tcp, stderr=stderr) 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.assertNoResult(d)
self.assertEqual(connect.mock_calls, [mock.call(reactor, tcp)]) self.assertEqual(connect.mock_calls, [mock.call(reactor)])
connect_d.callback(my_tor) connect_d.callback(my_tor)
tor = self.successResultOf(d) tor = self.successResultOf(d)
self.assertIs(tor, my_tor) self.assertIs(tor, my_tor)
self.assert_(ITorManager.providedBy(tor)) self.assert_(ITorManager.providedBy(tor))
self.assertEqual(stderr.getvalue(), " using Tor via control port\n") self.assertEqual(stderr.getvalue(),
" using Tor via default control port\n")
def test_connect_fails(self): def test_connect_fails(self):
reactor = object() reactor = object()
tcp = "port"
connect_d = defer.Deferred() connect_d = defer.Deferred()
stderr = io.StringIO() stderr = io.StringIO()
with mock.patch("wormhole.tor_manager.txtorcon.connect", with mock.patch("wormhole.tor_manager.txtorcon.connect",
side_effect=connect_d) as connect: side_effect=connect_d) as connect:
d = get_tor(reactor, tor_control_port=tcp, stderr=stderr) 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.assertNoResult(d)
self.assertEqual(connect.mock_calls, [mock.call(reactor, tcp)]) self.assertEqual(connect.mock_calls, [mock.call(reactor)])
connect_d.errback(ConnectError()) connect_d.errback(ConnectError())
tor = self.successResultOf(d) tor = self.successResultOf(d)
@ -81,7 +86,48 @@ class Tor(unittest.TestCase):
self.assert_(ITorManager.providedBy(tor)) self.assert_(ITorManager.providedBy(tor))
self.assertEqual(tor._reactor, reactor) self.assertEqual(tor._reactor, reactor)
self.assertEqual(stderr.getvalue(), self.assertEqual(stderr.getvalue(),
" unable to find Tor control port, using SOCKS\n") " 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()
with mock.patch("wormhole.tor_manager.txtorcon.connect",
side_effect=connect_d) as connect:
with mock.patch("wormhole.tor_manager.clientFromString",
side_effect=[ep]) as sfs:
d = get_tor(reactor, tor_control_port=tcp, stderr=stderr)
self.assertEqual(sfs.mock_calls, [mock.call(reactor, tcp)])
self.assertNoResult(d)
self.assertEqual(connect.mock_calls, [mock.call(reactor, ep)])
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 control port at PORT\n")
def test_connect_custom_control_port_fails(self):
reactor = object()
tcp = "port"
ep = 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=[ep]) as sfs:
d = get_tor(reactor, tor_control_port=tcp, stderr=stderr)
self.assertEqual(sfs.mock_calls, [mock.call(reactor, tcp)])
self.assertNoResult(d)
self.assertEqual(connect.mock_calls, [mock.call(reactor, ep)])
connect_d.errback(ConnectError())
self.failureResultOf(d, ConnectError)
self.assertEqual(stderr.getvalue(), "")
class SocksOnly(unittest.TestCase): class SocksOnly(unittest.TestCase):
def test_tor(self): def test_tor(self):

View File

@ -3,6 +3,7 @@ import sys
from attr import attrs, attrib from attr import attrs, attrib
from zope.interface.declarations import directlyProvides from zope.interface.declarations import directlyProvides
from twisted.internet.defer import inlineCallbacks, returnValue from twisted.internet.defer import inlineCallbacks, returnValue
from twisted.internet.endpoints import clientFromString
try: try:
import txtorcon import txtorcon
except ImportError: except ImportError:
@ -80,19 +81,24 @@ def get_tor(reactor, launch_tor=False, tor_control_port=None,
#data_directory=, #data_directory=,
#tor_binary=, #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: 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"): with timing.add("find tor"):
try: try:
# If tor_control_port is None (the default), txtorcon tor = yield txtorcon.connect(reactor)
# will look through a list of usual places. If it is set, print(" using Tor via default control port", file=stderr)
# it will look only in the place we tell it to.
tor = yield txtorcon.connect(reactor, tor_control_port)
print(" using Tor via control port", file=stderr)
except Exception: except Exception:
# TODO: make this more specific. I think connect() is # TODO: make this more specific. I think connect() is
# likely to throw a reactor.connectTCP -type error, like # likely to throw a reactor.connectTCP -type error, like
# ConnectionFailed or ConnectionRefused or something # 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) file=stderr)
tor = SocksOnlyTor(reactor) tor = SocksOnlyTor(reactor)
directlyProvides(tor, _interfaces.ITorManager) directlyProvides(tor, _interfaces.ITorManager)