High Level API

This is the recommended API. See the Programming Guide for “prose” documentation of these (and other) APIs.

Tor

class txtorcon.Tor(reactor, control_protocol, _tor_config=None, _process_proto=None, _non_anonymous=None)

I represent a single instance of Tor and act as a Builder/Factory for several useful objects you will probably want. There are two ways to create a Tor instance:

  • txtorcon.connect() to connect to a Tor that is already running (e.g. Tor Browser Bundle, a system Tor, …).

  • txtorcon.launch() to launch a fresh Tor instance

The stable API provided by this class is txtorcon.interface.ITor

If you desire more control, there are “lower level” APIs which are the very ones used by this class. However, this “highest level” API should cover many use-cases:

import txtorcon

@inlineCallbacks
def main(reactor):
    # tor = yield txtorcon.connect(UNIXClientEndpoint(reactor, "/var/run/tor/control"))
    tor = yield txtorcon.launch(reactor)

    onion_ep = tor.create_onion_endpoint(port=80)
    port = yield onion_ep.listen(Site())
    print(port.getHost())

don’t instantiate this class yourself – instead use the factory methods txtorcon.launch() or txtorcon.connect()

connect

controller.connect(control_endpoint=None, password_function=None)

Creates a txtorcon.Tor instance by connecting to an already-running tor’s control port. For example, a common default tor uses is UNIXClientEndpoint(reactor, ‘/var/run/tor/control’) or TCP4ClientEndpoint(reactor, ‘localhost’, 9051)

If only password authentication is available in the tor we connect to, the password_function is called (if supplied) to retrieve a valid password. This function can return a Deferred.

For example:

import txtorcon
from twisted.internet.task import react
from twisted.internet.defer import inlineCallbacks

@inlineCallbacks
def main(reactor):
    tor = yield txtorcon.connect(
        TCP4ClientEndpoint(reactor, "localhost", 9051)
    )
    state = yield tor.create_state()
    for circuit in state.circuits:
        print(circuit)
Parameters:
  • control_endpoint – None, an IStreamClientEndpoint to connect to, or a Sequence of IStreamClientEndpoint instances to connect to. If None, a list of defaults are tried.

  • password_function – See txtorcon.TorControlProtocol

Returns:

a Deferred that fires with a txtorcon.Tor instance

launch

controller.launch(progress_updates=None, control_port=None, data_directory=None, socks_port=None, non_anonymous_mode=None, stdout=None, stderr=None, timeout=None, tor_binary=None, user=None, connection_creator=None, kill_on_stderr=True, _tor_config=None)

launches a new Tor process, and returns a Deferred that fires with a new txtorcon.Tor instance. From this instance, you can create or get any “interesting” instances you need: the txtorcon.TorConfig instance, create endpoints, create txtorcon.TorState instance(s), etc.

Note that there is NO way to pass in a config; we only expost a couple of basic Tor options. If you need anything beyond these, you can access the TorConfig instance (via .config) and make any changes there, reflecting them in tor with .config.save().

You can igore all the options and safe defaults will be provided. However, it is recommended to pass data_directory especially if you will be starting up Tor frequently, as it saves a bunch of time (and bandwidth for the directory authorities). “Safe defaults” means:

  • a tempdir for a DataDirectory is used (respecting TMP) and is deleted when this tor is shut down (you therefore probably want to supply the data_directory= kwarg);

  • a random, currently-unused local TCP port is used as the SocksPort (specify socks_port= if you want your own). If you want no SOCKS listener at all, pass socks_port=0

  • we set __OwningControllerProcess and call TAKEOWNERSHIP so that if our control connection goes away, tor shuts down (see control-spec 3.23).

  • the launched Tor will use COOKIE authentication.

Parameters:
  • reactor – a Twisted IReactorCore implementation (usually twisted.internet.reactor)

  • progress_updates – a callback which gets progress updates; gets 3 args: percent, tag, summary (FIXME make an interface for this).

  • data_directory – set as the DataDirectory option to Tor, this is where tor keeps its state information (cached relays, etc); starting with an already-populated state directory is a lot faster. If None (the default), we create a tempdir for this and delete it on exit. It is recommended you pass something here.

  • non_anonymous_mode – sets the Tor options HiddenServiceSingleHopMode and HiddenServiceNonAnonymousMode to 1 and un-sets any SOCKSPort config, thus putting this Tor client into “non-anonymous mode” which allows starting so-called Single Onion services – which use single-hop circuits to rendezvous points. See WARNINGs in Tor manual! Also you need Tor 0.3.4.1 or later (e.g. any 0.3.5.* or newer) for this to work properly.

  • stdout – a file-like object to which we write anything that Tor prints on stdout (just needs to support write()).

  • stderr – a file-like object to which we write anything that Tor prints on stderr (just needs .write()). Note that we kill Tor off by default if anything appears on stderr; pass “kill_on_stderr=False” if you don’t want this behavior.

  • tor_binary – path to the Tor binary to run. If None (the default), we try to find the tor binary.

  • kill_on_stderr – When True (the default), if Tor prints anything on stderr we kill off the process, close the TorControlProtocol and raise an exception.

  • connection_creator – is mostly available to ease testing, so you probably don’t want to supply this. If supplied, it is a callable that should return a Deferred that delivers an IProtocol or ConnectError. See twisted.internet.interfaces.IStreamClientEndpoint.connect Note that this parameter is ignored if config.ControlPort == 0

Returns:

a Deferred which callbacks with txtorcon.Tor instance, from which you can retrieve the TorControlProtocol instance via the .protocol property.

HACKS:

  1. It’s hard to know when Tor has both (completely!) written its authentication cookie file AND is listening on the control port. It seems that waiting for the first ‘bootstrap’ message on stdout is sufficient. Seems fragile…and doesn’t work 100% of the time, so FIXME look at Tor source.

XXX this “User” thing was, IIRC, a feature for root-using scripts (!!) that were going to launch tor, but where tor would drop to a different user. Do we still want to support this? Probably relevant to Docker (where everything is root! yay!)

User: if this exists, we attempt to set ownership of the tempdir to this user (but only if our effective UID is 0).