1# -*- coding: utf-8 -*- 2 3from __future__ import absolute_import 4from __future__ import print_function 5from __future__ import with_statement 6 7import os 8import sys 9import six 10import shlex 11import tempfile 12import functools 13from io import StringIO 14from os.path import dirname, exists 15try: 16 from collections.abc import Sequence 17except ImportError: 18 from collections import Sequence 19 20from twisted.python import log 21from twisted.python.failure import Failure 22from twisted.internet.defer import inlineCallbacks, returnValue, Deferred, succeed, fail 23from twisted.internet import protocol, error 24from twisted.internet.endpoints import TCP4ClientEndpoint 25from twisted.internet.endpoints import UNIXClientEndpoint 26from twisted.internet.interfaces import IReactorTime, IReactorCore 27from twisted.internet.interfaces import IStreamClientEndpoint 28 29from zope.interface import implementer 30 31from txtorcon.util import delete_file_or_tree, find_keywords 32from txtorcon.util import find_tor_binary, available_tcp_port 33from txtorcon.log import txtorlog 34from txtorcon.torcontrolprotocol import TorProtocolFactory 35from txtorcon.torstate import TorState 36from txtorcon.torconfig import TorConfig 37from txtorcon.endpoints import TorClientEndpoint, _create_socks_endpoint 38from txtorcon.endpoints import TCPHiddenServiceEndpoint 39from txtorcon.onion import EphemeralOnionService, FilesystemOnionService, _validate_ports 40from txtorcon.util import _is_non_public_numeric_address 41 42from . import socks 43from .interface import ITor 44 45try: 46 from .controller_py3 import _AsyncOnionAuthContext 47 HAVE_ASYNC = True 48except Exception: 49 HAVE_ASYNC = False 50 51if sys.platform in ('linux', 'linux2', 'darwin'): 52 import pwd 53 54 55@inlineCallbacks 56def launch(reactor, 57 progress_updates=None, 58 control_port=None, 59 data_directory=None, 60 socks_port=None, 61 non_anonymous_mode=None, 62 stdout=None, 63 stderr=None, 64 timeout=None, 65 tor_binary=None, 66 user=None, # XXX like the config['User'] special-casing from before 67 # 'users' probably never need these: 68 connection_creator=None, 69 kill_on_stderr=True, 70 _tor_config=None, # a TorConfig instance, mostly for tests 71 ): 72 """ 73 launches a new Tor process, and returns a Deferred that fires with 74 a new :class:`txtorcon.Tor` instance. From this instance, you can 75 create or get any "interesting" instances you need: the 76 :class:`txtorcon.TorConfig` instance, create endpoints, create 77 :class:`txtorcon.TorState` instance(s), etc. 78 79 Note that there is NO way to pass in a config; we only expost a 80 couple of basic Tor options. If you need anything beyond these, 81 you can access the ``TorConfig`` instance (via ``.config``) 82 and make any changes there, reflecting them in tor with 83 ``.config.save()``. 84 85 You can igore all the options and safe defaults will be 86 provided. However, **it is recommended to pass data_directory** 87 especially if you will be starting up Tor frequently, as it saves 88 a bunch of time (and bandwidth for the directory 89 authorities). "Safe defaults" means: 90 91 - a tempdir for a ``DataDirectory`` is used (respecting ``TMP``) 92 and is deleted when this tor is shut down (you therefore 93 *probably* want to supply the ``data_directory=`` kwarg); 94 - a random, currently-unused local TCP port is used as the 95 ``SocksPort`` (specify ``socks_port=`` if you want your 96 own). If you want no SOCKS listener at all, pass 97 ``socks_port=0`` 98 - we set ``__OwningControllerProcess`` and call 99 ``TAKEOWNERSHIP`` so that if our control connection goes away, 100 tor shuts down (see `control-spec 101 <https://gitweb.torproject.org/torspec.git/blob/HEAD:/control-spec.txt>`_ 102 3.23). 103 - the launched Tor will use ``COOKIE`` authentication. 104 105 :param reactor: a Twisted IReactorCore implementation (usually 106 twisted.internet.reactor) 107 108 :param progress_updates: a callback which gets progress updates; gets 3 109 args: percent, tag, summary (FIXME make an interface for this). 110 111 :param data_directory: set as the ``DataDirectory`` option to Tor, 112 this is where tor keeps its state information (cached relays, 113 etc); starting with an already-populated state directory is a lot 114 faster. If ``None`` (the default), we create a tempdir for this 115 **and delete it on exit**. It is recommended you pass something here. 116 117 :param non_anonymous_mode: sets the Tor options 118 `HiddenServiceSingleHopMode` and 119 `HiddenServiceNonAnonymousMode` to 1 and un-sets any 120 `SOCKSPort` config, thus putting this Tor client into 121 "non-anonymous mode" which allows starting so-called Single 122 Onion services -- which use single-hop circuits to rendezvous 123 points. See WARNINGs in Tor manual! Also you need Tor 124 `0.3.4.1` or later (e.g. any `0.3.5.*` or newer) for this to 125 work properly. 126 127 :param stdout: a file-like object to which we write anything that 128 Tor prints on stdout (just needs to support write()). 129 130 :param stderr: a file-like object to which we write anything that 131 Tor prints on stderr (just needs .write()). Note that we kill 132 Tor off by default if anything appears on stderr; pass 133 "kill_on_stderr=False" if you don't want this behavior. 134 135 :param tor_binary: path to the Tor binary to run. If None (the 136 default), we try to find the tor binary. 137 138 :param kill_on_stderr: 139 When True (the default), if Tor prints anything on stderr we 140 kill off the process, close the TorControlProtocol and raise 141 an exception. 142 143 :param connection_creator: is mostly available to ease testing, so 144 you probably don't want to supply this. If supplied, it is a 145 callable that should return a Deferred that delivers an 146 :api:`twisted.internet.interfaces.IProtocol <IProtocol>` or 147 ConnectError. 148 See :api:`twisted.internet.interfaces.IStreamClientEndpoint`.connect 149 Note that this parameter is ignored if config.ControlPort == 0 150 151 :return: a Deferred which callbacks with :class:`txtorcon.Tor` 152 instance, from which you can retrieve the TorControlProtocol 153 instance via the ``.protocol`` property. 154 155 HACKS: 156 157 1. It's hard to know when Tor has both (completely!) written its 158 authentication cookie file AND is listening on the control 159 port. It seems that waiting for the first 'bootstrap' message on 160 stdout is sufficient. Seems fragile...and doesn't work 100% of 161 the time, so FIXME look at Tor source. 162 163 164 165 XXX this "User" thing was, IIRC, a feature for root-using scripts 166 (!!) that were going to launch tor, but where tor would drop to a 167 different user. Do we still want to support this? Probably 168 relevant to Docker (where everything is root! yay!) 169 170 ``User``: if this exists, we attempt to set ownership of the tempdir 171 to this user (but only if our effective UID is 0). 172 """ 173 174 # We have a slight problem with the approach: we need to pass a 175 # few minimum values to a torrc file so that Tor will start up 176 # enough that we may connect to it. Ideally, we'd be able to 177 # start a Tor up which doesn't really do anything except provide 178 # "AUTHENTICATE" and "GETINFO config/names" so we can do our 179 # config validation. 180 181 if not IReactorCore.providedBy(reactor): 182 raise ValueError( 183 "'reactor' argument must provide IReactorCore" 184 " (got '{}': {})".format( 185 type(reactor).__class__.__name__, 186 repr(reactor) 187 ) 188 ) 189 190 if tor_binary is None: 191 tor_binary = find_tor_binary() 192 if tor_binary is None: 193 # We fail right here instead of waiting for the reactor to start 194 raise TorNotFound('Tor binary could not be found') 195 196 # make sure we got things that have write() for stderr, stdout 197 # kwargs (XXX is there a "better" way to check for file-like 198 # object? do we use anything besides 'write()'?) 199 for arg in [stderr, stdout]: 200 if arg and not getattr(arg, "write", None): 201 raise RuntimeError( 202 'File-like object needed for stdout or stderr args.' 203 ) 204 205 config = _tor_config or TorConfig() 206 if data_directory is not None: 207 user_set_data_directory = True 208 config.DataDirectory = data_directory 209 try: 210 os.mkdir(data_directory, 0o0700) 211 except OSError: 212 pass 213 else: 214 user_set_data_directory = False 215 data_directory = tempfile.mkdtemp(prefix='tortmp') 216 config.DataDirectory = data_directory 217 # note: we also set up the ProcessProtocol to delete this when 218 # Tor exits, this is "just in case" fallback: 219 reactor.addSystemEventTrigger( 220 'before', 'shutdown', 221 functools.partial(delete_file_or_tree, data_directory) 222 ) 223 224 # things that used launch_tor() had to set ControlPort and/or 225 # SocksPort on the config to pass them, so we honour that here. 226 if control_port is None and _tor_config is not None: 227 try: 228 control_port = config.ControlPort 229 except KeyError: 230 control_port = None 231 232 if socks_port is None and _tor_config is not None: 233 try: 234 socks_port = config.SocksPort 235 except KeyError: 236 socks_port = None 237 238 if non_anonymous_mode: 239 if socks_port is not None: 240 raise ValueError( 241 "Cannot use SOCKS options with non_anonymous_mode=True" 242 ) 243 config.HiddenServiceNonAnonymousMode = 1 244 config.HiddenServiceSingleHopMode = 1 245 config.SOCKSPort = 0 246 else: 247 if socks_port is None: 248 socks_port = yield available_tcp_port(reactor) 249 config.SOCKSPort = socks_port 250 251 try: 252 our_user = user or config.User 253 except KeyError: 254 pass 255 else: 256 # if we're root, make sure the directory is owned by the User 257 # that Tor is configured to drop to 258 if sys.platform in ('linux', 'linux2', 'darwin') and os.geteuid() == 0: 259 os.chown(data_directory, pwd.getpwnam(our_user).pw_uid, -1) 260 261 # user can pass in a control port, or we set one up here 262 if control_port is None: 263 # on posix-y systems, we can use a unix-socket 264 if sys.platform in ('linux', 'linux2', 'darwin'): 265 # note: tor will not accept a relative path for ControlPort 266 control_port = 'unix:{}'.format( 267 os.path.join(os.path.realpath(data_directory), 'control.socket') 268 ) 269 else: 270 control_port = yield available_tcp_port(reactor) 271 else: 272 if str(control_port).startswith('unix:'): 273 control_path = control_port.lstrip('unix:') 274 containing_dir = dirname(control_path) 275 if not exists(containing_dir): 276 raise ValueError( 277 "The directory containing '{}' must exist".format( 278 containing_dir 279 ) 280 ) 281 # Tor will be sad if the directory isn't 0700 282 mode = (0o0777 & os.stat(containing_dir).st_mode) 283 if mode & ~(0o0700): 284 raise ValueError( 285 "The directory containing a unix control-socket ('{}') " 286 "must only be readable by the user".format(containing_dir) 287 ) 288 config.ControlPort = control_port 289 290 config.CookieAuthentication = 1 291 config.__OwningControllerProcess = os.getpid() 292 if connection_creator is None: 293 if str(control_port).startswith('unix:'): 294 connection_creator = functools.partial( 295 UNIXClientEndpoint(reactor, control_port[5:]).connect, 296 TorProtocolFactory() 297 ) 298 else: 299 connection_creator = functools.partial( 300 TCP4ClientEndpoint(reactor, 'localhost', control_port).connect, 301 TorProtocolFactory() 302 ) 303 # not an "else" on purpose; if we passed in "control_port=0" *and* 304 # a custom connection creator, we should still set this to None so 305 # it's never called (since we can't connect with ControlPort=0) 306 if control_port == 0: 307 connection_creator = None 308 309 # NOTE well, that if we don't pass "-f" then Tor will merrily load 310 # its default torrc, and apply our options over top... :/ should 311 # file a bug probably? --no-defaults or something maybe? (does 312 # --defaults-torrc - or something work?) 313 config_args = ['-f', '/dev/null/non-existant-on-purpose', '--ignore-missing-torrc'] 314 315 # ...now add all our config options on the command-line. This 316 # avoids writing a temporary torrc. 317 for (k, v) in config.config_args(): 318 config_args.append(k) 319 config_args.append(v) 320 321 process_protocol = TorProcessProtocol( 322 connection_creator, 323 progress_updates, 324 config, reactor, 325 timeout, 326 kill_on_stderr, 327 stdout, 328 stderr, 329 ) 330 if control_port == 0: 331 connected_cb = succeed(None) 332 else: 333 connected_cb = process_protocol.when_connected() 334 335 # we set both to_delete and the shutdown events because this 336 # process might be shut down way before the reactor, but if the 337 # reactor bombs out without the subprocess getting closed cleanly, 338 # we'll want the system shutdown events triggered so the temporary 339 # files get cleaned up either way 340 341 # we don't want to delete the user's directories, just temporary 342 # ones this method created. 343 if not user_set_data_directory: 344 process_protocol.to_delete = [data_directory] 345 reactor.addSystemEventTrigger( 346 'before', 'shutdown', 347 functools.partial(delete_file_or_tree, data_directory) 348 ) 349 350 log.msg('Spawning tor process with DataDirectory', data_directory) 351 args = [tor_binary] + config_args 352 transport = reactor.spawnProcess( 353 process_protocol, 354 tor_binary, 355 args=args, 356 env={'HOME': data_directory}, 357 path=data_directory if os.path.exists(data_directory) else None, # XXX error if it doesn't exist? 358 ) 359 transport.closeStdin() 360 proto = yield connected_cb 361 # note "proto" here is a TorProcessProtocol 362 363 # we might need to attach this protocol to the TorConfig 364 if config.protocol is None and proto is not None and proto.tor_protocol is not None: 365 # proto is None in the ControlPort=0 case 366 yield config.attach_protocol(proto.tor_protocol) 367 # note that attach_protocol waits for the protocol to be 368 # boostrapped if necessary 369 370 returnValue( 371 Tor( 372 reactor, 373 config.protocol, 374 _tor_config=config, 375 _process_proto=process_protocol, 376 _non_anonymous=True if non_anonymous_mode else False, 377 ) 378 ) 379 380 381@inlineCallbacks 382def connect(reactor, control_endpoint=None, password_function=None): 383 """ 384 Creates a :class:`txtorcon.Tor` instance by connecting to an 385 already-running tor's control port. For example, a common default 386 tor uses is UNIXClientEndpoint(reactor, '/var/run/tor/control') or 387 TCP4ClientEndpoint(reactor, 'localhost', 9051) 388 389 If only password authentication is available in the tor we connect 390 to, the ``password_function`` is called (if supplied) to retrieve 391 a valid password. This function can return a Deferred. 392 393 For example:: 394 395 import txtorcon 396 from twisted.internet.task import react 397 from twisted.internet.defer import inlineCallbacks 398 399 @inlineCallbacks 400 def main(reactor): 401 tor = yield txtorcon.connect( 402 TCP4ClientEndpoint(reactor, "localhost", 9051) 403 ) 404 state = yield tor.create_state() 405 for circuit in state.circuits: 406 print(circuit) 407 408 :param control_endpoint: None, an IStreamClientEndpoint to connect 409 to, or a Sequence of IStreamClientEndpoint instances to connect 410 to. If None, a list of defaults are tried. 411 412 :param password_function: 413 See :class:`txtorcon.TorControlProtocol` 414 415 :return: 416 a Deferred that fires with a :class:`txtorcon.Tor` instance 417 """ 418 419 @inlineCallbacks 420 def try_endpoint(control_ep): 421 assert IStreamClientEndpoint.providedBy(control_ep) 422 proto = yield control_ep.connect( 423 TorProtocolFactory( 424 password_function=password_function 425 ) 426 ) 427 config = yield TorConfig.from_protocol(proto) 428 tor = Tor(reactor, proto, _tor_config=config) 429 returnValue(tor) 430 431 if control_endpoint is None: 432 to_try = [ 433 UNIXClientEndpoint(reactor, '/var/run/tor/control'), 434 TCP4ClientEndpoint(reactor, '127.0.0.1', 9051), 435 TCP4ClientEndpoint(reactor, '127.0.0.1', 9151), 436 ] 437 elif IStreamClientEndpoint.providedBy(control_endpoint): 438 to_try = [control_endpoint] 439 elif isinstance(control_endpoint, Sequence): 440 to_try = control_endpoint 441 for ep in control_endpoint: 442 if not IStreamClientEndpoint.providedBy(ep): 443 raise ValueError( 444 "For control_endpoint=, '{}' must provide" 445 " IStreamClientEndpoint".format(ep) 446 ) 447 else: 448 raise ValueError( 449 "For control_endpoint=, '{}' must provide" 450 " IStreamClientEndpoint".format(control_endpoint) 451 ) 452 453 errors = [] 454 for idx, ep in enumerate(to_try): 455 try: 456 tor = yield try_endpoint(ep) 457 txtorlog.msg("Connected via '{}'".format(ep)) 458 returnValue(tor) 459 except Exception as e: 460 errors.append(e) 461 if len(errors) == 1: 462 raise errors[0] 463 raise RuntimeError( 464 'Failed to connect to: {}'.format( 465 ', '.join( 466 '{}: {}'.format(ep, err) for ep, err in zip(to_try, errors) 467 ) 468 ) 469 ) 470 471 472@implementer(ITor) 473class Tor(object): 474 """ 475 I represent a single instance of Tor and act as a Builder/Factory 476 for several useful objects you will probably want. There are two 477 ways to create a Tor instance: 478 479 - :func:`txtorcon.connect` to connect to a Tor that is already 480 running (e.g. Tor Browser Bundle, a system Tor, ...). 481 - :func:`txtorcon.launch` to launch a fresh Tor instance 482 483 The stable API provided by this class is :class:`txtorcon.interface.ITor` 484 485 If you desire more control, there are "lower level" APIs which are 486 the very ones used by this class. However, this "highest level" 487 API should cover many use-cases:: 488 489 import txtorcon 490 491 @inlineCallbacks 492 def main(reactor): 493 # tor = yield txtorcon.connect(UNIXClientEndpoint(reactor, "/var/run/tor/control")) 494 tor = yield txtorcon.launch(reactor) 495 496 onion_ep = tor.create_onion_endpoint(port=80) 497 port = yield onion_ep.listen(Site()) 498 print(port.getHost()) 499 """ 500 501 def __init__(self, reactor, control_protocol, _tor_config=None, _process_proto=None, _non_anonymous=None): 502 """ 503 don't instantiate this class yourself -- instead use the factory 504 methods :func:`txtorcon.launch` or :func:`txtorcon.connect` 505 """ 506 self._protocol = control_protocol 507 self._config = _tor_config 508 self._reactor = reactor 509 # this only passed/set when we launch() 510 self._process_protocol = _process_proto 511 # cache our preferred socks port (please use 512 # self._default_socks_endpoint() to get one) 513 self._socks_endpoint = None 514 # True if we've turned on non-anonymous mode / Onion services 515 self._non_anonymous = _non_anonymous 516 517 @inlineCallbacks 518 def quit(self): 519 """ 520 Closes the control connection, and if we launched this Tor 521 instance we'll send it a TERM and wait until it exits. 522 """ 523 if self._protocol is not None: 524 yield self._protocol.quit() 525 if self._process_protocol is not None: 526 yield self._process_protocol.quit() 527 if self._protocol is None and self._process_protocol is None: 528 raise RuntimeError( 529 "This Tor has no protocol instance; we can't quit" 530 ) 531 if self._protocol is not None: 532 yield self._protocol.on_disconnect 533 534 @property 535 def process(self): 536 """ 537 An object implementing 538 :api:`twisted.internet.interfaces.IProcessProtocol` if this 539 Tor instance was launched, or None. 540 """ 541 if self._process_protocol: 542 return self._process_protocol 543 return None 544 545 @property 546 def protocol(self): 547 """ 548 The TorControlProtocol instance that is communicating with this 549 Tor instance. 550 """ 551 return self._protocol 552 553 @property 554 def version(self): 555 return self._protocol.version 556 557 @inlineCallbacks 558 def get_config(self): 559 """ 560 :return: a Deferred that fires with a TorConfig instance. This 561 instance represents up-to-date configuration of the tor 562 instance (even if another controller is connected). If you 563 call this more than once you'll get the same TorConfig back. 564 """ 565 if self._config is None: 566 self._config = yield TorConfig.from_protocol(self._protocol) 567 returnValue(self._config) 568 569 def web_agent(self, pool=None, socks_endpoint=None): 570 """ 571 :param socks_endpoint: If ``None`` (the default), a suitable 572 SOCKS port is chosen from our config (or added). If supplied, 573 should be a Deferred which fires an IStreamClientEndpoint 574 (e.g. the return-value from 575 :meth:`txtorcon.TorConfig.socks_endpoint`) or an immediate 576 IStreamClientEndpoint You probably don't need to mess with 577 this. 578 579 :param pool: passed on to the Agent (as ``pool=``) 580 """ 581 if self._non_anonymous: 582 raise Exception( 583 "Cannot use web_agent when in non_anonymous mode" 584 ) 585 # local import since not all platforms have this 586 from txtorcon import web 587 588 if socks_endpoint is None: 589 socks_endpoint = _create_socks_endpoint(self._reactor, self._protocol) 590 if not isinstance(socks_endpoint, Deferred): 591 if not IStreamClientEndpoint.providedBy(socks_endpoint): 592 raise ValueError( 593 "'socks_endpoint' should be a Deferred or an IStreamClient" 594 "Endpoint (got '{}')".format(type(socks_endpoint)) 595 ) 596 return web.tor_agent( 597 self._reactor, 598 socks_endpoint, 599 pool=pool, 600 ) 601 602 @inlineCallbacks 603 def dns_resolve(self, hostname): 604 """ 605 :param hostname: a string 606 607 :returns: a Deferred that calbacks with the hostname as looked-up 608 via Tor (or errback). This uses Tor's custom extension to the 609 SOCKS5 protocol. 610 """ 611 socks_ep = yield self._default_socks_endpoint() 612 ans = yield socks.resolve(socks_ep, hostname) 613 returnValue(ans) 614 615 @inlineCallbacks 616 def dns_resolve_ptr(self, ip): 617 """ 618 :param ip: a string, like "127.0.0.1" 619 620 :returns: a Deferred that calbacks with the IP address as 621 looked-up via Tor (or errback). This uses Tor's custom 622 extension to the SOCKS5 protocol. 623 """ 624 socks_ep = yield self._default_socks_endpoint() 625 ans = yield socks.resolve_ptr(socks_ep, ip) 626 returnValue(ans) 627 628 @inlineCallbacks 629 def add_onion_authentication(self, onion_host, token): 630 """ 631 Add a client-side authentication token for a particular Onion 632 service. 633 """ 634 # if we add the same onion twice, Tor rejects us. We throw an 635 # error if we already have that .onion but the incoming token 636 # doesn't match 637 if isinstance(onion_host, bytes): 638 onion_host = onion_host.decode('ascii') 639 640 config = yield self.get_config() 641 tokens = { 642 servauth.split()[0]: servauth.split()[1] 643 for servauth in config.HidServAuth 644 } 645 try: 646 maybe_token = tokens[onion_host] 647 if maybe_token != token: 648 raise ValueError( 649 "Token conflict for host '{}'".format(onion_host) 650 ) 651 return 652 except KeyError: 653 pass 654 655 # add our onion + token combo 656 config.HidServAuth.append( 657 u"{} {}".format(onion_host, token) 658 ) 659 yield config.save() 660 661 @inlineCallbacks 662 def remove_onion_authentication(self, onion_host): 663 """ 664 Remove a token for an onion host 665 666 :returns: True if successful, False if there wasn't a token 667 for that host. 668 """ 669 if isinstance(onion_host, bytes): 670 onion_host = onion_host.decode('ascii') 671 672 config = yield self.get_config() 673 to_remove = None 674 for auth in config.HidServAuth: 675 host, token = auth.split() 676 if host == onion_host: 677 to_remove = auth 678 679 if to_remove is not None: 680 config.HidServAuth.remove(to_remove) 681 yield config.save() 682 returnValue(True) 683 returnValue(False) 684 685 def onion_authentication(self, onion_host, token): 686 """ 687 (Python3 only!) This returns an async context-manager that will 688 add and remove onion authentication. For example, inside an 689 `async def` method that's had `ensureDeferred` called on it:: 690 691 async with tor.onion_authentication("timaq4ygg2iegci7.onion", "seekrit token"): 692 agent = tor.web_agent() 693 resp = await agent.request(b'GET', "http://timaq4ygg2iegci7.onion/") 694 body = await readBody(resp) 695 # after the "async with" the token will be removed from Tor's configuration 696 697 Under the hood, this just uses the add_onion_authentication 698 and remove_onion_authentication methods so on Python2 you can 699 use those together with try/finally to get the same effect. 700 """ 701 if not HAVE_ASYNC: 702 raise RuntimeError( 703 "async context-managers not supported in Python3.4 or lower" 704 ) 705 return _AsyncOnionAuthContext( 706 self, onion_host, token 707 ) 708 709 def stream_via(self, host, port, tls=False, socks_endpoint=None): 710 """ 711 This returns an IStreamClientEndpoint_ instance that will use this 712 Tor (via SOCKS) to visit the ``(host, port)`` indicated. 713 714 :param host: The host to connect to. You MUST pass host-names 715 to this. If you absolutely know that you've not leaked DNS 716 (e.g. you save IPs in your app's configuration or similar) 717 then you can pass an IP. 718 719 :param port: Port to connect to. 720 721 :param tls: If True, it will wrap the return endpoint in one 722 that does TLS (default: False). 723 724 :param socks_endpoint: Normally not needed (default: None) 725 but you can pass an IStreamClientEndpoint_ directed at one 726 of the local Tor's SOCKS5 ports (e.g. created with 727 :meth:`txtorcon.TorConfig.create_socks_endpoint`). Can be 728 a Deferred. 729 730 .. _IStreamClientEndpoint: https://twistedmatrix.com/documents/current/api/twisted.internet.interfaces.IStreamClientEndpoint.html 731 """ 732 if _is_non_public_numeric_address(host): 733 raise ValueError("'{}' isn't going to work over Tor".format(host)) 734 735 if socks_endpoint is None: 736 socks_endpoint = self._default_socks_endpoint() 737 # socks_endpoint may be a a Deferred, but TorClientEndpoint handles it 738 return TorClientEndpoint( 739 host, port, 740 socks_endpoint=socks_endpoint, 741 tls=tls, 742 reactor=self._reactor, 743 ) 744 745 def create_authenticated_onion_endpoint(self, port, auth, private_key=None, version=None): 746 """ 747 WARNING: API subject to change 748 749 When creating an authenticated Onion service a token is 750 created for each user. For 'stealth' authentication, the 751 hostname is also different for each user. The difference between 752 this method and :meth:`txtorcon.Tor.create_onion_endpoint` is 753 in this case the "onion_service" instance implements 754 :class:`txtorcon.IAuthenticatedOnionClients`. 755 756 :returns: an object that implements IStreamServerEndpoint, 757 which will create an "ephemeral" Onion service when 758 ``.listen()`` is called. This uses the ``ADD_ONION`` Tor 759 control-protocol command. The object returned from 760 ``.listen()`` will be a :class:TorOnionListeningPort``; 761 its ``.onion_service`` attribute will be a 762 :class:`txtorcon.IAuthenticatedOnionClients` instance. 763 764 :param port: the port to listen publically on the Tor network 765 on (e.g. 80 for a Web server) 766 767 :param private_key: if not None (the default), this should be 768 the same blob of key material that you received from the 769 :class:`txtorcon.IOnionService` object during a previous 770 run (i.e. from the ``.provate_key`` attribute). 771 772 :param version: if not None, a specific version of service to 773 use; version=3 is Proposition 224 and version=2 is the 774 older 1024-bit key based implementation. 775 776 :param auth: a AuthBasic or AuthStealth instance 777 """ 778 return TCPHiddenServiceEndpoint( 779 self._reactor, self.get_config(), port, 780 hidden_service_dir=None, 781 local_port=None, 782 ephemeral=True, 783 private_key=private_key, 784 version=version, 785 auth=auth, 786 ) 787 788 def create_onion_endpoint(self, port, private_key=None, version=None, single_hop=None): 789 """ 790 WARNING: API subject to change 791 792 :returns: an object that implements IStreamServerEndpoint, 793 which will create an "ephemeral" Onion service when 794 ``.listen()`` is called. This uses the ``ADD_ONION`` tor 795 control-protocol command. The object returned from 796 ``.listen()`` will be a :class:TorOnionListeningPort``; 797 its ``.onion_service`` attribute will be a 798 :class:`txtorcon.IOnionService` instance. 799 800 :param port: the port to listen publically on the Tor network 801 on (e.g. 80 for a Web server) 802 803 :param private_key: if not None (the default), this should be 804 the same blob of key material that you received from the 805 :class:`txtorcon.IOnionService` object during a previous 806 run (i.e. from the ``.private_key`` attribute). 807 808 :param version: if not None, a specific version of service to 809 use; version=3 is Proposition 224 and version=2 is the 810 older 1024-bit key based implementation. 811 812 :param single_hop: if True, pass the `NonAnonymous` flag. Note 813 that Tor options `HiddenServiceSingleHopMode`, 814 `HiddenServiceNonAnonymousMode` must be set to `1` and there 815 must be no `SOCKSPort` configured for this to actually work. 816 """ 817 # note, we're just depending on this being The Ultimate 818 # Everything endpoint. Which seems fine, because "normal" 819 # users should use this or another factory-method to 820 # instantiate them... 821 return TCPHiddenServiceEndpoint( 822 self._reactor, self.get_config(), port, 823 hidden_service_dir=None, 824 local_port=None, 825 ephemeral=True, 826 private_key=private_key, 827 version=version, 828 auth=None, 829 single_hop=single_hop, 830 ) 831 832 def create_filesystem_onion_endpoint(self, port, hs_dir, group_readable=False, version=None): 833 """ 834 WARNING: API subject to change 835 836 :returns: an object that implements IStreamServerEndpoint. When 837 the ``.listen()`` method is called, the endpoint will create 838 an Onion service whose keys are on disk when ``.listen()`` is 839 called. The object returned from ``.listen()`` will be a 840 :class:TorOnionListeningPort``; its ``.onion_service`` 841 attribute will be a :class:`txtorcon.IOnionService` instance. 842 843 :param port: the port to listen publically on the Tor network 844 on (e.g. 80 for a Web server) 845 846 :param hs_dir: the directory in which keys are stored for this 847 service. 848 849 :param group_readable: controls the Tor 850 `HiddenServiceDirGroupReadable` which will either set (or not) 851 group read-permissions on the hs_dir. 852 853 :param version: if not None, a specific version of service to 854 use; version=3 is Proposition 224 and version=2 is the 855 older 1024-bit key based implementation. The default is version 3. 856 """ 857 return TCPHiddenServiceEndpoint( 858 self._reactor, self.get_config(), port, 859 hidden_service_dir=hs_dir, 860 local_port=None, 861 ephemeral=False, 862 private_key=None, 863 group_readable=int(group_readable), 864 version=version, 865 auth=None, 866 ) 867 868 def create_filesystem_authenticated_onion_endpoint(self, port, hs_dir, auth, group_readable=False, version=None): 869 """ 870 WARNING: API subject to change 871 872 :returns: an object that implements IStreamServerEndpoint. When 873 the ``.listen()`` method is called, the endpoint will create 874 an Onion service whose keys are on disk when ``.listen()`` is 875 called. The object returned from ``.listen()`` will be a 876 :class:TorOnionListeningPort``; its ``.onion_service`` 877 attribute will be a :class:`txtorcon.IOnionService` instance. 878 879 :param port: the port to listen publically on the Tor network 880 on (e.g. 80 for a Web server) 881 882 :param hs_dir: the directory in which keys are stored for this 883 service. 884 885 :param auth: instance of :class:`txtorcon.AuthBasic` or 886 :class:`txtorcon.AuthStealth` controlling the type of 887 authentication to use. 888 889 :param group_readable: controls the Tor 890 `HiddenServiceDirGroupReadable` which will either set (or not) 891 group read-permissions on the hs_dir. 892 893 :param version: if not None, a specific version of service to 894 use; version=3 is Proposition 224 and version=2 is the 895 older 1024-bit key based implementation. The default is version 3. 896 """ 897 return TCPHiddenServiceEndpoint( 898 self._reactor, self.get_config(), port, 899 hidden_service_dir=hs_dir, 900 local_port=None, 901 ephemeral=False, 902 private_key=None, 903 group_readable=int(group_readable), 904 version=version, 905 auth=auth, 906 ) 907 908 # XXX or get_state()? and make there be always 0 or 1 states; cf. convo w/ Warner 909 @inlineCallbacks 910 def create_state(self): 911 """ 912 returns a Deferred that fires with a ready-to-go 913 :class:`txtorcon.TorState` instance. 914 """ 915 state = TorState(self.protocol) 916 yield state.post_bootstrap 917 returnValue(state) 918 919 def __str__(self): 920 return "<Tor version='{tor_version}'>".format( 921 tor_version=self._protocol.version, 922 ) 923 924 @inlineCallbacks 925 def is_ready(self): 926 """ 927 :return: a Deferred that fires with True if this Tor is 928 non-dormant and ready to go. This will return True if `GETINFO 929 dormant` is false or if `GETINFO status/enough-dir-info` is 930 true or if `GETINFO status/circuit-established` true. 931 """ 932 info = yield self.protocol.get_info( 933 "dormant", 934 "status/enough-dir-info", 935 "status/circuit-established", 936 ) 937 returnValue( 938 not( 939 int(info["dormant"]) or 940 not int(info["status/enough-dir-info"]) or 941 not int(info["status/circuit-established"]) 942 ) 943 ) 944 945 @inlineCallbacks 946 def become_ready(self): 947 """ 948 Make sure Tor is no longer dormant. 949 950 If Tor is currently dormant, it is woken up by doing a DNS 951 request for torproject.org 952 """ 953 ready = yield self.is_ready() 954 if not ready: 955 yield self.dns_resolve(u'torproject.org') 956 return 957 958 @inlineCallbacks 959 def _default_socks_endpoint(self): 960 """ 961 Returns a Deferred that fires with our default SOCKS endpoint 962 (which might mean setting one up in our attacked Tor if it 963 doesn't have one) 964 """ 965 if self._non_anonymous: 966 raise Exception( 967 "Cannot use SOCKS when in non_anonymous mode" 968 ) 969 if self._socks_endpoint is None: 970 self._socks_endpoint = yield _create_socks_endpoint(self._reactor, self._protocol) 971 returnValue(self._socks_endpoint) 972 973 # For all these create_*() methods, instead of magically computing 974 # the class-name from arguments (e.g. we could decide "it's a 975 # Filesystem thing" if "hidden_service_dir=" is passed) we have an 976 # explicit method for each type of service. This means each method 977 # always returns the same type of object (good!) and user-code is 978 # more explicit about what they want (also good!) .. but the 979 # method names are kind of long (not-ideal) 980 981 @inlineCallbacks 982 def create_onion_service(self, ports, private_key=None, version=3, 983 progress=None, await_all_uploads=False, 984 single_hop=None, detach=None): 985 """ 986 Create a new Onion service 987 988 This method will create a new Onion service, returning (via 989 Deferred) an instance that implements IOnionService. (To 990 create authenticated onion services, see XXX). This method 991 awaits at least one upload of the Onion service's 'descriptor' 992 to the Tor network -- this can take from 30s to a couple 993 minutes. 994 995 :param private_key: None, ``txtorcon.DISCARD`` or a key-blob 996 retained from a prior run 997 998 Passing ``None`` means a new one will be created. It can be 999 retrieved from the ``.private_key`` property of the returned 1000 object. You **must** retain this key yourself (and pass it in 1001 to this method in the future) if you wish to keep the same 1002 ``.onion`` domain when re-starting your program. 1003 1004 Passing ``txtorcon.DISCARD`` means txtorcon will never learn the 1005 private key from Tor and so there will be no way to re-create 1006 an Onion Service on the same address after Tor exits. 1007 1008 :param version: The latest Tor releases support 'Proposition 1009 224' (version 3) services. These are the default. 1010 1011 :param progress: if provided, a function that takes 3 1012 arguments: ``(percent_done, tag, description)`` which may 1013 be called any number of times to indicate some progress has 1014 been made. 1015 1016 :param await_all_uploads: if False (the default) then we wait 1017 until at least one upload of our Descriptor to a Directory 1018 Authority has completed; if True we wait until all have 1019 completed. 1020 1021 :param single_hop: if True, pass the `NonAnonymous` flag. Note 1022 that Tor options `HiddenServiceSingleHopMode`, 1023 `HiddenServiceNonAnonymousMode` must be set to `1` and there 1024 must be no `SOCKSPort` configured for this to actually work. 1025 1026 :param detach: if True, the created service won't be tied to 1027 this control connection and will still be active when this 1028 control-connection goes away (this means the service will 1029 appear in `GETINFO onions/detached` to all other 1030 controllers) 1031 """ 1032 if version not in (2, 3): 1033 raise ValueError( 1034 "The only valid Onion service versions are 2 or 3" 1035 ) 1036 if not isinstance(ports, Sequence) or isinstance(ports, six.string_types): 1037 raise ValueError("'ports' must be a sequence (list, tuple, ..)") 1038 1039 processed_ports = yield _validate_ports(self._reactor, ports) 1040 config = yield self.get_config() 1041 service = yield EphemeralOnionService.create( 1042 reactor=self._reactor, 1043 config=config, 1044 ports=processed_ports, 1045 private_key=private_key, 1046 version=version, 1047 progress=progress, 1048 await_all_uploads=await_all_uploads, 1049 single_hop=single_hop, 1050 detach=detach, 1051 ) 1052 returnValue(service) 1053 1054 @inlineCallbacks 1055 def create_filesystem_onion_service(self, ports, onion_service_dir, 1056 version=3, 1057 group_readable=False, 1058 progress=None, 1059 await_all_uploads=False): 1060 """Create a new Onion service stored on disk 1061 1062 This method will create a new Onion service, returning (via 1063 Deferred) an instance that implements IOnionService. (To 1064 create authenticated onion services, see XXX). This method 1065 awaits at least one upload of the Onion service's 'descriptor' 1066 to the Tor network -- this can take from 30s to a couple 1067 minutes. 1068 1069 :param ports: a collection of ports to advertise; these are 1070 forwarded locally on a random port. Each entry may instead be 1071 a 2-tuple, which chooses an explicit local port. 1072 1073 :param onion_service_dir: a path to an Onion Service 1074 directory. 1075 1076 Tor will write a ``hostname`` file in this directory along 1077 with the private keys for the service (if they do not already 1078 exist). You do not need to retain the private key yourself. 1079 1080 :param version: which kind of Onion Service to create. The 1081 default is ``3`` which are the Proposition 224 1082 services. Version ``2`` are the previous services. There are 1083 no other valid versions currently. 1084 1085 :param group_readable: if True, Tor creates the directory with 1086 group read permissions. The default is False. 1087 1088 :param progress: if provided, a function that takes 3 1089 arguments: ``(percent_done, tag, description)`` which may 1090 be called any number of times to indicate some progress has 1091 been made. 1092 1093 """ 1094 if not isinstance(ports, Sequence) or isinstance(ports, six.string_types): 1095 raise ValueError("'ports' must be a sequence (list, tuple, ..)") 1096 processed_ports = yield _validate_ports(self._reactor, ports) 1097 1098 if version not in (2, 3): 1099 raise ValueError( 1100 "The only valid Onion service versions are 2 or 3" 1101 ) 1102 config = yield self.get_config() 1103 service = yield FilesystemOnionService.create( 1104 reactor=self._reactor, 1105 config=config, 1106 hsdir=onion_service_dir, 1107 ports=processed_ports, 1108 version=version, 1109 group_readable=group_readable, 1110 progress=progress, 1111 await_all_uploads=await_all_uploads, 1112 ) 1113 returnValue(service) 1114 1115 1116class TorNotFound(RuntimeError): 1117 """ 1118 Raised by launch_tor() in case the tor binary was unspecified and could 1119 not be found by consulting the shell. 1120 """ 1121 1122 1123class TorProcessProtocol(protocol.ProcessProtocol): 1124 1125 def __init__(self, connection_creator, progress_updates=None, config=None, 1126 ireactortime=None, timeout=None, kill_on_stderr=True, 1127 stdout=None, stderr=None): 1128 """ 1129 This will read the output from a Tor process and attempt a 1130 connection to its control port when it sees any 'Bootstrapped' 1131 message on stdout. You probably don't need to use this 1132 directly except as the return value from the 1133 :func:`txtorcon.launch_tor` method. tor_protocol contains a 1134 valid :class:`txtorcon.TorControlProtocol` instance by that 1135 point. 1136 1137 connection_creator is a callable that should return a Deferred 1138 that callbacks with a :class:`txtorcon.TorControlProtocol`; 1139 see :func:`txtorcon.launch_tor` for the default one which is a 1140 functools.partial that will call 1141 ``connect(TorProtocolFactory())`` on an appropriate 1142 :api:`twisted.internet.endpoints.TCP4ClientEndpoint` 1143 1144 :param connection_creator: A no-parameter callable which 1145 returns a Deferred which promises a 1146 :api:`twisted.internet.interfaces.IStreamClientEndpoint 1147 <IStreamClientEndpoint>`. If this is None, we do NOT 1148 attempt to connect to the underlying Tor process. 1149 1150 :param progress_updates: A callback which received progress 1151 updates with three args: percent, tag, summary 1152 1153 :param config: a TorConfig object to connect to the 1154 TorControlProtocl from the launched tor (should it succeed) 1155 1156 :param ireactortime: 1157 An object implementing IReactorTime (i.e. a reactor) which 1158 needs to be supplied if you pass a timeout. 1159 1160 :param timeout: 1161 An int representing the timeout in seconds. If we are 1162 unable to reach 100% by this time we will consider the 1163 setting up of Tor to have failed. Must supply ireactortime 1164 if you supply this. 1165 1166 :param kill_on_stderr: 1167 When True, kill subprocess if we receive anything on stderr 1168 1169 :param stdout: 1170 Anything subprocess writes to stdout is sent to .write() on this 1171 1172 :param stderr: 1173 Anything subprocess writes to stderr is sent to .write() on this 1174 1175 :ivar tor_protocol: The TorControlProtocol instance connected 1176 to the Tor this 1177 :api:`twisted.internet.protocol.ProcessProtocol 1178 <ProcessProtocol>`` is speaking to. Will be valid after 1179 the Deferred returned from 1180 :meth:`TorProcessProtocol.when_connected` is triggered. 1181 """ 1182 1183 self.config = config 1184 self.tor_protocol = None 1185 self.progress_updates = progress_updates 1186 1187 # XXX if connection_creator is not None .. is connected_cb 1188 # tied to connection_creator...? 1189 if connection_creator: 1190 self.connection_creator = connection_creator 1191 else: 1192 self.connection_creator = None 1193 # use SingleObserver 1194 self._connected_listeners = [] # list of Deferred (None when we're connected) 1195 1196 self.attempted_connect = False 1197 self.to_delete = [] 1198 self.kill_on_stderr = kill_on_stderr 1199 self.stderr = stderr 1200 self.stdout = stdout 1201 self.collected_stdout = StringIO() 1202 1203 self._setup_complete = False 1204 self._did_timeout = False 1205 self._timeout_delayed_call = None 1206 self._on_exit = [] # Deferred's we owe a call/errback to when we exit 1207 if timeout: 1208 if not ireactortime: 1209 raise RuntimeError( 1210 'Must supply an IReactorTime object when supplying a ' 1211 'timeout') 1212 ireactortime = IReactorTime(ireactortime) 1213 self._timeout_delayed_call = ireactortime.callLater( 1214 timeout, self._timeout_expired) 1215 1216 def when_connected(self): 1217 if self._connected_listeners is None: 1218 return succeed(self) 1219 d = Deferred() 1220 self._connected_listeners.append(d) 1221 return d 1222 1223 def _maybe_notify_connected(self, arg): 1224 """ 1225 Internal helper. 1226 1227 .callback or .errback on all Deferreds we've returned from 1228 `when_connected` 1229 """ 1230 if self._connected_listeners is None: 1231 return 1232 for d in self._connected_listeners: 1233 # Twisted will turn this into an errback if "arg" is a 1234 # Failure 1235 d.callback(arg) 1236 self._connected_listeners = None 1237 1238 def quit(self): 1239 """ 1240 This will terminate (with SIGTERM) the underlying Tor process. 1241 1242 :returns: a Deferred that callback()'s (with None) when the 1243 process has actually exited. 1244 """ 1245 1246 try: 1247 self.transport.signalProcess('TERM') 1248 d = Deferred() 1249 self._on_exit.append(d) 1250 1251 except error.ProcessExitedAlready: 1252 self.transport.loseConnection() 1253 d = succeed(None) 1254 except Exception: 1255 d = fail() 1256 return d 1257 1258 def _signal_on_exit(self, reason): 1259 to_notify = self._on_exit 1260 self._on_exit = [] 1261 for d in to_notify: 1262 d.callback(None) 1263 1264 def outReceived(self, data): 1265 """ 1266 :api:`twisted.internet.protocol.ProcessProtocol <ProcessProtocol>` API 1267 """ 1268 1269 if self.stdout: 1270 self.stdout.write(data.decode('ascii')) 1271 1272 # minor hack: we can't try this in connectionMade because 1273 # that's when the process first starts up so Tor hasn't 1274 # opened any ports properly yet. So, we presume that after 1275 # its first output we're good-to-go. If this fails, we'll 1276 # reset and try again at the next output (see this class' 1277 # tor_connection_failed) 1278 txtorlog.msg(data) 1279 if not self.attempted_connect and self.connection_creator \ 1280 and b'Opening Control listener' in data: 1281 self.attempted_connect = True 1282 # hmmm, we don't "do" anything with this Deferred? 1283 # (should it be connected to the when_connected 1284 # Deferreds?) 1285 d = self.connection_creator() 1286 d.addCallback(self._tor_connected) 1287 d.addErrback(self._tor_connection_failed) 1288# XXX 'should' be able to improve the error-handling by directly tying 1289# this Deferred into the notifications -- BUT we might try again, so 1290# we need to know "have we given up -- had an error" and only in that 1291# case send to the connected things. I think? 1292# d.addCallback(self._maybe_notify_connected) 1293 1294 def _timeout_expired(self): 1295 """ 1296 A timeout was supplied during setup, and the time has run out. 1297 """ 1298 self._did_timeout = True 1299 try: 1300 self.transport.signalProcess('TERM') 1301 except error.ProcessExitedAlready: 1302 # XXX why don't we just always do this? 1303 self.transport.loseConnection() 1304 1305 fail = Failure(RuntimeError("timeout while launching Tor")) 1306 self._maybe_notify_connected(fail) 1307 1308 def errReceived(self, data): 1309 """ 1310 :api:`twisted.internet.protocol.ProcessProtocol <ProcessProtocol>` API 1311 """ 1312 1313 if self.stderr: 1314 self.stderr.write(data) 1315 1316 if self.kill_on_stderr: 1317 self.transport.loseConnection() 1318 raise RuntimeError( 1319 "Received stderr output from slave Tor process: " + data.decode('utf8') 1320 ) 1321 1322 def cleanup(self): 1323 """ 1324 Clean up my temporary files. 1325 """ 1326 1327 all([delete_file_or_tree(f) for f in self.to_delete]) 1328 self.to_delete = [] 1329 1330 def processExited(self, reason): 1331 self._signal_on_exit(reason) 1332 1333 def processEnded(self, status): 1334 """ 1335 :api:`twisted.internet.protocol.ProcessProtocol <ProcessProtocol>` API 1336 """ 1337 self.cleanup() 1338 1339 if status.value.exitCode is None: 1340 if self._did_timeout: 1341 err = RuntimeError("Timeout waiting for Tor launch.") 1342 else: 1343 err = RuntimeError( 1344 "Tor was killed (%s)." % status.value.signal) 1345 else: 1346 err = RuntimeError( 1347 "Tor exited with error-code %d" % status.value.exitCode) 1348 1349 # hmmm, this log() should probably go away...not always an 1350 # error (e.g. .quit() 1351 log.err(err) 1352 self._maybe_notify_connected(Failure(err)) 1353 1354 def progress(self, percent, tag, summary): 1355 """ 1356 Can be overridden or monkey-patched if you want to get 1357 progress updates yourself. 1358 """ 1359 1360 if self.progress_updates: 1361 self.progress_updates(percent, tag, summary) 1362 1363 # the below are all callbacks 1364 1365 def _tor_connection_failed(self, failure): 1366 # FIXME more robust error-handling please, like a timeout so 1367 # we don't just wait forever after 100% bootstrapped (that 1368 # is, we're ignoring these errors, but shouldn't do so after 1369 # we'll stop trying) 1370 # XXX also, should check if the failure is e.g. a syntax error 1371 # or an actually connection failure 1372 1373 # okay, so this is a little trickier than I thought at first: 1374 # we *can* just relay this back to the 1375 # connection_creator()-returned Deferred, *but* we don't know 1376 # if this is "the last" error and we're going to try again 1377 # (and thus e.g. should fail all the when_connected() 1378 # Deferreds) or not. 1379 log.err(failure) 1380 self.attempted_connect = False 1381 return None 1382 1383 def _status_client(self, arg): 1384 args = shlex.split(arg) 1385 if args[1] != 'BOOTSTRAP': 1386 return 1387 1388 kw = find_keywords(args) 1389 prog = int(kw['PROGRESS']) 1390 tag = kw['TAG'] 1391 summary = kw['SUMMARY'] 1392 self.progress(prog, tag, summary) 1393 1394 if prog == 100: 1395 if self._timeout_delayed_call: 1396 self._timeout_delayed_call.cancel() 1397 self._timeout_delayed_call = None 1398 self._maybe_notify_connected(self) 1399 1400 @inlineCallbacks 1401 def _tor_connected(self, proto): 1402 txtorlog.msg("tor_connected %s" % proto) 1403 1404 self.tor_protocol = proto 1405 self.tor_protocol.is_owned = self.transport.pid 1406 1407 yield self.tor_protocol.post_bootstrap 1408 txtorlog.msg("Protocol is bootstrapped") 1409 yield self.tor_protocol.add_event_listener('STATUS_CLIENT', self._status_client) 1410 yield self.tor_protocol.queue_command('TAKEOWNERSHIP') 1411 yield self.tor_protocol.queue_command('RESETCONF __OwningControllerProcess') 1412 if self.config is not None and self.config.protocol is None: 1413 yield self.config.attach_protocol(proto) 1414 returnValue(self) # XXX or "proto"? 1415