Changeset 839140c in trunk


Ignore:
Timestamp:
2023-06-12T17:41:49Z (23 months ago)
Author:
GitHub <noreply@…>
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)
Message:

Merge pull request #1303 from tahoe-lafs/4029-http-storage-client-respects-request-to-use-tor

Http storage client respects request to use tor

Fixes ticket:4029

Files:
2 added
11 edited

Legend:

Unmodified
Added
Removed
  • TabularUnified .github/workflows/ci.yml

    rd510103 r839140c  
    167167        include:
    168168          - os: macos-12
    169             python-version: "3.9"
     169            python-version: "3.11"
    170170            force-foolscap: false
    171171          - os: windows-latest
  • TabularUnified integration/conftest.py

    rd510103 r839140c  
    280280
    281281
    282 @pytest.fixture(scope='session')
     282@pytest.fixture
    283283@log_call(
    284284    action_type=u"integration:tor:introducer",
     
    343343
    344344
    345 @pytest.fixture(scope='session')
     345@pytest.fixture
    346346def tor_introducer_furl(tor_introducer, temp_dir):
    347347    furl_fname = join(temp_dir, 'introducer_tor', 'private', 'introducer.furl')
  • TabularUnified integration/test_tor.py

    rd510103 r839140c  
    2020)
    2121from allmydata.client import read_config
     22from allmydata.util.deferredutil import async_to_deferred
    2223
    2324# see "conftest.py" for the fixtures (e.g. "tor_network")
     
    3233@pytest_twisted.inlineCallbacks
    3334def 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)
    3643    yield util.await_client_ready(carol, minimum_number_of_servers=2, timeout=600)
    3744    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)
    3846
    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
     49async 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
    4155    gold_path = join(temp_dir, "gold")
    4256    with open(gold_path, "w") as f:
     
    5569        (
    5670            sys.executable, '-b', '-m', 'allmydata.scripts.runner',
    57             '-d', join(temp_dir, 'carol'),
     71            '-d', upload_to.node_dir,
    5872            'put', gold_path,
    5973        ),
    6074        env=environ,
    6175    )
    62     yield proto.done
     76    await proto.done
    6377    cap = proto.output.getvalue().strip().split()[-1]
    6478    print("capability: {}".format(cap))
     
    7084        (
    7185            sys.executable, '-b', '-m', 'allmydata.scripts.runner',
    72             '-d', join(temp_dir, 'dave'),
     86            '-d', download_from.node_dir,
    7387            'get', cap,
    7488        ),
    7589        env=environ,
    7690    )
    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()
    8194
    8295
    8396@pytest_twisted.inlineCallbacks
    84 def _create_anonymous_node(reactor, name, control_port, request, temp_dir, flog_gatherer, tor_network, introducer_furl):
     97def _create_anonymous_node(reactor, name, control_port, request, temp_dir, flog_gatherer, tor_network, introducer_furl, shares_total: int) -> util.TahoeProcess:
    8598    node_dir = FilePath(temp_dir).child(name)
    8699    web_port = "tcp:{}:interface=localhost".format(control_port + 2000)
     
    104117                '--shares-needed', '1',
    105118                '--shares-happy', '1',
    106                 '--shares-total', '2',
     119                '--shares-total', str(shares_total),
    107120                node_dir.path,
    108121            ),
     
    114127    # Which services should this client connect to?
    115128    write_introducer(node_dir, "default", introducer_furl)
     129    util.basic_node_configuration(request, flog_gatherer, node_dir.path)
    116130
    117131    config = read_config(node_dir.path, "tub.port")
    118     config.set_config("node", "log_gatherer.furl", flog_gatherer)
    119132    config.set_config("tor", "onion", "true")
    120133    config.set_config("tor", "onion.external_port", "3457")
     
    126139    print("okay, launched")
    127140    return result
     141
     142@pytest.mark.skipif(sys.platform.startswith('darwin'), reason='This test has issues on macOS')
     143@pytest_twisted.inlineCallbacks
     144def 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  
    141141    def outReceived(self, data):
    142142        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")
    144145        self._output.write(data)
    145146        if not self.magic_seen.called and self._magic_text in self._output.getvalue():
     
    149150    def errReceived(self, data):
    150151        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")
    152154
    153155
     
    310312    d.addCallback(lambda ignored: tahoe_process)
    311313    return d
     314
     315
     316def 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)
    312344
    313345
     
    352384
    353385        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)
    377387        created_d.addCallback(created)
    378388
     
    622632            for server in servers
    623633        ]
    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:
    634637            print("waiting because at least one server too old")
    635638            time.sleep(1)
  • TabularUnified nix/python-overrides.nix

    rd510103 r839140c  
    2222  txi2p = self.callPackage ./txi2p.nix { };
    2323
    24   # Update the version of klein.
     24  # Some packages are of somewhat too-old versions - update them.
    2525  klein = self.callPackage ./klein.nix {
    2626    # Avoid infinite recursion.
    2727    inherit (super) klein;
     28  };
     29  txtorcon = self.callPackage ./txtorcon.nix {
     30    inherit (super) txtorcon;
    2831  };
    2932
  • TabularUnified setup.py

    rd510103 r839140c  
    165165
    166166tor_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",
    171170]
    172171
  • TabularUnified src/allmydata/client.py

    rd510103 r839140c  
    1111from base64 import urlsafe_b64encode
    1212from functools import partial
    13 # On Python 2 this will be the backported package:
    1413from configparser import NoSectionError
    1514
     
    4847from allmydata.util.time_format import parse_duration, parse_date
    4948from allmydata.util.i2p_provider import create as create_i2p_provider
    50 from allmydata.util.tor_provider import create as create_tor_provider
     49from allmydata.util.tor_provider import create as create_tor_provider, _Provider as TorProvider
    5150from allmydata.stats import StatsProvider
    5251from allmydata.history import History
     
    269268    storage_broker = create_storage_farm_broker(
    270269        config, default_connection_handlers, foolscap_connection_handlers,
    271         tub_options, introducer_clients
     270        tub_options, introducer_clients, tor_provider
    272271    )
    273272
     
    465464
    466465
    467 def create_storage_farm_broker(config: _Config, default_connection_handlers, foolscap_connection_handlers, tub_options, introducer_clients):
     466def create_storage_farm_broker(config: _Config, default_connection_handlers, foolscap_connection_handlers, tub_options, introducer_clients, tor_provider: Optional[TorProvider]):
    468467    """
    469468    Create a StorageFarmBroker object, for use by Uploader/Downloader
     
    501500        node_config=config,
    502501        storage_client_config=storage_client_config,
     502        default_connection_handlers=default_connection_handlers,
     503        tor_provider=tor_provider,
    503504    )
    504505    for ic in introducer_clients:
  • TabularUnified src/allmydata/storage/http_client.py

    rd510103 r839140c  
    1616    Set,
    1717    Dict,
     18    Callable,
    1819)
    1920from base64 import b64encode
     
    3233from twisted.web.http_headers import Headers
    3334from twisted.web import http
    34 from twisted.web.iweb import IPolicyForHTTPS, IResponse
     35from twisted.web.iweb import IPolicyForHTTPS, IResponse, IAgent
    3536from twisted.internet.defer import inlineCallbacks, Deferred, succeed
    3637from twisted.internet.interfaces import (
     
    338339    @classmethod
    339340    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,
    341351    ) -> StorageClient:
    342352        """
    343353        Create a ``StorageClient`` for the given NURL.
    344354        """
     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
    345360        assert nurl.fragment == "v=1"
    346361        assert nurl.scheme == "pb"
     
    354369            cls.TEST_MODE_REGISTER_HTTP_POOL(pool)
    355370
     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
    356381        treq_client = HTTPClient(
    357             Agent(
     382            agent_factory(
    358383                reactor,
    359384                _StorageClientHTTPSPolicy(expected_spki_hash=certificate_hash),
    360                 pool=pool,
     385                pool,
    361386            )
    362387        )
  • TabularUnified src/allmydata/storage_client.py

    rd510103 r839140c  
    5252from twisted.python.failure import Failure
    5353from twisted.web import http
     54from twisted.web.iweb import IAgent, IPolicyForHTTPS
    5455from twisted.internet.task import LoopingCall
    5556from twisted.internet import defer, reactor
     
    7879    ed25519,
    7980)
     81from allmydata.util.tor_provider import _Provider as TorProvider
    8082from allmydata.util import log, base32, connection_status
    8183from allmydata.util.assertutil import precondition
     
    203205            node_config: _Config,
    204206            storage_client_config=None,
     207            default_connection_handlers=None,
     208            tor_provider: Optional[TorProvider]=None,
    205209    ):
    206210        service.MultiService.__init__(self)
     211        if default_connection_handlers is None:
     212            default_connection_handlers = {"tcp": "tcp"}
     213
    207214        assert permute_peers # False not implemented yet
    208215        self.permute_peers = permute_peers
     
    224231        self._threshold_listeners : list[tuple[float,defer.Deferred[Any]]]= [] # tuples of (threshold, Deferred)
    225232        self._connected_high_water_mark = 0
     233        self._tor_provider = tor_provider
     234        self._default_connection_handlers = default_connection_handlers
    226235
    227236    @log_call(action_type=u"storage-client:broker:set-static-servers")
     
    316325                server["ann"],
    317326                grid_manager_verifier=gm_verifier,
     327                default_connection_handlers=self._default_connection_handlers,
     328                tor_provider=self._tor_provider
    318329            )
    319330            s.on_status_changed(lambda _: self._got_connection())
     
    10501061    """
    10511062
    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):
    10531064        service.MultiService.__init__(self)
    10541065        assert isinstance(server_id, bytes)
     
    10581069        self._reactor = reactor
    10591070        self._grid_manager_verifier = grid_manager_verifier
     1071        self._tor_provider = tor_provider
     1072        self._default_connection_handlers = default_connection_handlers
     1073
    10601074        furl = announcement["anonymous-storage-FURL"].encode("utf-8")
    10611075        (
     
    12191233        return connecting
    12201234
     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
    12211255    @async_to_deferred
    12221256    async def _pick_server_and_get_version(self):
     
    12361270            # version() calls before we are live talking to a server, it could only
    12371271            # be one. See https://tahoe-lafs.org/trac/tahoe-lafs/ticket/3992
     1272
     1273            agent_factory = await self._agent_factory()
    12381274
    12391275            def request(reactor, nurl: DecodedURL):
     
    12431279                pool.retryAutomatically = False
    12441280                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)
    12461284                ).get_version()
    12471285
     
    12501288            # If we've gotten this far, we've found a working NURL.
    12511289            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                )
    12531294            )
    12541295            return self._istorage_server
  • TabularUnified src/allmydata/test/test_tor_provider.py

    rd510103 r839140c  
    22Ported to Python 3.
    33"""
    4 from __future__ import absolute_import
    5 from __future__ import division
    6 from __future__ import print_function
    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
    124
    135import os
     
    9587        private_dir = "private"
    9688        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)
    10091
    10192        with mock.patch("allmydata.util.tor_provider.allocate_tcp_port",
     
    10394            d = tor_provider._launch_tor(reactor, tor_executable, private_dir,
    10495                                         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)
    10798
    10899    def test_launch(self):
     
    162153
    163154
     155class FakeTor:
     156    """Pretends to be a ``txtorcon.Tor`` instance."""
     157    def __init__(self):
     158        self.protocol = object()
     159
     160
    164161class CreateOnion(unittest.TestCase):
    165162    def test_no_txtorcon(self):
     
    172169                             "Cannot create onion without txtorcon. "
    173170                             "Please 'pip install tahoe-lafs[tor]' to fix this.")
     171
    174172    def _do_test_launch(self, executable):
    175173        basedir = self.mktemp()
     
    182180            args.append("--tor-executable=%s" % executable)
    183181        cli_config = make_cli_config(basedir, *args)
    184         protocol = object()
     182        tor_instance = FakeTor()
    185183        launch_tor = mock.Mock(return_value=defer.succeed(("control_endpoint",
    186                                                            protocol)))
     184                                                           tor_instance)))
    187185        txtorcon = mock.Mock()
    188186        ehs = mock.Mock()
     
    205203                                      os.path.abspath(private_dir), txtorcon)
    206204        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)
    209207
    210208        expected = {"launch": "true",
     
    588586        with mock_txtorcon(txtorcon):
    589587            p = tor_provider.create(reactor, cfg)
     588        tor_instance = FakeTor()
    590589        tor_state = mock.Mock()
    591         tor_state.protocol = object()
     590        tor_state.protocol = tor_instance.protocol
    592591        ehs = mock.Mock()
    593592        ehs.add_to_tor = mock.Mock(return_value=defer.succeed(None))
    594593        ehs.remove_from_tor = mock.Mock(return_value=defer.succeed(None))
    595594        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)))
    597596        with mock.patch("allmydata.util.tor_provider._launch_tor",
    598597                        launch_tor):
     
    629628        with mock_txtorcon(txtorcon):
    630629            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)
    634632        ehs = mock.Mock()
    635633        ehs.add_to_tor = mock.Mock(return_value=defer.succeed(None))
     
    643641        self.successResultOf(d)
    644642        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)
    646644        cfs.assert_called_with(reactor, "ep_desc")
    647         txtorcon.build_tor_connection.assert_called_with(tcep)
     645        txtorcon.connect.assert_called_with(reactor, tcep)
    648646        txtorcon.EphemeralHiddenService.assert_called_with("456 127.0.0.1:123",
    649647                                                           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)
    651649
    652650        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  
    33Ported to Python 3.
    44"""
    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
     6from __future__ import annotations
     7
     8from typing import Optional
    139import os
    1410
     
    2723    IAddressFamily,
    2824)
     25
    2926
    3027def _import_tor():
     
    4239        return None
    4340
    44 def create(reactor, config, import_tor=None, import_txtorcon=None):
     41def create(reactor, config, import_tor=None, import_txtorcon=None) -> Optional[_Provider]:
    4542    """
    4643    Create a new _Provider service (this is an IService so must be
     
    9996@inlineCallbacks
    10097def _launch_tor(reactor, tor_executable, private_dir, txtorcon):
     98    """
     99    Launches Tor, returns a corresponding ``(control endpoint string,
     100    txtorcon.Tor instance)`` tuple.
     101    """
    101102    # TODO: handle default tor-executable
    102103    # TODO: it might be a good idea to find exactly which Tor we used,
     
    105106    # different Tor being present at node startup. OTOH, maybe we don't
    106107    # need to worry about it.
    107     tor_config = txtorcon.TorConfig()
    108     tor_config.DataDirectory = data_directory(private_dir)
    109108
    110109    # 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),
    118116        tor_binary=tor_executable,
     117        socks_port=allocate_tcp_port(),
    119118        # can be useful when debugging; mirror Tor's output to ours
    120119        # stdout=sys.stdout,
     
    122121    )
    123122
    124     # now tor is launched and ready to be spoken to
    125     # as a side effect, we've got an ITorControlProtocol ready to go
    126     tor_control_proto = tpp.tor_protocol
    127 
    128123    # How/when to shut down the new process? for normal usage, the child
    129124    # tor will exit when it notices its parent (us) quit. Unit tests will
     
    135130    # that fires when Tor has actually exited.
    136131
    137     returnValue((tor_control_endpoint_desc, tor_control_proto))
     132    returnValue((tor_control_endpoint_desc, tor))
     133
    138134
    139135@inlineCallbacks
     
    170166            tahoe_config_tor["tor.executable"] = tor_executable
    171167        print("launching Tor (to allocate .onion address)..", file=stdout)
    172         (_, tor_control_proto) = yield _launch_tor(
     168        (_, tor) = yield _launch_tor(
    173169            reactor, tor_executable, private_dir, txtorcon)
     170        tor_control_proto = tor.protocol
    174171        print("Tor launched", file=stdout)
    175172    else:
     
    295292
    296293    def _get_launched_tor(self, reactor):
    297         # this fires with a tuple of (control_endpoint, tor_protocol)
     294        # this fires with a tuple of (control_endpoint, txtorcon.Tor instance)
    298295        if not self._tor_launched:
    299296            self._tor_launched = OneShotObserverList()
     
    326323            require("private_key_file")
    327324
    328     @inlineCallbacks
    329     def _start_onion(self, reactor):
     325    def get_tor_instance(self, reactor: object):
     326        """Return a ``Deferred`` that fires with a ``txtorcon.Tor`` instance."""
    330327        # launch tor, if necessary
    331328        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])
    333330        else:
    334331            controlport = self._get_tor_config("control.port", None)
    335332            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
    339339        local_port = int(self._get_tor_config("onion.local_port"))
    340340        external_port = int(self._get_tor_config("onion.external_port"))
Note: See TracChangeset for help on using the changeset viewer.