fall backs to SOCKS if we can't reach control port

This commit is contained in:
Brian Warner 2017-05-23 15:01:57 -07:00
parent 46a9c9eeb9
commit 269faf190a
2 changed files with 53 additions and 19 deletions

View File

@ -4,7 +4,7 @@ from twisted.trial import unittest
from twisted.internet import defer
from twisted.internet.error import ConnectError
from ..tor_manager import get_tor
from ..tor_manager import get_tor, SocksOnlyTor
from ..errors import NoTorError
from .._interfaces import ITorManager
@ -62,7 +62,7 @@ class Tor(unittest.TestCase):
tor = self.successResultOf(d)
self.assertIs(tor, my_tor)
self.assert_(ITorManager.providedBy(tor))
self.assertEqual(stderr.getvalue(), " using Tor\n")
self.assertEqual(stderr.getvalue(), " using Tor via control port\n")
def test_connect_fails(self):
reactor = object()
@ -74,7 +74,27 @@ class Tor(unittest.TestCase):
d = get_tor(reactor, tor_control_port=tcp, stderr=stderr)
self.assertNoResult(d)
self.assertEqual(connect.mock_calls, [mock.call(reactor, tcp)])
connect_d.errback(ConnectError())
self.failureResultOf(d, ConnectError)
self.assertEqual(stderr.getvalue(),
" unable to find control port, bailing\n")
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")
class SocksOnly(unittest.TestCase):
def test_tor(self):
reactor = object()
sot = SocksOnlyTor(reactor)
fake_ep = object()
with mock.patch("wormhole.tor_manager.txtorcon.TorClientEndpoint",
return_value=fake_ep) as tce:
ep = sot.stream_via("host", "port")
self.assertIs(ep, fake_ep)
self.assertEqual(tce.mock_calls, [mock.call("host", "port",
socks_endpoint=None,
tls=False,
reactor=reactor)])

View File

@ -1,5 +1,6 @@
from __future__ import print_function, unicode_literals
import sys
from attr import attrs, attrib
from zope.interface.declarations import directlyProvides
from twisted.internet.defer import inlineCallbacks, returnValue
try:
@ -9,6 +10,18 @@ except ImportError:
from . import _interfaces, errors
from .timing import DebugTiming
@attrs
class SocksOnlyTor(object):
_reactor = attrib()
def stream_via(self, host, port, tls=False):
return txtorcon.TorClientEndpoint(
host, port,
socks_endpoint=None, # tries localhost:9050 and 9150
tls=tls,
reactor=self._reactor,
)
@inlineCallbacks
def get_tor(reactor, launch_tor=False, tor_control_port=None,
timing=None, stderr=sys.stderr):
@ -18,14 +31,15 @@ def get_tor(reactor, launch_tor=False, tor_control_port=None,
connections (and inbound onion-service listeners, if necessary).
Otherwise if tor_control_port is provided, I will attempt to connect
to an existing Tor's control port at the endpoint it specifies. I'll
to an existing Tor's control port at the endpoint it specifies. I'll
ask that Tor for its SOCKS port.
With no arguments, I will try to connect to an existing Tor's control
port at the usual places: [unix:/var/run/tor/control,
tcp:127.0.0.1:9051, tcp:127.0.0.1:9151]. If any are successful, I'll
ask that Tor for its SOCKS port. If none are successful, I'll attempt
to do SOCKS to tcp:127.0.0.1:9050.
tcp:127.0.0.1:9051, tcp:127.0.0.1:9151]. If any are successful, I'll
ask that Tor for its SOCKS port. If none are successful, I'll
attempt to do SOCKS to the usual places: [tcp:127.0.0.1:9050,
tcp:127.0.0.1:9150].
If I am unable to make a SOCKS connection, the initial connection to
the Rendezvous Server will fail, and the program will terminate.
@ -69,17 +83,17 @@ def get_tor(reactor, launch_tor=False, tor_control_port=None,
else:
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.
tor = yield txtorcon.connect(reactor, tor_control_port)
print(" using Tor", file=stderr)
print(" using Tor via control port", file=stderr)
except Exception:
#socks_desc = "tcp:127.0.0.1:9050" # fallback
#print(" using Tor (SOCKS port %s)" % socks_desc,
# file=stderr)
print(" unable to find control port, bailing",
# 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",
file=stderr)
# TODO: something nicer. I think connect() is likely to throw
# a reactor.connectTCP -type error, like ConnectionFailed or
# ConnectionRefused or something
raise
tor = SocksOnlyTor(reactor)
directlyProvides(tor, _interfaces.ITorManager)
returnValue(tor)