Changeset 839140c in trunk
- Timestamp:
- 2023-06-12T17:41:49Z (23 months ago)
- Branches:
- master
- Children:
- 07a288f, 9cf69c5, cb082b2
- Parents:
- d510103 (diff), 7ff20a3 (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent. - git-author:
- Itamar Turner-Trauring <itamar@…> (2023-06-12 17:41:49)
- git-committer:
- GitHub <noreply@…> (2023-06-12 17:41:49)
- Files:
-
- 2 added
- 11 edited
Legend:
- Unmodified
- Added
- Removed
-
TabularUnified .github/workflows/ci.yml ¶
rd510103 r839140c 167 167 include: 168 168 - os: macos-12 169 python-version: "3. 9"169 python-version: "3.11" 170 170 force-foolscap: false 171 171 - os: windows-latest -
TabularUnified integration/conftest.py ¶
rd510103 r839140c 280 280 281 281 282 @pytest.fixture (scope='session')282 @pytest.fixture 283 283 @log_call( 284 284 action_type=u"integration:tor:introducer", … … 343 343 344 344 345 @pytest.fixture (scope='session')345 @pytest.fixture 346 346 def tor_introducer_furl(tor_introducer, temp_dir): 347 347 furl_fname = join(temp_dir, 'introducer_tor', 'private', 'introducer.furl') -
TabularUnified integration/test_tor.py ¶
rd510103 r839140c 20 20 ) 21 21 from allmydata.client import read_config 22 from allmydata.util.deferredutil import async_to_deferred 22 23 23 24 # see "conftest.py" for the fixtures (e.g. "tor_network") … … 32 33 @pytest_twisted.inlineCallbacks 33 34 def test_onion_service_storage(reactor, request, temp_dir, flog_gatherer, tor_network, tor_introducer_furl): 34 carol = yield _create_anonymous_node(reactor, 'carol', 8008, request, temp_dir, flog_gatherer, tor_network, tor_introducer_furl) 35 dave = yield _create_anonymous_node(reactor, 'dave', 8009, request, temp_dir, flog_gatherer, tor_network, tor_introducer_furl) 35 """ 36 Two nodes and an introducer all configured to use Tahoe. 37 38 The two nodes can talk to the introducer and each other: we upload to one 39 node, read from the other. 40 """ 41 carol = yield _create_anonymous_node(reactor, 'carol', 8008, request, temp_dir, flog_gatherer, tor_network, tor_introducer_furl, 2) 42 dave = yield _create_anonymous_node(reactor, 'dave', 8009, request, temp_dir, flog_gatherer, tor_network, tor_introducer_furl, 2) 36 43 yield util.await_client_ready(carol, minimum_number_of_servers=2, timeout=600) 37 44 yield util.await_client_ready(dave, minimum_number_of_servers=2, timeout=600) 45 yield upload_to_one_download_from_the_other(reactor, temp_dir, carol, dave) 38 46 39 # ensure both nodes are connected to "a grid" by uploading 40 # something via carol, and retrieve it using dave. 47 48 @async_to_deferred 49 async def upload_to_one_download_from_the_other(reactor, temp_dir, upload_to: util.TahoeProcess, download_from: util.TahoeProcess): 50 """ 51 Ensure both nodes are connected to "a grid" by uploading something via one 52 node, and retrieve it using the other. 53 """ 54 41 55 gold_path = join(temp_dir, "gold") 42 56 with open(gold_path, "w") as f: … … 55 69 ( 56 70 sys.executable, '-b', '-m', 'allmydata.scripts.runner', 57 '-d', join(temp_dir, 'carol'),71 '-d', upload_to.node_dir, 58 72 'put', gold_path, 59 73 ), 60 74 env=environ, 61 75 ) 62 yieldproto.done76 await proto.done 63 77 cap = proto.output.getvalue().strip().split()[-1] 64 78 print("capability: {}".format(cap)) … … 70 84 ( 71 85 sys.executable, '-b', '-m', 'allmydata.scripts.runner', 72 '-d', join(temp_dir, 'dave'),86 '-d', download_from.node_dir, 73 87 'get', cap, 74 88 ), 75 89 env=environ, 76 90 ) 77 yield proto.done 78 79 dave_got = proto.output.getvalue().strip() 80 assert dave_got == open(gold_path, 'rb').read().strip() 91 await proto.done 92 download_got = proto.output.getvalue().strip() 93 assert download_got == open(gold_path, 'rb').read().strip() 81 94 82 95 83 96 @pytest_twisted.inlineCallbacks 84 def _create_anonymous_node(reactor, name, control_port, request, temp_dir, flog_gatherer, tor_network, introducer_furl ):97 def _create_anonymous_node(reactor, name, control_port, request, temp_dir, flog_gatherer, tor_network, introducer_furl, shares_total: int) -> util.TahoeProcess: 85 98 node_dir = FilePath(temp_dir).child(name) 86 99 web_port = "tcp:{}:interface=localhost".format(control_port + 2000) … … 104 117 '--shares-needed', '1', 105 118 '--shares-happy', '1', 106 '--shares-total', '2',119 '--shares-total', str(shares_total), 107 120 node_dir.path, 108 121 ), … … 114 127 # Which services should this client connect to? 115 128 write_introducer(node_dir, "default", introducer_furl) 129 util.basic_node_configuration(request, flog_gatherer, node_dir.path) 116 130 117 131 config = read_config(node_dir.path, "tub.port") 118 config.set_config("node", "log_gatherer.furl", flog_gatherer)119 132 config.set_config("tor", "onion", "true") 120 133 config.set_config("tor", "onion.external_port", "3457") … … 126 139 print("okay, launched") 127 140 return result 141 142 @pytest.mark.skipif(sys.platform.startswith('darwin'), reason='This test has issues on macOS') 143 @pytest_twisted.inlineCallbacks 144 def test_anonymous_client(reactor, request, temp_dir, flog_gatherer, tor_network, introducer_furl): 145 """ 146 A normal node (normie) and a normal introducer are configured, and one node 147 (anonymoose) which is configured to be anonymous by talking via Tor. 148 149 Anonymoose should be able to communicate with normie. 150 151 TODO how to ensure that anonymoose is actually using Tor? 152 """ 153 normie = yield util._create_node( 154 reactor, request, temp_dir, introducer_furl, flog_gatherer, "normie", 155 web_port="tcp:9989:interface=localhost", 156 storage=True, needed=1, happy=1, total=1, 157 ) 158 yield util.await_client_ready(normie) 159 160 anonymoose = yield _create_anonymous_node(reactor, 'anonymoose', 8008, request, temp_dir, flog_gatherer, tor_network, introducer_furl, 1) 161 yield util.await_client_ready(anonymoose, minimum_number_of_servers=1, timeout=600) 162 163 yield upload_to_one_download_from_the_other(reactor, temp_dir, normie, anonymoose) -
TabularUnified integration/util.py ¶
rd510103 r839140c 141 141 def outReceived(self, data): 142 142 data = str(data, sys.stdout.encoding) 143 sys.stdout.write(self.name + data) 143 for line in data.splitlines(): 144 sys.stdout.write(self.name + line + "\n") 144 145 self._output.write(data) 145 146 if not self.magic_seen.called and self._magic_text in self._output.getvalue(): … … 149 150 def errReceived(self, data): 150 151 data = str(data, sys.stderr.encoding) 151 sys.stdout.write(self.name + data) 152 for line in data.splitlines(): 153 sys.stdout.write(self.name + line + "\n") 152 154 153 155 … … 310 312 d.addCallback(lambda ignored: tahoe_process) 311 313 return d 314 315 316 def basic_node_configuration(request, flog_gatherer, node_dir: str): 317 """ 318 Setup common configuration options for a node, given a ``pytest`` request 319 fixture. 320 """ 321 config_path = join(node_dir, 'tahoe.cfg') 322 config = get_config(config_path) 323 set_config( 324 config, 325 u'node', 326 u'log_gatherer.furl', 327 flog_gatherer, 328 ) 329 force_foolscap = request.config.getoption("force_foolscap") 330 assert force_foolscap in (True, False) 331 set_config( 332 config, 333 'storage', 334 'force_foolscap', 335 str(force_foolscap), 336 ) 337 set_config( 338 config, 339 'client', 340 'force_foolscap', 341 str(force_foolscap), 342 ) 343 write_config(FilePath(config_path), config) 312 344 313 345 … … 352 384 353 385 def created(_): 354 config_path = join(node_dir, 'tahoe.cfg') 355 config = get_config(config_path) 356 set_config( 357 config, 358 u'node', 359 u'log_gatherer.furl', 360 flog_gatherer, 361 ) 362 force_foolscap = request.config.getoption("force_foolscap") 363 assert force_foolscap in (True, False) 364 set_config( 365 config, 366 'storage', 367 'force_foolscap', 368 str(force_foolscap), 369 ) 370 set_config( 371 config, 372 'client', 373 'force_foolscap', 374 str(force_foolscap), 375 ) 376 write_config(FilePath(config_path), config) 386 basic_node_configuration(request, flog_gatherer, node_dir) 377 387 created_d.addCallback(created) 378 388 … … 622 632 for server in servers 623 633 ] 624 # if any times are null/None that server has never been 625 # contacted (so it's down still, probably) 626 never_received_data = server_times.count(None) 627 if never_received_data > 0: 628 print(f"waiting because {never_received_data} server(s) not contacted") 629 time.sleep(1) 630 continue 631 632 # check that all times are 'recent enough' 633 if any([time.time() - t > liveness for t in server_times]): 634 # check that all times are 'recent enough' (it's OK if _some_ servers 635 # are down, we just want to make sure a sufficient number are up) 636 if len([time.time() - t <= liveness for t in server_times if t is not None]) < minimum_number_of_servers: 634 637 print("waiting because at least one server too old") 635 638 time.sleep(1) -
TabularUnified nix/python-overrides.nix ¶
rd510103 r839140c 22 22 txi2p = self.callPackage ./txi2p.nix { }; 23 23 24 # Update the version of klein.24 # Some packages are of somewhat too-old versions - update them. 25 25 klein = self.callPackage ./klein.nix { 26 26 # Avoid infinite recursion. 27 27 inherit (super) klein; 28 }; 29 txtorcon = self.callPackage ./txtorcon.nix { 30 inherit (super) txtorcon; 28 31 }; 29 32 -
TabularUnified setup.py ¶
rd510103 r839140c 165 165 166 166 tor_requires = [ 167 # This is exactly what `foolscap[tor]` means but pip resolves the pair of 168 # dependencies "foolscap[i2p] foolscap[tor]" to "foolscap[i2p]" so we lose 169 # this if we don't declare it ourselves! 170 "txtorcon >= 0.17.0", 167 # 23.5 added support for custom TLS contexts in web_agent(), which is 168 # needed for the HTTP storage client to run over Tor. 169 "txtorcon >= 23.5.0", 171 170 ] 172 171 -
TabularUnified src/allmydata/client.py ¶
rd510103 r839140c 11 11 from base64 import urlsafe_b64encode 12 12 from functools import partial 13 # On Python 2 this will be the backported package:14 13 from configparser import NoSectionError 15 14 … … 48 47 from allmydata.util.time_format import parse_duration, parse_date 49 48 from allmydata.util.i2p_provider import create as create_i2p_provider 50 from allmydata.util.tor_provider import create as create_tor_provider 49 from allmydata.util.tor_provider import create as create_tor_provider, _Provider as TorProvider 51 50 from allmydata.stats import StatsProvider 52 51 from allmydata.history import History … … 269 268 storage_broker = create_storage_farm_broker( 270 269 config, default_connection_handlers, foolscap_connection_handlers, 271 tub_options, introducer_clients 270 tub_options, introducer_clients, tor_provider 272 271 ) 273 272 … … 465 464 466 465 467 def create_storage_farm_broker(config: _Config, default_connection_handlers, foolscap_connection_handlers, tub_options, introducer_clients ):466 def create_storage_farm_broker(config: _Config, default_connection_handlers, foolscap_connection_handlers, tub_options, introducer_clients, tor_provider: Optional[TorProvider]): 468 467 """ 469 468 Create a StorageFarmBroker object, for use by Uploader/Downloader … … 501 500 node_config=config, 502 501 storage_client_config=storage_client_config, 502 default_connection_handlers=default_connection_handlers, 503 tor_provider=tor_provider, 503 504 ) 504 505 for ic in introducer_clients: -
TabularUnified src/allmydata/storage/http_client.py ¶
rd510103 r839140c 16 16 Set, 17 17 Dict, 18 Callable, 18 19 ) 19 20 from base64 import b64encode … … 32 33 from twisted.web.http_headers import Headers 33 34 from twisted.web import http 34 from twisted.web.iweb import IPolicyForHTTPS, IResponse 35 from twisted.web.iweb import IPolicyForHTTPS, IResponse, IAgent 35 36 from twisted.internet.defer import inlineCallbacks, Deferred, succeed 36 37 from twisted.internet.interfaces import ( … … 338 339 @classmethod 339 340 def from_nurl( 340 cls, nurl: DecodedURL, reactor, pool: Optional[HTTPConnectionPool] = None 341 cls, 342 nurl: DecodedURL, 343 reactor, 344 # TODO default_connection_handlers should really be a class, not a dict 345 # of strings... 346 default_connection_handlers: dict[str, str], 347 pool: Optional[HTTPConnectionPool] = None, 348 agent_factory: Optional[ 349 Callable[[object, IPolicyForHTTPS, HTTPConnectionPool], IAgent] 350 ] = None, 341 351 ) -> StorageClient: 342 352 """ 343 353 Create a ``StorageClient`` for the given NURL. 344 354 """ 355 # Safety check: if we're using normal TCP connections, we better not be 356 # configured for Tor or I2P. 357 if agent_factory is None: 358 assert default_connection_handlers["tcp"] == "tcp" 359 345 360 assert nurl.fragment == "v=1" 346 361 assert nurl.scheme == "pb" … … 354 369 cls.TEST_MODE_REGISTER_HTTP_POOL(pool) 355 370 371 def default_agent_factory( 372 reactor: object, 373 tls_context_factory: IPolicyForHTTPS, 374 pool: HTTPConnectionPool, 375 ) -> IAgent: 376 return Agent(reactor, tls_context_factory, pool=pool) 377 378 if agent_factory is None: 379 agent_factory = default_agent_factory 380 356 381 treq_client = HTTPClient( 357 Agent(382 agent_factory( 358 383 reactor, 359 384 _StorageClientHTTPSPolicy(expected_spki_hash=certificate_hash), 360 pool =pool,385 pool, 361 386 ) 362 387 ) -
TabularUnified src/allmydata/storage_client.py ¶
rd510103 r839140c 52 52 from twisted.python.failure import Failure 53 53 from twisted.web import http 54 from twisted.web.iweb import IAgent, IPolicyForHTTPS 54 55 from twisted.internet.task import LoopingCall 55 56 from twisted.internet import defer, reactor … … 78 79 ed25519, 79 80 ) 81 from allmydata.util.tor_provider import _Provider as TorProvider 80 82 from allmydata.util import log, base32, connection_status 81 83 from allmydata.util.assertutil import precondition … … 203 205 node_config: _Config, 204 206 storage_client_config=None, 207 default_connection_handlers=None, 208 tor_provider: Optional[TorProvider]=None, 205 209 ): 206 210 service.MultiService.__init__(self) 211 if default_connection_handlers is None: 212 default_connection_handlers = {"tcp": "tcp"} 213 207 214 assert permute_peers # False not implemented yet 208 215 self.permute_peers = permute_peers … … 224 231 self._threshold_listeners : list[tuple[float,defer.Deferred[Any]]]= [] # tuples of (threshold, Deferred) 225 232 self._connected_high_water_mark = 0 233 self._tor_provider = tor_provider 234 self._default_connection_handlers = default_connection_handlers 226 235 227 236 @log_call(action_type=u"storage-client:broker:set-static-servers") … … 316 325 server["ann"], 317 326 grid_manager_verifier=gm_verifier, 327 default_connection_handlers=self._default_connection_handlers, 328 tor_provider=self._tor_provider 318 329 ) 319 330 s.on_status_changed(lambda _: self._got_connection()) … … 1050 1061 """ 1051 1062 1052 def __init__(self, server_id: bytes, announcement, reactor=reactor, grid_manager_verifier=None):1063 def __init__(self, server_id: bytes, announcement, default_connection_handlers: dict[str,str], reactor=reactor, grid_manager_verifier=None, tor_provider: Optional[TorProvider]=None): 1053 1064 service.MultiService.__init__(self) 1054 1065 assert isinstance(server_id, bytes) … … 1058 1069 self._reactor = reactor 1059 1070 self._grid_manager_verifier = grid_manager_verifier 1071 self._tor_provider = tor_provider 1072 self._default_connection_handlers = default_connection_handlers 1073 1060 1074 furl = announcement["anonymous-storage-FURL"].encode("utf-8") 1061 1075 ( … … 1219 1233 return connecting 1220 1234 1235 async def _agent_factory(self) -> Optional[Callable[[object, IPolicyForHTTPS, HTTPConnectionPool],IAgent]]: 1236 """Return a factory for ``twisted.web.iweb.IAgent``.""" 1237 # TODO default_connection_handlers should really be an object, not a 1238 # dict, so we can ask "is this using Tor" without poking at a 1239 # dictionary with arbitrary strings... See 1240 # https://tahoe-lafs.org/trac/tahoe-lafs/ticket/4032 1241 handler = self._default_connection_handlers["tcp"] 1242 if handler == "tcp": 1243 return None 1244 if handler == "tor": 1245 assert self._tor_provider is not None 1246 tor_instance = await self._tor_provider.get_tor_instance(self._reactor) 1247 1248 def agent_factory(reactor: object, tls_context_factory: IPolicyForHTTPS, pool: HTTPConnectionPool) -> IAgent: 1249 assert reactor == self._reactor 1250 return tor_instance.web_agent(pool=pool, tls_context_factory=tls_context_factory) 1251 return agent_factory 1252 else: 1253 raise RuntimeError(f"Unsupported tcp connection handler: {handler}") 1254 1221 1255 @async_to_deferred 1222 1256 async def _pick_server_and_get_version(self): … … 1236 1270 # version() calls before we are live talking to a server, it could only 1237 1271 # be one. See https://tahoe-lafs.org/trac/tahoe-lafs/ticket/3992 1272 1273 agent_factory = await self._agent_factory() 1238 1274 1239 1275 def request(reactor, nurl: DecodedURL): … … 1243 1279 pool.retryAutomatically = False 1244 1280 return StorageClientGeneral( 1245 StorageClient.from_nurl(nurl, reactor, pool) 1281 StorageClient.from_nurl( 1282 nurl, reactor, self._default_connection_handlers, 1283 pool=pool, agent_factory=agent_factory) 1246 1284 ).get_version() 1247 1285 … … 1250 1288 # If we've gotten this far, we've found a working NURL. 1251 1289 self._istorage_server = _HTTPStorageServer.from_http_client( 1252 StorageClient.from_nurl(nurl, reactor) 1290 StorageClient.from_nurl( 1291 nurl, reactor, self._default_connection_handlers, 1292 agent_factory=agent_factory 1293 ) 1253 1294 ) 1254 1295 return self._istorage_server -
TabularUnified src/allmydata/test/test_tor_provider.py ¶
rd510103 r839140c 2 2 Ported to Python 3. 3 3 """ 4 from __future__ import absolute_import5 from __future__ import division6 from __future__ import print_function7 from __future__ import unicode_literals8 9 from future.utils import PY210 if PY2:11 from future.builtins import filter, map, zip, ascii, chr, hex, input, next, oct, open, pow, round, super, bytes, dict, list, object, range, str, max, min # noqa: F40112 4 13 5 import os … … 95 87 private_dir = "private" 96 88 txtorcon = mock.Mock() 97 tpp = mock.Mock 98 tpp.tor_protocol = mock.Mock() 99 txtorcon.launch_tor = mock.Mock(return_value=tpp) 89 tor = mock.Mock 90 txtorcon.launch = mock.Mock(return_value=tor) 100 91 101 92 with mock.patch("allmydata.util.tor_provider.allocate_tcp_port", … … 103 94 d = tor_provider._launch_tor(reactor, tor_executable, private_dir, 104 95 txtorcon) 105 tor_control_endpoint, tor_ control_proto= self.successResultOf(d)106 self.assertIs(tor_ control_proto, tpp.tor_protocol)96 tor_control_endpoint, tor_result = self.successResultOf(d) 97 self.assertIs(tor_result, tor) 107 98 108 99 def test_launch(self): … … 162 153 163 154 155 class FakeTor: 156 """Pretends to be a ``txtorcon.Tor`` instance.""" 157 def __init__(self): 158 self.protocol = object() 159 160 164 161 class CreateOnion(unittest.TestCase): 165 162 def test_no_txtorcon(self): … … 172 169 "Cannot create onion without txtorcon. " 173 170 "Please 'pip install tahoe-lafs[tor]' to fix this.") 171 174 172 def _do_test_launch(self, executable): 175 173 basedir = self.mktemp() … … 182 180 args.append("--tor-executable=%s" % executable) 183 181 cli_config = make_cli_config(basedir, *args) 184 protocol = object()182 tor_instance = FakeTor() 185 183 launch_tor = mock.Mock(return_value=defer.succeed(("control_endpoint", 186 protocol)))184 tor_instance))) 187 185 txtorcon = mock.Mock() 188 186 ehs = mock.Mock() … … 205 203 os.path.abspath(private_dir), txtorcon) 206 204 txtorcon.EphemeralHiddenService.assert_called_with("3457 127.0.0.1:999999") 207 ehs.add_to_tor.assert_called_with( protocol)208 ehs.remove_from_tor.assert_called_with( protocol)205 ehs.add_to_tor.assert_called_with(tor_instance.protocol) 206 ehs.remove_from_tor.assert_called_with(tor_instance.protocol) 209 207 210 208 expected = {"launch": "true", … … 588 586 with mock_txtorcon(txtorcon): 589 587 p = tor_provider.create(reactor, cfg) 588 tor_instance = FakeTor() 590 589 tor_state = mock.Mock() 591 tor_state.protocol = object()590 tor_state.protocol = tor_instance.protocol 592 591 ehs = mock.Mock() 593 592 ehs.add_to_tor = mock.Mock(return_value=defer.succeed(None)) 594 593 ehs.remove_from_tor = mock.Mock(return_value=defer.succeed(None)) 595 594 txtorcon.EphemeralHiddenService = mock.Mock(return_value=ehs) 596 launch_tor = mock.Mock(return_value=defer.succeed((None,tor_ state.protocol)))595 launch_tor = mock.Mock(return_value=defer.succeed((None,tor_instance))) 597 596 with mock.patch("allmydata.util.tor_provider._launch_tor", 598 597 launch_tor): … … 629 628 with mock_txtorcon(txtorcon): 630 629 p = tor_provider.create(reactor, cfg) 631 tor_state = mock.Mock() 632 tor_state.protocol = object() 633 txtorcon.build_tor_connection = mock.Mock(return_value=tor_state) 630 tor_instance = FakeTor() 631 txtorcon.connect = mock.Mock(return_value=tor_instance) 634 632 ehs = mock.Mock() 635 633 ehs.add_to_tor = mock.Mock(return_value=defer.succeed(None)) … … 643 641 self.successResultOf(d) 644 642 self.assertIs(p._onion_ehs, ehs) 645 self.assertIs(p._onion_tor_control_proto, tor_ state.protocol)643 self.assertIs(p._onion_tor_control_proto, tor_instance.protocol) 646 644 cfs.assert_called_with(reactor, "ep_desc") 647 txtorcon. build_tor_connection.assert_called_with(tcep)645 txtorcon.connect.assert_called_with(reactor, tcep) 648 646 txtorcon.EphemeralHiddenService.assert_called_with("456 127.0.0.1:123", 649 647 b"private key") 650 ehs.add_to_tor.assert_called_with(tor_ state.protocol)648 ehs.add_to_tor.assert_called_with(tor_instance.protocol) 651 649 652 650 yield p.stopService() 653 ehs.remove_from_tor.assert_called_with(tor_ state.protocol)651 ehs.remove_from_tor.assert_called_with(tor_instance.protocol) -
TabularUnified src/allmydata/util/tor_provider.py ¶
rd510103 r839140c 3 3 Ported to Python 3. 4 4 """ 5 from __future__ import absolute_import, print_function, with_statement 6 from __future__ import division 7 from __future__ import unicode_literals 8 9 from future.utils import PY2 10 if PY2: 11 from future.builtins import filter, map, zip, ascii, chr, hex, input, next, oct, open, pow, round, super, bytes, dict, list, object, range, str, max, min # noqa: F401 12 5 6 from __future__ import annotations 7 8 from typing import Optional 13 9 import os 14 10 … … 27 23 IAddressFamily, 28 24 ) 25 29 26 30 27 def _import_tor(): … … 42 39 return None 43 40 44 def create(reactor, config, import_tor=None, import_txtorcon=None) :41 def create(reactor, config, import_tor=None, import_txtorcon=None) -> Optional[_Provider]: 45 42 """ 46 43 Create a new _Provider service (this is an IService so must be … … 99 96 @inlineCallbacks 100 97 def _launch_tor(reactor, tor_executable, private_dir, txtorcon): 98 """ 99 Launches Tor, returns a corresponding ``(control endpoint string, 100 txtorcon.Tor instance)`` tuple. 101 """ 101 102 # TODO: handle default tor-executable 102 103 # TODO: it might be a good idea to find exactly which Tor we used, … … 105 106 # different Tor being present at node startup. OTOH, maybe we don't 106 107 # need to worry about it. 107 tor_config = txtorcon.TorConfig()108 tor_config.DataDirectory = data_directory(private_dir)109 108 110 109 # unix-domain control socket 111 tor_config.ControlPort = "unix:" + os.path.join(private_dir, "tor.control") 112 tor_control_endpoint_desc = tor_config.ControlPort 113 114 tor_config.SOCKSPort = allocate_tcp_port() 115 116 tpp = yield txtorcon.launch_tor( 117 tor_config, reactor, 110 tor_control_endpoint_desc = "unix:" + os.path.join(private_dir, "tor.control") 111 112 tor = yield txtorcon.launch( 113 reactor, 114 control_port=tor_control_endpoint_desc, 115 data_directory=data_directory(private_dir), 118 116 tor_binary=tor_executable, 117 socks_port=allocate_tcp_port(), 119 118 # can be useful when debugging; mirror Tor's output to ours 120 119 # stdout=sys.stdout, … … 122 121 ) 123 122 124 # now tor is launched and ready to be spoken to125 # as a side effect, we've got an ITorControlProtocol ready to go126 tor_control_proto = tpp.tor_protocol127 128 123 # How/when to shut down the new process? for normal usage, the child 129 124 # tor will exit when it notices its parent (us) quit. Unit tests will … … 135 130 # that fires when Tor has actually exited. 136 131 137 returnValue((tor_control_endpoint_desc, tor_control_proto)) 132 returnValue((tor_control_endpoint_desc, tor)) 133 138 134 139 135 @inlineCallbacks … … 170 166 tahoe_config_tor["tor.executable"] = tor_executable 171 167 print("launching Tor (to allocate .onion address)..", file=stdout) 172 (_, tor _control_proto) = yield _launch_tor(168 (_, tor) = yield _launch_tor( 173 169 reactor, tor_executable, private_dir, txtorcon) 170 tor_control_proto = tor.protocol 174 171 print("Tor launched", file=stdout) 175 172 else: … … 295 292 296 293 def _get_launched_tor(self, reactor): 297 # this fires with a tuple of (control_endpoint, t or_protocol)294 # this fires with a tuple of (control_endpoint, txtorcon.Tor instance) 298 295 if not self._tor_launched: 299 296 self._tor_launched = OneShotObserverList() … … 326 323 require("private_key_file") 327 324 328 @inlineCallbacks329 def _start_onion(self, reactor):325 def get_tor_instance(self, reactor: object): 326 """Return a ``Deferred`` that fires with a ``txtorcon.Tor`` instance.""" 330 327 # launch tor, if necessary 331 328 if self._get_tor_config("launch", False, boolean=True): 332 (_, tor_control_proto) = yield self._get_launched_tor(reactor)329 return self._get_launched_tor(reactor).addCallback(lambda t: t[1]) 333 330 else: 334 331 controlport = self._get_tor_config("control.port", None) 335 332 tcep = clientFromString(reactor, controlport) 336 tor_state = yield self._txtorcon.build_tor_connection(tcep) 337 tor_control_proto = tor_state.protocol 338 333 return self._txtorcon.connect(reactor, tcep) 334 335 @inlineCallbacks 336 def _start_onion(self, reactor): 337 tor_instance = yield self.get_tor_instance(reactor) 338 tor_control_proto = tor_instance.protocol 339 339 local_port = int(self._get_tor_config("onion.local_port")) 340 340 external_port = int(self._get_tor_config("onion.external_port"))
Note: See TracChangeset
for help on using the changeset viewer.