rewrite tor support
This shifts most reponsibility to the new txtorcon "Controller" object, where it belongs. We no longer need a list of likely control-port locations, nor do we need to keep track of the SOCKS port ourselves. The one downside is that if a control-port is not reachable, then this does not fall back to using a plain SOCKS port (usually tcp:localhost:9050). txtorcon no longer uses txsocksx, so it no longer advertises a simple way to use Tor without the control port. This shouldn't affect users who run the TorBrowserBundle, or who are running a tor daemon which they can control directly, but it may break for users who want to use a pre-existing tor daemon that they don't have permissions to speak control-port to.
This commit is contained in:
		
							parent
							
								
									805e07cd97
								
							
						
					
					
						commit
						46a9c9eeb9
					
				|  | @ -3,7 +3,7 @@ import re | |||
| import six | ||||
| from zope.interface import implementer | ||||
| from attr import attrs, attrib | ||||
| from attr.validators import provides, instance_of | ||||
| from attr.validators import provides, instance_of, optional | ||||
| from twisted.python import log | ||||
| from automat import MethodicalMachine | ||||
| from . import _interfaces | ||||
|  | @ -35,7 +35,7 @@ class Boss(object): | |||
|     _versions = attrib(validator=instance_of(dict)) | ||||
|     _reactor = attrib() | ||||
|     _journal = attrib(validator=provides(_interfaces.IJournal)) | ||||
|     _tor_manager = attrib() # TODO: ITorManager or None | ||||
|     _tor = attrib(validator=optional(provides(_interfaces.ITorManager))) | ||||
|     _timing = attrib(validator=provides(_interfaces.ITiming)) | ||||
|     m = MethodicalMachine() | ||||
|     set_trace = getattr(m, "_setTrace", lambda self, f: None) | ||||
|  | @ -53,7 +53,7 @@ class Boss(object): | |||
|         self._R = Receive(self._side, self._timing) | ||||
|         self._RC = RendezvousConnector(self._url, self._appid, self._side, | ||||
|                                        self._reactor, self._journal, | ||||
|                                        self._tor_manager, self._timing) | ||||
|                                        self._tor, self._timing) | ||||
|         self._L = Lister(self._timing) | ||||
|         self._A = Allocator(self._timing) | ||||
|         self._I = Input(self._timing) | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ from __future__ import print_function, absolute_import, unicode_literals | |||
| import os | ||||
| from six.moves.urllib_parse import urlparse | ||||
| from attr import attrs, attrib | ||||
| from attr.validators import provides, instance_of | ||||
| from attr.validators import provides, instance_of, optional | ||||
| from zope.interface import implementer | ||||
| from twisted.python import log | ||||
| from twisted.internet import defer, endpoints | ||||
|  | @ -65,7 +65,7 @@ class RendezvousConnector(object): | |||
|     _side = attrib(validator=instance_of(type(u""))) | ||||
|     _reactor = attrib() | ||||
|     _journal = attrib(validator=provides(_interfaces.IJournal)) | ||||
|     _tor_manager = attrib() # TODO: ITorManager or None | ||||
|     _tor = attrib(validator=optional(provides(_interfaces.ITorManager))) | ||||
|     _timing = attrib(validator=provides(_interfaces.ITiming)) | ||||
| 
 | ||||
|     def __attrs_post_init__(self): | ||||
|  | @ -86,8 +86,9 @@ class RendezvousConnector(object): | |||
|             self._trace(old_state="", input=what, new_state="") | ||||
| 
 | ||||
|     def _make_endpoint(self, hostname, port): | ||||
|         if self._tor_manager: | ||||
|             return self._tor_manager.get_endpoint_for(hostname, port) | ||||
|         if self._tor: | ||||
|             # TODO: when we enable TLS, maybe add tls=True here | ||||
|             return self._tor.stream_via(hostname, port) | ||||
|         return endpoints.HostnameEndpoint(self._reactor, hostname, port) | ||||
| 
 | ||||
|     def wire(self, boss, nameplate, mailbox, allocator, lister, terminator): | ||||
|  |  | |||
|  | @ -7,7 +7,7 @@ from twisted.internet.defer import inlineCallbacks, returnValue | |||
| from twisted.python import log | ||||
| from wormhole import create, input_with_completion, __version__ | ||||
| from ..transit import TransitReceiver | ||||
| from ..errors import TransferError, WormholeClosedError, NoTorError | ||||
| from ..errors import TransferError, WormholeClosedError | ||||
| from ..util import (dict_to_bytes, bytes_to_dict, bytes_to_hexstr, | ||||
|                     estimate_free_space) | ||||
| from .welcome import handle_welcome | ||||
|  | @ -45,7 +45,7 @@ class Receiver: | |||
|         assert isinstance(args.relay_url, type(u"")) | ||||
|         self.args = args | ||||
|         self._reactor = reactor | ||||
|         self._tor_manager = None | ||||
|         self._tor = None | ||||
|         self._transit_receiver = None | ||||
| 
 | ||||
|     def _msg(self, *args, **kwargs): | ||||
|  | @ -55,29 +55,26 @@ class Receiver: | |||
|     def go(self): | ||||
|         if self.args.tor: | ||||
|             with self.args.timing.add("import", which="tor_manager"): | ||||
|                 from ..tor_manager import TorManager | ||||
|             self._tor_manager = TorManager(self._reactor, | ||||
|                                            self.args.launch_tor, | ||||
|                                            self.args.tor_control_port, | ||||
|                                            timing=self.args.timing) | ||||
|             if not self._tor_manager.tor_available(): | ||||
|                 raise NoTorError() | ||||
|                 from ..tor_manager import get_tor | ||||
|             # For now, block everything until Tor has started. Soon: launch | ||||
|             # tor in parallel with everything else, make sure the TorManager | ||||
|             # tor in parallel with everything else, make sure the Tor object | ||||
|             # can lazy-provide an endpoint, and overlap the startup process | ||||
|             # with the user handing off the wormhole code | ||||
|             yield self._tor_manager.start() | ||||
|             self._tor = yield get_tor(self._reactor, | ||||
|                                       self.args.launch_tor, | ||||
|                                       self.args.tor_control_port, | ||||
|                                       timing=self.args.timing) | ||||
| 
 | ||||
|         w = create(self.args.appid or APPID, self.args.relay_url, | ||||
|                    self._reactor, | ||||
|                    tor_manager=self._tor_manager, | ||||
|                    tor=self._tor, | ||||
|                    timing=self.args.timing) | ||||
|         self._w = w # so tests can wait on events too | ||||
| 
 | ||||
|         # I wanted to do this instead: | ||||
|         # | ||||
|         #    try: | ||||
|         #        yield self._go(w, tor_manager) | ||||
|         #        yield self._go(w, tor) | ||||
|         #    finally: | ||||
|         #        yield w.close() | ||||
|         # | ||||
|  | @ -230,7 +227,7 @@ class Receiver: | |||
|     def _build_transit(self, w, sender_transit): | ||||
|         tr = TransitReceiver(self.args.transit_helper, | ||||
|                              no_listen=(not self.args.listen), | ||||
|                              tor_manager=self._tor_manager, | ||||
|                              tor=self._tor, | ||||
|                              reactor=self._reactor, | ||||
|                              timing=self.args.timing) | ||||
|         self._transit_receiver = tr | ||||
|  |  | |||
|  | @ -6,8 +6,7 @@ from twisted.python import log | |||
| from twisted.protocols import basic | ||||
| from twisted.internet import reactor | ||||
| from twisted.internet.defer import inlineCallbacks, returnValue | ||||
| from ..errors import (TransferError, WormholeClosedError, NoTorError, | ||||
|                       UnsendableFileError) | ||||
| from ..errors import (TransferError, WormholeClosedError, UnsendableFileError) | ||||
| from wormhole import create, __version__ | ||||
| from ..transit import TransitSender | ||||
| from ..util import dict_to_bytes, bytes_to_dict, bytes_to_hexstr | ||||
|  | @ -31,7 +30,7 @@ class Sender: | |||
|     def __init__(self, args, reactor): | ||||
|         self._args = args | ||||
|         self._reactor = reactor | ||||
|         self._tor_manager = None | ||||
|         self._tor = None | ||||
|         self._timing = args.timing | ||||
|         self._fd_to_send = None | ||||
|         self._transit_sender = None | ||||
|  | @ -41,22 +40,19 @@ class Sender: | |||
|         assert isinstance(self._args.relay_url, type(u"")) | ||||
|         if self._args.tor: | ||||
|             with self._timing.add("import", which="tor_manager"): | ||||
|                 from ..tor_manager import TorManager | ||||
|             self._tor_manager = TorManager(reactor, | ||||
|                                            self._args.launch_tor, | ||||
|                                            self._args.tor_control_port, | ||||
|                                            timing=self._timing) | ||||
|             if not self._tor_manager.tor_available(): | ||||
|                 raise NoTorError() | ||||
|                 from ..tor_manager import get_tor | ||||
|             # For now, block everything until Tor has started. Soon: launch | ||||
|             # tor in parallel with everything else, make sure the TorManager | ||||
|             # tor in parallel with everything else, make sure the Tor object | ||||
|             # can lazy-provide an endpoint, and overlap the startup process | ||||
|             # with the user handing off the wormhole code | ||||
|             yield self._tor_manager.start() | ||||
|             self._tor = yield get_tor(reactor, | ||||
|                                       self._args.launch_tor, | ||||
|                                       self._args.tor_control_port, | ||||
|                                       timing=self._timing) | ||||
| 
 | ||||
|         w = create(self._args.appid or APPID, self._args.relay_url, | ||||
|                    self._reactor, | ||||
|                    tor_manager=self._tor_manager, | ||||
|                    tor=self._tor, | ||||
|                    timing=self._timing) | ||||
|         d = self._go(w) | ||||
| 
 | ||||
|  | @ -151,7 +147,7 @@ class Sender: | |||
|         if self._fd_to_send: | ||||
|             ts = TransitSender(args.transit_helper, | ||||
|                                no_listen=(not args.listen), | ||||
|                                tor_manager=self._tor_manager, | ||||
|                                tor=self._tor, | ||||
|                                reactor=self._reactor, | ||||
|                                timing=self._timing) | ||||
|             self._transit_sender = ts | ||||
|  |  | |||
|  | @ -4,9 +4,10 @@ from textwrap import fill, dedent | |||
| from humanize import naturalsize | ||||
| import mock | ||||
| import click.testing | ||||
| from zope.interface import implementer | ||||
| from twisted.trial import unittest | ||||
| from twisted.python import procutils, log | ||||
| from twisted.internet import defer, endpoints, reactor | ||||
| from twisted.internet import endpoints, reactor | ||||
| from twisted.internet.utils import getProcessOutputAndValue | ||||
| from twisted.internet.defer import gatherResults, inlineCallbacks, returnValue | ||||
| from .. import __version__ | ||||
|  | @ -14,6 +15,7 @@ from .common import ServerBase, config | |||
| from ..cli import cmd_send, cmd_receive, welcome, cli | ||||
| from ..errors import (TransferError, WrongPasswordError, WelcomeError, | ||||
|                       UnsendableFileError) | ||||
| from .._interfaces import ITorManager | ||||
| from wormhole.server.cmd_server import MyPlugin | ||||
| from wormhole.server.cli import server | ||||
| 
 | ||||
|  | @ -297,15 +299,12 @@ class ScriptVersion(ServerBase, ScriptsBase, unittest.TestCase): | |||
|         self.failUnlessEqual(ver.strip(), "magic-wormhole {}".format(__version__)) | ||||
|         self.failUnlessEqual(rc, 0) | ||||
| 
 | ||||
| class FakeTorManager: | ||||
| @implementer(ITorManager) | ||||
| class FakeTor: | ||||
|     # use normal endpoints, but record the fact that we were asked | ||||
|     def __init__(self): | ||||
|         self.endpoints = [] | ||||
|     def tor_available(self): | ||||
|         return True | ||||
|     def start(self): | ||||
|         return defer.succeed(None) | ||||
|     def get_endpoint_for(self, host, port): | ||||
|     def stream_via(self, host, port): | ||||
|         self.endpoints.append((host, port)) | ||||
|         return endpoints.HostnameEndpoint(reactor, host, port) | ||||
| 
 | ||||
|  | @ -456,16 +455,16 @@ class PregeneratedCode(ServerBase, ScriptsBase, unittest.TestCase): | |||
|             if fake_tor: | ||||
|                 send_cfg.tor = True | ||||
|                 send_cfg.transit_helper = self.transit | ||||
|                 tx_tm = FakeTorManager() | ||||
|                 with mock.patch("wormhole.tor_manager.TorManager", | ||||
|                 tx_tm = FakeTor() | ||||
|                 with mock.patch("wormhole.tor_manager.get_tor", | ||||
|                                 return_value=tx_tm, | ||||
|                                 ) as mtx_tm: | ||||
|                     send_d = cmd_send.send(send_cfg) | ||||
| 
 | ||||
|                 recv_cfg.tor = True | ||||
|                 recv_cfg.transit_helper = self.transit | ||||
|                 rx_tm = FakeTorManager() | ||||
|                 with mock.patch("wormhole.tor_manager.TorManager", | ||||
|                 rx_tm = FakeTor() | ||||
|                 with mock.patch("wormhole.tor_manager.get_tor", | ||||
|                                 return_value=rx_tm, | ||||
|                                 ) as mrx_tm: | ||||
|                     receive_d = cmd_receive.receive(recv_cfg) | ||||
|  |  | |||
|  | @ -3,335 +3,78 @@ import mock, io | |||
| from twisted.trial import unittest | ||||
| from twisted.internet import defer | ||||
| from twisted.internet.error import ConnectError | ||||
| from six import next | ||||
| 
 | ||||
| from ..tor_manager import TorManager, DEFAULT_VALUE | ||||
| from ..tor_manager import get_tor | ||||
| from ..errors import NoTorError | ||||
| from .._interfaces import ITorManager | ||||
| 
 | ||||
| class X(): | ||||
|     pass | ||||
| 
 | ||||
| class Tor(unittest.TestCase): | ||||
|     def test_create(self): | ||||
|         tm = TorManager(None) | ||||
|         del tm | ||||
|     def test_no_txtorcon(self): | ||||
|         with mock.patch("wormhole.tor_manager.txtorcon", None): | ||||
|             self.failureResultOf(get_tor(None), NoTorError) | ||||
| 
 | ||||
|     def test_bad_args(self): | ||||
|         e = self.assertRaises(TypeError, | ||||
|                               TorManager, None, launch_tor="not boolean") | ||||
|         self.assertEqual(str(e), "launch_tor= must be boolean") | ||||
|         e = self.assertRaises(TypeError, | ||||
|                               TorManager, None, tor_control_port=1234) | ||||
|         self.assertEqual(str(e), "tor_control_port= must be str or None") | ||||
|         e = self.assertRaises(ValueError, | ||||
|                               TorManager, None, launch_tor=True, | ||||
|                               tor_control_port="tcp:127.0.0.1:1234") | ||||
|         self.assertEqual(str(e), | ||||
|         f = self.failureResultOf(get_tor(None, launch_tor="not boolean"), | ||||
|                                  TypeError) | ||||
|         self.assertEqual(str(f.value), "launch_tor= must be boolean") | ||||
| 
 | ||||
|         f = self.failureResultOf(get_tor(None, tor_control_port=1234), | ||||
|                                  TypeError) | ||||
|         self.assertEqual(str(f.value), "tor_control_port= must be str or None") | ||||
|         f = self.failureResultOf(get_tor(None, launch_tor=True, | ||||
|                                          tor_control_port="tcp:127.0.0.1:1234"), | ||||
|                                  ValueError) | ||||
|         self.assertEqual(str(f.value), | ||||
|                          "cannot combine --launch-tor and --tor-control-port=") | ||||
| 
 | ||||
|     def test_start_launch_tor(self): | ||||
|         reactor = object() | ||||
|         stderr = io.StringIO() | ||||
|         tm = TorManager(reactor, launch_tor=True, stderr=stderr) | ||||
|         dlt_d = defer.Deferred() | ||||
|         tm._do_launch_tor = mock.Mock(return_value=dlt_d) | ||||
|         tm._try_control_port = mock.Mock() | ||||
|         d = tm.start() | ||||
|         self.assertNoResult(d) | ||||
|         tsep = object() | ||||
|         with mock.patch("wormhole.tor_manager.clientFromString", | ||||
|                         return_value=tsep) as cfs: | ||||
|             dlt_d.callback(("tproto", "tconfig", "socks_desc")) | ||||
|             res = self.successResultOf(d) | ||||
|             self.assertEqual(res, None) | ||||
|             self.assertEqual(tm._tor_protocol, "tproto") | ||||
|             self.assertEqual(tm._tor_config, "tconfig") | ||||
|             self.assertEqual(tm._tor_socks_endpoint, tsep) | ||||
|             self.assertEqual(tm._do_launch_tor.mock_calls, [mock.call()]) | ||||
|             self.assertEqual(tm._try_control_port.mock_calls, []) | ||||
|             self.assertEqual(cfs.mock_calls, [mock.call(reactor, "socks_desc")]) | ||||
| 
 | ||||
|     def test_start_control_port_default_failure(self): | ||||
|         reactor = object() | ||||
|         stderr = io.StringIO() | ||||
|         tm = TorManager(reactor, stderr=stderr) | ||||
|         tm._do_launch_tor = mock.Mock() | ||||
|         tcp_ds = [defer.Deferred() for i in range(5)] | ||||
|         tcp_ds_iter = iter(tcp_ds) | ||||
|         attempted_control_ports = [] | ||||
|         def next_d(control_port): | ||||
|             attempted_control_ports.append(control_port) | ||||
|             return next(tcp_ds_iter) | ||||
|         tm._try_control_port = mock.Mock(side_effect=next_d) | ||||
|         d = tm.start() | ||||
|         tsep = object() | ||||
|         with mock.patch("wormhole.tor_manager.clientFromString", | ||||
|                         return_value=tsep) as cfs: | ||||
|             self.assertNoResult(d) | ||||
|             self.assertEqual(attempted_control_ports, | ||||
|                              ["unix:/var/run/tor/control"]) | ||||
|             self.assertEqual(tm._try_control_port.mock_calls, | ||||
|                              [mock.call("unix:/var/run/tor/control")]) | ||||
|             tcp_ds[0].callback((None, None, None)) | ||||
| 
 | ||||
|             self.assertNoResult(d) | ||||
|             self.assertEqual(attempted_control_ports, | ||||
|                              ["unix:/var/run/tor/control", | ||||
|                               "tcp:127.0.0.1:9051", | ||||
|                               ]) | ||||
|             self.assertEqual(tm._try_control_port.mock_calls, | ||||
|                              [mock.call("unix:/var/run/tor/control"), | ||||
|                               mock.call("tcp:127.0.0.1:9051"), | ||||
|                               ]) | ||||
|             tcp_ds[1].callback((None, None, None)) | ||||
| 
 | ||||
|             self.assertNoResult(d) | ||||
|             self.assertEqual(attempted_control_ports, | ||||
|                              ["unix:/var/run/tor/control", | ||||
|                               "tcp:127.0.0.1:9051", | ||||
|                               "tcp:127.0.0.1:9151", | ||||
|                               ]) | ||||
|             self.assertEqual(tm._try_control_port.mock_calls, | ||||
|                              [mock.call("unix:/var/run/tor/control"), | ||||
|                               mock.call("tcp:127.0.0.1:9051"), | ||||
|                               mock.call("tcp:127.0.0.1:9151"), | ||||
|                               ]) | ||||
|             tcp_ds[2].callback((None, None, None)) | ||||
| 
 | ||||
|             res = self.successResultOf(d) | ||||
|             self.assertEqual(res, None) | ||||
|             self.assertEqual(tm._tor_protocol, None) | ||||
|             self.assertEqual(tm._tor_config, None) | ||||
|             self.assertEqual(tm._tor_socks_endpoint, tsep) | ||||
|             self.assertEqual(tm._do_launch_tor.mock_calls, []) | ||||
|             self.assertEqual(cfs.mock_calls, | ||||
|                              [mock.call(reactor, "tcp:127.0.0.1:9050")]) | ||||
| 
 | ||||
|     def test_start_control_port_default(self): | ||||
|         reactor = object() | ||||
|         stderr = io.StringIO() | ||||
|         tm = TorManager(reactor, stderr=stderr) | ||||
|         tm._do_launch_tor = mock.Mock() | ||||
|         tcp_d = defer.Deferred() | ||||
|         # let it succeed on the first try | ||||
|         tm._try_control_port = mock.Mock(return_value=tcp_d) | ||||
|         d = tm.start() | ||||
|         self.assertNoResult(d) | ||||
|         tsep = object() | ||||
|         with mock.patch("wormhole.tor_manager.clientFromString", | ||||
|                         return_value=tsep) as cfs: | ||||
|             tcp_d.callback(("tproto", "tconfig", "socks_desc")) | ||||
|             res = self.successResultOf(d) | ||||
|             self.assertEqual(res, None) | ||||
|             self.assertEqual(tm._tor_protocol, "tproto") | ||||
|             self.assertEqual(tm._tor_config, "tconfig") | ||||
|             self.assertEqual(tm._tor_socks_endpoint, tsep) | ||||
|             self.assertEqual(tm._do_launch_tor.mock_calls, []) | ||||
|             self.assertEqual(tm._try_control_port.mock_calls, | ||||
|                              [mock.call("unix:/var/run/tor/control")]) | ||||
|             self.assertEqual(cfs.mock_calls, [mock.call(reactor, "socks_desc")]) | ||||
| 
 | ||||
|     def test_start_control_port_non_default_failure(self): | ||||
|         reactor = object() | ||||
|         my_port = "my_port" | ||||
|         stderr = io.StringIO() | ||||
|         tm = TorManager(reactor, tor_control_port=my_port, stderr=stderr) | ||||
|         tm._do_launch_tor = mock.Mock() | ||||
|         tcp_ds = [defer.Deferred() for i in range(5)] | ||||
|         tcp_ds_iter = iter(tcp_ds) | ||||
|         attempted_control_ports = [] | ||||
|         def next_d(control_port): | ||||
|             attempted_control_ports.append(control_port) | ||||
|             return next(tcp_ds_iter) | ||||
|         tm._try_control_port = mock.Mock(side_effect=next_d) | ||||
|         d = tm.start() | ||||
|         tsep = object() | ||||
|         with mock.patch("wormhole.tor_manager.clientFromString", | ||||
|                         return_value=tsep) as cfs: | ||||
|             self.assertNoResult(d) | ||||
|             self.assertEqual(attempted_control_ports, [my_port]) | ||||
|             self.assertEqual(tm._try_control_port.mock_calls, | ||||
|                              [mock.call(my_port)]) | ||||
|             tcp_ds[0].callback((None, None, None)) | ||||
| 
 | ||||
|             res = self.successResultOf(d) | ||||
|             self.assertEqual(res, None) | ||||
|             self.assertEqual(tm._tor_protocol, None) | ||||
|             self.assertEqual(tm._tor_config, None) | ||||
|             self.assertEqual(tm._tor_socks_endpoint, tsep) | ||||
|             self.assertEqual(tm._do_launch_tor.mock_calls, []) | ||||
|             self.assertEqual(cfs.mock_calls, | ||||
|                              [mock.call(reactor, "tcp:127.0.0.1:9050")]) | ||||
| 
 | ||||
|     def test_start_control_port_non_default(self): | ||||
|         reactor = object() | ||||
|         my_port = "my_port" | ||||
|         stderr = io.StringIO() | ||||
|         tm = TorManager(reactor, tor_control_port=my_port, stderr=stderr) | ||||
|         tm._do_launch_tor = mock.Mock() | ||||
|         tcp_d = defer.Deferred() | ||||
|         tm._try_control_port = mock.Mock(return_value=tcp_d) | ||||
|         d = tm.start() | ||||
|         self.assertNoResult(d) | ||||
|         tsep = object() | ||||
|         with mock.patch("wormhole.tor_manager.clientFromString", | ||||
|                         return_value=tsep) as cfs: | ||||
|             tcp_d.callback(("tproto", "tconfig", "socks_desc")) | ||||
|             res = self.successResultOf(d) | ||||
|             self.assertEqual(res, None) | ||||
|             self.assertEqual(tm._tor_protocol, "tproto") | ||||
|             self.assertEqual(tm._tor_config, "tconfig") | ||||
|             self.assertEqual(tm._tor_socks_endpoint, tsep) | ||||
|             self.assertEqual(tm._do_launch_tor.mock_calls, []) | ||||
|             self.assertEqual(tm._try_control_port.mock_calls, | ||||
|                              [mock.call(my_port)]) | ||||
|             self.assertEqual(cfs.mock_calls, [mock.call(reactor, "socks_desc")]) | ||||
| 
 | ||||
|     def test_launch(self): | ||||
|         reactor = object() | ||||
|         my_tor = X() # object() didn't like providedBy() | ||||
|         launch_d = defer.Deferred() | ||||
|         stderr = io.StringIO() | ||||
|         tc = mock.Mock() | ||||
|         mock_TorConfig = mock.patch("wormhole.tor_manager.TorConfig", | ||||
|                                     return_value=tc) | ||||
|         lt_d = defer.Deferred() | ||||
|         mock_launch_tor = mock.patch("wormhole.tor_manager.launch_tor", | ||||
|                                      return_value=lt_d) | ||||
|         mock_allocate_tcp_port = mock.patch("wormhole.tor_manager.allocate_tcp_port", | ||||
|                                             return_value=12345) | ||||
|         mock_clientFromString = mock.patch("wormhole.tor_manager.clientFromString") | ||||
|         with mock_TorConfig as mtc: | ||||
|             with mock_launch_tor as mlt: | ||||
|                 with mock_allocate_tcp_port as matp: | ||||
|                     with mock_clientFromString as mcfs: | ||||
|                         tm = TorManager(reactor, launch_tor=True, stderr=stderr) | ||||
|                         d = tm.start() | ||||
|                         self.assertNoResult(d) | ||||
|                         tp = mock.Mock() | ||||
|                         lt_d.callback(tp) | ||||
|         res = self.successResultOf(d) | ||||
|         self.assertEqual(res, None) | ||||
|         self.assertIs(tm._tor_protocol, tp) | ||||
|         self.assertIs(tm._tor_config, tc) | ||||
|         self.assertEqual(mtc.mock_calls, [mock.call()]) | ||||
|         self.assertEqual(mlt.mock_calls, [mock.call(tc, reactor)]) | ||||
|         self.assertEqual(matp.mock_calls, [mock.call()]) | ||||
|         self.assertEqual(mcfs.mock_calls, | ||||
|                          [mock.call(reactor, "tcp:127.0.0.1:12345")]) | ||||
|         with mock.patch("wormhole.tor_manager.txtorcon.launch", | ||||
|                         side_effect=launch_d) as launch: | ||||
|             d = get_tor(reactor, launch_tor=True, stderr=stderr) | ||||
|             self.assertNoResult(d) | ||||
|             self.assertEqual(launch.mock_calls, [mock.call(reactor)]) | ||||
|             launch_d.callback(my_tor) | ||||
|             tor = self.successResultOf(d) | ||||
|             self.assertIs(tor, my_tor) | ||||
|             self.assert_(ITorManager.providedBy(tor)) | ||||
|             self.assertEqual(stderr.getvalue(), | ||||
|                              " launching a new Tor process, this may take a while..\n") | ||||
| 
 | ||||
|     def _do_test_try_control_port(self, socks_ports, exp_socks_desc, | ||||
|                                   btc_exception=None, tcfp_exception=None): | ||||
|     def test_connect(self): | ||||
|         reactor = object() | ||||
|         my_tor = X() # object() didn't like providedBy() | ||||
|         tcp = "port" | ||||
|         connect_d = defer.Deferred() | ||||
|         stderr = io.StringIO() | ||||
|         ep = object() | ||||
|         mock_clientFromString = mock.patch("wormhole.tor_manager.clientFromString", | ||||
|                                            return_value=ep) | ||||
|         tproto = mock.Mock() | ||||
|         btc_d = defer.Deferred() | ||||
|         mock_build_tor_connection = mock.patch("wormhole.tor_manager.build_tor_connection", return_value=btc_d) | ||||
|         torconfig = mock.Mock() | ||||
|         tc = mock.Mock() | ||||
|         tc.SocksPort = iter(socks_ports) | ||||
|         tc_d = defer.Deferred() | ||||
|         torconfig.from_protocol = mock.Mock(return_value=tc_d) | ||||
|         mock_torconfig = mock.patch("wormhole.tor_manager.TorConfig", torconfig) | ||||
|         with mock.patch("wormhole.tor_manager.txtorcon.connect", | ||||
|                         side_effect=connect_d) as connect: | ||||
|             d = get_tor(reactor, tor_control_port=tcp, stderr=stderr) | ||||
|             self.assertNoResult(d) | ||||
|             self.assertEqual(connect.mock_calls, [mock.call(reactor, tcp)]) | ||||
|             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\n") | ||||
| 
 | ||||
|         control_port = object() | ||||
| 
 | ||||
|         with mock_clientFromString as cfs: | ||||
|             with mock_build_tor_connection as btc: | ||||
|                 with mock_torconfig: | ||||
|                     tm = TorManager(reactor, stderr=stderr) | ||||
|                     d = tm._try_control_port(control_port) | ||||
|                     # waiting in 'tproto = yield build_tor_connection(..)' | ||||
|                     self.assertNoResult(d) | ||||
|                     self.assertEqual(cfs.mock_calls, | ||||
|                                      [mock.call(reactor, control_port)]) | ||||
|                     self.assertEqual(btc.mock_calls, | ||||
|                                      [mock.call(ep, build_state=False)]) | ||||
|                     self.assertEqual(torconfig.from_protocol.mock_calls, []) | ||||
| 
 | ||||
|                     btc_d.callback(tproto) | ||||
|                     # waiting in 'tconfig = yield TorConfig.from_protocol(..)' | ||||
|                     self.assertNoResult(d) | ||||
|                     self.assertEqual(torconfig.from_protocol.mock_calls, | ||||
|                                      [mock.call(tproto)]) | ||||
| 
 | ||||
|                     tc_d.callback(tc) | ||||
|                     res = self.successResultOf(d) | ||||
|                     self.assertEqual(res, (tproto, tc, exp_socks_desc)) | ||||
| 
 | ||||
|     def test_try_control_port(self): | ||||
|         self._do_test_try_control_port(["1234 ignorestuff", | ||||
|                                         "unix:/foo WorldWritable"], | ||||
|                                        "tcp:127.0.0.1:1234") | ||||
|         self._do_test_try_control_port(["unix:/foo WorldWritable", | ||||
|                                         "1234 ignorestuff"], | ||||
|                                        "unix:/foo") | ||||
|         self._do_test_try_control_port([DEFAULT_VALUE, | ||||
|                                         "1234"], | ||||
|                                        "tcp:127.0.0.1:9050") | ||||
| 
 | ||||
|     def _do_test_try_control_port_exception(self, btc_exc=None, tcfp_exc=None): | ||||
|     def test_connect_fails(self): | ||||
|         reactor = object() | ||||
|         tcp = "port" | ||||
|         connect_d = defer.Deferred() | ||||
|         stderr = io.StringIO() | ||||
|         ep = object() | ||||
|         mock_clientFromString = mock.patch("wormhole.tor_manager.clientFromString", | ||||
|                                            return_value=ep) | ||||
|         tproto = mock.Mock() | ||||
|         btc_d = defer.Deferred() | ||||
|         mock_build_tor_connection = mock.patch("wormhole.tor_manager.build_tor_connection", return_value=btc_d) | ||||
|         torconfig = mock.Mock() | ||||
|         tcfp_d = defer.Deferred() | ||||
|         torconfig.from_protocol = mock.Mock(return_value=tcfp_d) | ||||
|         mock_torconfig = mock.patch("wormhole.tor_manager.TorConfig", torconfig) | ||||
| 
 | ||||
|         control_port = object() | ||||
| 
 | ||||
|         with mock_clientFromString: | ||||
|             with mock_build_tor_connection: | ||||
|                 with mock_torconfig: | ||||
|                     tm = TorManager(reactor, stderr=stderr) | ||||
|                     d = tm._try_control_port(control_port) | ||||
|                     # waiting in 'tproto = yield build_tor_connection(..)' | ||||
|                     self.assertNoResult(d) | ||||
| 
 | ||||
|                     if btc_exc: | ||||
|                         btc_d.errback(btc_exc) | ||||
|                     else: | ||||
|                         btc_d.callback(tproto) | ||||
|                         assert tcfp_exc | ||||
|                         tcfp_d.errback(tcfp_exc) | ||||
| 
 | ||||
|                     res = self.successResultOf(d) | ||||
|                     self.assertEqual(res, (None, None, None)) | ||||
| 
 | ||||
|     def test_try_control_port_error(self): | ||||
|         self._do_test_try_control_port_exception(btc_exc=ValueError()) | ||||
|         self._do_test_try_control_port_exception(btc_exc=ConnectError()) | ||||
|         self._do_test_try_control_port_exception(tcfp_exc=ValueError()) | ||||
|         self._do_test_try_control_port_exception(tcfp_exc=ConnectError()) | ||||
| 
 | ||||
|     def test_badaddr(self): | ||||
|         tm = TorManager(None) | ||||
|         isnon = tm.is_non_public_numeric_address | ||||
|         self.assertTrue(isnon("10.0.0.1")) | ||||
|         self.assertTrue(isnon("127.0.0.1")) | ||||
|         self.assertTrue(isnon("192.168.78.254")) | ||||
|         self.assertTrue(isnon("::1")) | ||||
|         self.assertFalse(isnon("8.8.8.8")) | ||||
|         self.assertFalse(isnon("example.org")) | ||||
| 
 | ||||
|     def test_endpoint(self): | ||||
|         reactor = object() | ||||
|         stderr = io.StringIO() | ||||
|         tm = TorManager(reactor, stderr=stderr) | ||||
|         tm._tor_socks_endpoint = tse = object() | ||||
|         exp_ep = object() | ||||
|         with mock.patch("wormhole.tor_manager.TorClientEndpoint", | ||||
|                         return_value=exp_ep) as tce: | ||||
|             ep = tm.get_endpoint_for("example.com", 1234) | ||||
|             self.assertIs(ep, exp_ep) | ||||
|             self.assertEqual(tce.mock_calls, | ||||
|                              [mock.call(b"example.com", 1234, | ||||
|                                         socks_endpoint=tse)]) | ||||
|         with mock.patch("wormhole.tor_manager.TorClientEndpoint", | ||||
|                         return_value=exp_ep) as tce: | ||||
|             ep = tm.get_endpoint_for("127.0.0.1", 1234) | ||||
|             self.assertEqual(ep, None) | ||||
|             self.assertEqual(tce.mock_calls, []) | ||||
|         with mock.patch("wormhole.tor_manager.txtorcon.connect", | ||||
|                         side_effect=connect_d) as connect: | ||||
|             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") | ||||
|  |  | |||
|  | @ -149,14 +149,14 @@ class Hints(unittest.TestCase): | |||
|         self.assertIsInstance(efho(transit.DirectTCPV1Hint("host", 1234, 0.0)), | ||||
|                               endpoints.HostnameEndpoint) | ||||
|         self.assertEqual(efho("unknown:stuff:yowza:pivlor"), None) | ||||
|         # c._tor_manager is currently None | ||||
|         # c._tor is currently None | ||||
|         self.assertEqual(efho(transit.TorTCPV1Hint("host", "port", 0)), None) | ||||
|         c._tor_manager = mock.Mock() | ||||
|         c._tor = mock.Mock() | ||||
|         def tor_ep(hostname, port): | ||||
|             if hostname == "non-public": | ||||
|                 return None | ||||
|             return ("tor_ep", hostname, port) | ||||
|         c._tor_manager.get_endpoint_for = mock.Mock(side_effect=tor_ep) | ||||
|         c._tor.stream_via = mock.Mock(side_effect=tor_ep) | ||||
|         self.assertEqual(efho(transit.DirectTCPV1Hint("host", 1234, 0.0)), | ||||
|                          ("tor_ep", "host", 1234)) | ||||
|         self.assertEqual(efho(transit.TorTCPV1Hint("host2.onion", 1234, 0.0)), | ||||
|  | @ -1470,7 +1470,7 @@ class Transit(unittest.TestCase): | |||
|     @inlineCallbacks | ||||
|     def test_success_direct_tor(self): | ||||
|         clock = task.Clock() | ||||
|         s = transit.TransitSender("", tor_manager=mock.Mock(), reactor=clock) | ||||
|         s = transit.TransitSender("", tor=mock.Mock(), reactor=clock) | ||||
|         s.set_transit_key(b"key") | ||||
|         hints = yield s.get_connection_hints() # start the listener | ||||
|         del hints | ||||
|  | @ -1491,7 +1491,7 @@ class Transit(unittest.TestCase): | |||
|     @inlineCallbacks | ||||
|     def test_success_direct_tor_relay(self): | ||||
|         clock = task.Clock() | ||||
|         s = transit.TransitSender("", tor_manager=mock.Mock(), reactor=clock) | ||||
|         s = transit.TransitSender("", tor=mock.Mock(), reactor=clock) | ||||
|         s.set_transit_key(b"key") | ||||
|         hints = yield s.get_connection_hints() # start the listener | ||||
|         del hints | ||||
|  |  | |||
|  | @ -1,171 +1,85 @@ | |||
| from __future__ import print_function, unicode_literals | ||||
| import sys, re | ||||
| import six | ||||
| from zope.interface import implementer | ||||
| import sys | ||||
| from zope.interface.declarations import directlyProvides | ||||
| from twisted.internet.defer import inlineCallbacks, returnValue | ||||
| from twisted.internet.error import ConnectError | ||||
| from twisted.internet.endpoints import clientFromString | ||||
| try: | ||||
|     from txtorcon import (TorConfig, launch_tor, build_tor_connection, | ||||
|                           DEFAULT_VALUE, TorClientEndpoint) | ||||
|     import txtorcon | ||||
| except ImportError: | ||||
|     TorConfig = None | ||||
|     launch_tor = None | ||||
|     build_tor_connection = None | ||||
|     TorClientEndpoint = None | ||||
|     DEFAULT_VALUE = "DEFAULT_VALUE" | ||||
| import ipaddress | ||||
| from . import _interfaces | ||||
|     txtorcon = None | ||||
| from . import _interfaces, errors | ||||
| from .timing import DebugTiming | ||||
| from .transit import allocate_tcp_port | ||||
| 
 | ||||
| @inlineCallbacks | ||||
| def get_tor(reactor, launch_tor=False, tor_control_port=None, | ||||
|             timing=None, stderr=sys.stderr): | ||||
|     """ | ||||
|     If launch_tor=True, I will try to launch a new Tor process, ask it | ||||
|     for its SOCKS and control ports, and use those for outbound | ||||
|     connections (and inbound onion-service listeners, if necessary). | ||||
| 
 | ||||
| @implementer(_interfaces.ITorManager) | ||||
| class TorManager: | ||||
|     def __init__(self, reactor, launch_tor=False, tor_control_port=None, | ||||
|                  timing=None, stderr=sys.stderr): | ||||
|         """ | ||||
|         If launch_tor=True, I will try to launch a new Tor process, ask it | ||||
|         for its SOCKS and control ports, and use those for outbound | ||||
|         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 | ||||
|     ask that Tor for its SOCKS port. | ||||
| 
 | ||||
|         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 | ||||
|         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. | ||||
| 
 | ||||
|         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. | ||||
|     If I am unable to make a SOCKS connection, the initial connection to | ||||
|     the Rendezvous Server will fail, and the program will terminate. | ||||
| 
 | ||||
|         If I am unable to make a SOCKS connection, the initial connection to | ||||
|         the Rendezvous Server will fail, and the program will terminate. | ||||
|     Control-port connections can only succeed if I can authenticate (by | ||||
|     reading a cookie file named by the Tor process), so the current user | ||||
|     must have permission to read that file (either they started Tor, e.g. | ||||
|     TorBrowser, or they are in a unix group that's been given access, | ||||
|     e.g. debian-tor). | ||||
|     """ | ||||
|     # rationale: launching a new Tor takes a long time, so only do it if | ||||
|     # the user specifically asks for it with --launch-tor. Using an | ||||
|     # existing Tor should be much faster, but still requires general | ||||
|     # permission via --tor. | ||||
| 
 | ||||
|         Control-port connections can only succeed if I can authenticate (by | ||||
|         reading a cookie file named by the Tor process), so the current user | ||||
|         must have permission to read that file (either they started Tor, e.g. | ||||
|         TorBrowser, or they are in a unix group that's been given access, | ||||
|         e.g. debian-tor). | ||||
|         """ | ||||
|         # rationale: launching a new Tor takes a long time, so only do it if | ||||
|         # the user specifically asks for it with --launch-tor. Using an | ||||
|         # existing Tor should be much faster, but still requires general | ||||
|         # permission via --tor. | ||||
|     if not txtorcon: | ||||
|         raise errors.NoTorError() | ||||
| 
 | ||||
|         self._reactor = reactor | ||||
|         if not isinstance(launch_tor, bool): # note: False is int | ||||
|             raise TypeError("launch_tor= must be boolean") | ||||
|         if not isinstance(tor_control_port, (type(""), type(None))): | ||||
|             raise TypeError("tor_control_port= must be str or None") | ||||
|         if launch_tor and tor_control_port is not None: | ||||
|             raise ValueError("cannot combine --launch-tor and --tor-control-port=") | ||||
|         self._launch_tor = launch_tor | ||||
|         self._tor_control_port = tor_control_port | ||||
|         self._timing = timing or DebugTiming() | ||||
|         self._stderr = stderr | ||||
|     if not isinstance(launch_tor, bool): # note: False is int | ||||
|         raise TypeError("launch_tor= must be boolean") | ||||
|     if not isinstance(tor_control_port, (type(""), type(None))): | ||||
|         raise TypeError("tor_control_port= must be str or None") | ||||
|     assert tor_control_port != "" | ||||
|     if launch_tor and tor_control_port is not None: | ||||
|         raise ValueError("cannot combine --launch-tor and --tor-control-port=") | ||||
|     timing = timing or DebugTiming() | ||||
| 
 | ||||
|     def tor_available(self): | ||||
|         # unit tests mock out everything we get from txtorcon, so we can test | ||||
|         # this class under py3 even if txtorcon isn't installed. But the real | ||||
|         # commands need to know if they have Tor or not. | ||||
|         return bool(TorConfig) | ||||
|     # Connect to an existing Tor, or create a new one. If we need to | ||||
|     # launch an onion service, then we need a working control port (and | ||||
|     # authentication cookie). If we're only acting as a client, we don't | ||||
|     # need the control port. | ||||
| 
 | ||||
|     @inlineCallbacks | ||||
|     def start(self): | ||||
|         # Connect to an existing Tor, or create a new one. If we need to | ||||
|         # launch an onion service, then we need a working control port (and | ||||
|         # authentication cookie). If we're only acting as a client, we don't | ||||
|         # need the control port. | ||||
| 
 | ||||
|         if self._launch_tor: | ||||
|             print(" launching a new Tor process, this may take a while..", | ||||
|                   file=self._stderr) | ||||
|             with self._timing.add("launch tor"): | ||||
|                 (tproto, tconfig, socks_desc) = yield self._do_launch_tor() | ||||
|         else: | ||||
|             control_ports = ["unix:/var/run/tor/control", # debian tor package | ||||
|                              "tcp:127.0.0.1:9051", # standard Tor | ||||
|                              "tcp:127.0.0.1:9151", # TorBrowser | ||||
|                              ] | ||||
|             if self._tor_control_port: | ||||
|                 control_ports = [self._tor_control_port] | ||||
|             with self._timing.add("find tor"): | ||||
|                 for control_port in control_ports: | ||||
|                     (tproto, tconfig, | ||||
|                      socks_desc) = yield self._try_control_port(control_port) | ||||
|                     if tproto: | ||||
|                         print(" using Tor (control port %s) (SOCKS port %s)" | ||||
|                               % (control_port, socks_desc), | ||||
|                               file=self._stderr) | ||||
|                         break | ||||
|                 else: | ||||
|                     tproto = None | ||||
|                     tconfig = None | ||||
|                     socks_desc = "tcp:127.0.0.1:9050" # fallback | ||||
|                     print(" using Tor (SOCKS port %s)" % socks_desc, | ||||
|                           file=self._stderr) | ||||
| 
 | ||||
|         self._tor_protocol = tproto | ||||
|         self._tor_config = tconfig | ||||
|         self._tor_socks_endpoint = clientFromString(self._reactor, socks_desc) | ||||
| 
 | ||||
|     @inlineCallbacks | ||||
|     def _do_launch_tor(self): | ||||
|         tconfig = TorConfig() | ||||
|         #tconfig.ControlPort = allocate_tcp_port() # defaults to 9052 | ||||
|         tconfig.SocksPort = allocate_tcp_port() | ||||
|         socks_desc = "tcp:127.0.0.1:%d" % tconfig.SocksPort | ||||
|         # this could take tor_binary= | ||||
|         tproto = yield launch_tor(tconfig, self._reactor) | ||||
|         returnValue((tproto, tconfig, socks_desc)) | ||||
| 
 | ||||
|     @inlineCallbacks | ||||
|     def _try_control_port(self, control_port): | ||||
|         NOPE = (None, None, None) | ||||
|         ep = clientFromString(self._reactor, control_port) | ||||
|         try: | ||||
|             tproto = yield build_tor_connection(ep, build_state=False) | ||||
|             # now wait for bootstrap | ||||
|             tconfig = yield TorConfig.from_protocol(tproto) | ||||
|         except (ValueError, ConnectError): | ||||
|             returnValue(NOPE) | ||||
|         socks_ports = list(tconfig.SocksPort) | ||||
|         socks_port = socks_ports[0] # TODO: when might there be multiple? | ||||
|         # I've seen "9050", and "unix:/var/run/tor/socks WorldWritable" | ||||
|         pieces = socks_port.split() | ||||
|         p = pieces[0] | ||||
|         if p == DEFAULT_VALUE: | ||||
|             socks_desc = "tcp:127.0.0.1:9050" | ||||
|         elif re.search('^\d+$', p): | ||||
|             socks_desc = "tcp:127.0.0.1:%s" % p | ||||
|         else: | ||||
|             socks_desc = p | ||||
|         returnValue((tproto, tconfig, socks_desc)) | ||||
| 
 | ||||
|     def is_non_public_numeric_address(self, host): | ||||
|         # for numeric hostnames, skip RFC1918 addresses, since no Tor exit | ||||
|         # node will be able to reach those. Likewise ignore IPv6 addresses. | ||||
|         try: | ||||
|             a = ipaddress.ip_address(host) | ||||
|         except ValueError: | ||||
|             return False # non-numeric, let Tor try it | ||||
|         if a.version != 4: | ||||
|             return True # IPv6 gets ignored | ||||
|         if (a.is_loopback or a.is_multicast or a.is_private or a.is_reserved | ||||
|             or a.is_unspecified): | ||||
|             return True # too weird, don't connect | ||||
|         return False | ||||
| 
 | ||||
|     def get_endpoint_for(self, host, port): | ||||
|         assert isinstance(port, six.integer_types) | ||||
|         if self.is_non_public_numeric_address(host): | ||||
|             return None | ||||
| 
 | ||||
|         # txsocksx doesn't like unicode: it concatenates some binary protocol | ||||
|         # bytes with the hostname when talking to the SOCKS server, so the | ||||
|         # py2 automatic unicode promotion blows up | ||||
|         host = host.encode("ascii") | ||||
|         ep = TorClientEndpoint(host, port, | ||||
|                                socks_endpoint=self._tor_socks_endpoint) | ||||
|         return ep | ||||
|     if launch_tor: | ||||
|         print(" launching a new Tor process, this may take a while..", | ||||
|               file=stderr) | ||||
|         with timing.add("launch tor"): | ||||
|             tor = yield txtorcon.launch(reactor, | ||||
|                                         #data_directory=, | ||||
|                                         #tor_binary=, | ||||
|                                         ) | ||||
|     else: | ||||
|         with timing.add("find tor"): | ||||
|             try: | ||||
|                 tor = yield txtorcon.connect(reactor, tor_control_port) | ||||
|                 print(" using Tor", 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", | ||||
|                       file=stderr) | ||||
|                 # TODO: something nicer. I think connect() is likely to throw | ||||
|                 # a reactor.connectTCP -type error, like ConnectionFailed or | ||||
|                 # ConnectionRefused or something | ||||
|                 raise | ||||
|     directlyProvides(tor, _interfaces.ITorManager) | ||||
|     returnValue(tor) | ||||
|  |  | |||
|  | @ -589,7 +589,7 @@ class Common: | |||
|     RELAY_DELAY = 2.0 | ||||
|     TRANSIT_KEY_LENGTH = SecretBox.KEY_SIZE | ||||
| 
 | ||||
|     def __init__(self, transit_relay, no_listen=False, tor_manager=None, | ||||
|     def __init__(self, transit_relay, no_listen=False, tor=None, | ||||
|                  reactor=reactor, timing=None): | ||||
|         self._side = bytes_to_hexstr(os.urandom(8)) # unicode | ||||
|         if transit_relay: | ||||
|  | @ -603,7 +603,7 @@ class Common: | |||
|             self._transit_relays = [] | ||||
|         self._their_direct_hints = [] # hintobjs | ||||
|         self._our_relay_hints = set(self._transit_relays) | ||||
|         self._tor_manager = tor_manager | ||||
|         self._tor = tor | ||||
|         self._transit_key = None | ||||
|         self._no_listen = no_listen | ||||
|         self._waiting_for_transit_key = [] | ||||
|  | @ -614,7 +614,7 @@ class Common: | |||
|         self._timing.add("transit") | ||||
| 
 | ||||
|     def _build_listener(self): | ||||
|         if self._no_listen or self._tor_manager: | ||||
|         if self._no_listen or self._tor: | ||||
|             return ([], None) | ||||
|         portnum = allocate_tcp_port() | ||||
|         addresses = ipaddrs.find_addresses() | ||||
|  | @ -820,7 +820,7 @@ class Common: | |||
|             if not ep: | ||||
|                 continue | ||||
|             description = "->%s" % describe_hint_obj(hint_obj) | ||||
|             if self._tor_manager: | ||||
|             if self._tor: | ||||
|                 description = "tor" + description | ||||
|             d = self._start_connector(ep, description) | ||||
|             contenders.append(d) | ||||
|  | @ -847,7 +847,7 @@ class Common: | |||
|                 if not ep: | ||||
|                     continue | ||||
|                 description = "->relay:%s" % describe_hint_obj(hint_obj) | ||||
|                 if self._tor_manager: | ||||
|                 if self._tor: | ||||
|                     description = "tor" + description | ||||
|                 d = task.deferLater(self._reactor, relay_delay, | ||||
|                                     self._start_connector, ep, description, | ||||
|  | @ -887,12 +887,11 @@ class Common: | |||
|         return d | ||||
| 
 | ||||
|     def _endpoint_from_hint_obj(self, hint): | ||||
|         if self._tor_manager: | ||||
|         if self._tor: | ||||
|             if isinstance(hint, (DirectTCPV1Hint, TorTCPV1Hint)): | ||||
|                 # our TorManager will return None for non-public IPv4 | ||||
|                 # this Tor object will return None for non-public IPv4 | ||||
|                 # addresses and any IPv6 address | ||||
|                 return self._tor_manager.get_endpoint_for(hint.hostname, | ||||
|                                                           hint.port) | ||||
|                 return self._tor.stream_via(hint.hostname, hint.port) | ||||
|             return None | ||||
|         if isinstance(hint, DirectTCPV1Hint): | ||||
|             return endpoints.HostnameEndpoint(self._reactor, | ||||
|  |  | |||
|  | @ -279,7 +279,7 @@ class _DeferredWormhole(object): | |||
| 
 | ||||
| def create(appid, relay_url, reactor, # use keyword args for everything else | ||||
|            versions={}, | ||||
|            delegate=None, journal=None, tor_manager=None, | ||||
|            delegate=None, journal=None, tor=None, | ||||
|            timing=None, | ||||
|            stderr=sys.stderr): | ||||
|     timing = timing or DebugTiming() | ||||
|  | @ -292,13 +292,13 @@ def create(appid, relay_url, reactor, # use keyword args for everything else | |||
|     wormhole_versions = {} # will be used to indicate Wormhole capabilities | ||||
|     wormhole_versions["app_versions"] = versions # app-specific capabilities | ||||
|     b = Boss(w, side, relay_url, appid, wormhole_versions, | ||||
|              reactor, journal, tor_manager, timing) | ||||
|              reactor, journal, tor, timing) | ||||
|     w._set_boss(b) | ||||
|     b.start() | ||||
|     return w | ||||
| 
 | ||||
| ## def from_serialized(serialized, reactor, delegate, | ||||
| ##                     journal=None, tor_manager=None, | ||||
| ##                     journal=None, tor=None, | ||||
| ##                     timing=None, stderr=sys.stderr): | ||||
| ##     assert serialized["serialized_wormhole_version"] == 1 | ||||
| ##     timing = timing or DebugTiming() | ||||
|  |  | |||
|  | @ -2,8 +2,7 @@ import json | |||
| from twisted.internet.defer import inlineCallbacks, returnValue | ||||
| 
 | ||||
| from . import wormhole | ||||
| from .tor_manager import TorManager | ||||
| from .errors import NoTorError | ||||
| from .tor_manager import get_tor | ||||
| 
 | ||||
| @inlineCallbacks | ||||
| def receive(reactor, appid, relay_url, code, | ||||
|  | @ -27,18 +26,15 @@ def receive(reactor, appid, relay_url, code, | |||
|     :param on_code: if not None, this is called when we have a code (even if you passed in one explicitly) | ||||
|     :type on_code: single-argument callable | ||||
|     """ | ||||
|     tm = None | ||||
|     tor = None | ||||
|     if use_tor: | ||||
|         tm = TorManager(reactor, launch_tor, tor_control_port) | ||||
|         tor = yield get_tor(reactor, launch_tor, tor_control_port) | ||||
|         # For now, block everything until Tor has started. Soon: launch | ||||
|         # tor in parallel with everything else, make sure the TorManager | ||||
|         # tor in parallel with everything else, make sure the Tor object | ||||
|         # can lazy-provide an endpoint, and overlap the startup process | ||||
|         # with the user handing off the wormhole code | ||||
|         if not tm.tor_available(): | ||||
|             raise NoTorError() | ||||
|         yield tm.start() | ||||
| 
 | ||||
|     wh = wormhole.create(appid, relay_url, reactor, tor_manager=tm) | ||||
|     wh = wormhole.create(appid, relay_url, reactor, tor=tor) | ||||
|     if code is None: | ||||
|         wh.allocate_code() | ||||
|         code = yield wh.get_code() | ||||
|  | @ -92,17 +88,14 @@ def send(reactor, appid, relay_url, data, code, | |||
|     :param on_code: if not None, this is called when we have a code (even if you passed in one explicitly) | ||||
|     :type on_code: single-argument callable | ||||
|     """ | ||||
|     tm = None | ||||
|     tor = None | ||||
|     if use_tor: | ||||
|         tm = TorManager(reactor, launch_tor, tor_control_port) | ||||
|         tor = yield get_tor(reactor, launch_tor, tor_control_port) | ||||
|         # For now, block everything until Tor has started. Soon: launch | ||||
|         # tor in parallel with everything else, make sure the TorManager | ||||
|         # tor in parallel with everything else, make sure the Tor object | ||||
|         # can lazy-provide an endpoint, and overlap the startup process | ||||
|         # with the user handing off the wormhole code | ||||
|         if not tm.tor_available(): | ||||
|             raise NoTorError() | ||||
|         yield tm.start() | ||||
|     wh = wormhole.create(appid, relay_url, reactor, tor_manager=tm) | ||||
|     wh = wormhole.create(appid, relay_url, reactor, tor=tor) | ||||
|     if code is None: | ||||
|         wh.allocate_code() | ||||
|         code = yield wh.get_code() | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	Block a user