txtorcon Documentation Release 1.0.0

meejah

January 25, 2017

Contents

1 Documentation 3 1.1 Introduction...... 3 1.1.1 Features Overview...... 3 1.1.2 Shell-cast Overview...... 4 1.1.3 Known Users...... 4 1.2 Installing txtorcon...... 4 1.2.1 Latest Release...... 4 1.2.2 Compatibility...... 5 1.2.3 Configuration...... 5 1.2.4 Source Code...... 5 1.2.5 Development Environment...... 6 1.2.6 Integration Tests...... 6 1.2.7 Dependencies / Requirements...... 6 1.3 Programming Guide...... 7 1.3.1 API Stability...... 7 1.3.2 A Tor Instance...... 8 1.3.3 A Note On Style...... 8 1.3.4 Tracking and Changing Tor’s Configuration...... 8 1.3.5 Monitor and Change Tor’s State...... 9 1.3.6 Making Connections Over Tor...... 10 1.3.7 Onion (Hidden) Services...... 11 1.3.8 Custom Circuits...... 14 1.3.9 Attaching Streams to Circuits...... 15 1.4 Examples...... 15 1.4.1 Web: clients...... 15 1.4.2 Web: servers (services)...... 17 1.4.3 Starting Tor...... 21 1.4.4 Circuits and Streams...... 24 1.4.5 Configuration...... 28 1.4.6 Events...... 29 1.4.7 Miscellaneous...... 29 1.5 Contributions...... 31 1.5.1 Contact Information...... 31 1.5.2 Public Key...... 31 1.5.3 Pull Requests...... 32 1.5.4 Making a Release...... 32

2 Official Releases: 35

i 2.1 Releases...... 35 2.1.1 upcoming: 1.0.0...... 35 2.1.2 unreleased...... 36 2.1.3 v0.18.0...... 36 2.1.4 v0.17.0...... 36 2.1.5 v0.16.1...... 36 2.1.6 v0.16.0...... 36 2.1.7 v0.15.1...... 36 2.1.8 v0.15.0...... 37 2.1.9 v0.14.1...... 37 2.1.10 v0.14.0...... 37 2.1.11 v0.13.0...... 38 2.1.12 v0.12.0...... 38 2.1.13 v0.11.0...... 38 2.1.14 v0.10.1...... 39 2.1.15 v0.10.0...... 39 2.1.16 v0.9.2...... 39 2.1.17 v0.9.1...... 40 2.1.18 v0.8.2...... 40 2.1.19 v0.8.1...... 40 2.1.20 v0.8.0...... 41 2.1.21 v0.7...... 41 2.1.22 v0.6...... 41 2.1.23 v0.5...... 42 2.1.24 v0.4...... 42 2.1.25 v0.3...... 42 2.1.26 v0.2...... 42 2.1.27 v0.1...... 43

3 API Documentation 45 3.1 API Documentation...... 45 3.1.1 High Level API...... 45 3.1.2 Tracking and Changing Live Tor State...... 47 3.1.3 Reading and Writing Live Tor Configuration...... 54 3.1.4 Endpoints and Related Classes...... 57 3.1.5 Low-Level Protocol Classes...... 61 3.1.6 txtorcon.interface Module...... 66 3.1.7 txtorcon.util Module...... 69

4 Indices and tables 71

ii txtorcon Documentation, Release 1.0.0

• docs: https://txtorcon.readthedocs.org or http://timaq4ygg2iegci7.onion • code: https://github.com/meejah/txtorcon • torsocks git clone git://timaq4ygg2iegci7.onion/txtorcon.git • If this is your first time exploring txtorcon, please look at the Introduction first. Supported and tested platforms: Python 2.7+, Python 3.5+, PyPy 5.0.0+ using Twisted 15.5.0+, 16.3.0+ or later.

Contents 1 txtorcon Documentation, Release 1.0.0

2 Contents CHAPTER 1

Documentation

1.1 Introduction txtorcon is an implementation of the control-spec for Tor using the Twisted networking library for Python. With txtorcon you can launch tor; connect to already-running tor instances; use tor as a client (via SOCKS5); set up services over tor; change all aspects of configuration; track live state (active circuits and streams, etc); do DNS via Tor; and query other information from the tor daemon. txtorcon would be of interest to anyone wishing to write event-based software in Python that uses the Tor network as a client or a service (or just wants to display information about a locally running tor). Twisted already provides many robust protocol implementations, deployment, logging and integration with GTK, and other graphics frameworks – so txtorcon can be used for command-line or GUI applications or integrate with long-lived daemons easily. In fact, due to support for endpoints (adding the tor: and onion: plugins), many Twisted applications can now integrate with Tor with no code changes. For example, you can use the existing Twisted webserver via twistd to serve your ~/public_html directory over an onion service: $ sudo apt-get install python-txtorcon $ twistd web --port "onion:80" --path ~/public_html txtorcon strives to provide sane and safe defaults.

1.1.1 Features Overview

Currently, txtorcon is capable of: • making arbitrary client connections to other services over Tor; • configuring twisted.web.client.Agent instances to do Web requests over Tor; • doing both of the above over specific circuits; • listening as an Onion service; • maintaining up-to-date (live) state information about Tor: Circuits, Streams and Routers (relays); • maintaining current (live) configuration information; • maintaining representation of Tor’s address mappings (with expiry); • interrogating initial state of all three of the above; • listening for and altering stream -> circuit mappings; • building custom circuits;

3 txtorcon Documentation, Release 1.0.0

• Circuit and Stream state listeners; • listening for any Tor EVENT; • launching and/or controlling a Tor instance (including Tor Browser Bundle); • complete Twisted endpoint support (both “onion”/server side and client-side). This means you may be able to use existing Twisted software via Tor with no code changes. It also is the preferred way to connect (or listen) in Twisted. Comments (positive or negative) appreciated. Even better if they come with patches

1.1.2 Shell-cast Overview

A text-only screencast-type overview of some of txtorcon’s features, from asciinema.org:

1.1.3 Known Users

• magic-wormhole “get things from one computer to another, safely” • Tahoe-LAFS a Free and Open encrypted distributed storage system • txtorcon received a brief mention at 29C3 starting at 12:20 (or via youtube). • carml command-line utilities for Tor • foolscap RPC system inspired by Twisted’s built-in “Perspective Broker” package. • APAF anonymous Python application framework • OONI the Open Observatory of Network Interference • exitaddr scan Tor exit addresses • txtorhttpproxy simple HTTP proxy in Twisted • bulb Web-based Tor status monitor • onionvpn “ipv6 to onion service virtual public network adapter” • torperf2 new Tor node network performance measurement service • torweb web-based Tor controller/monitor • potator “A Tor-based Decentralized Virtual Private Network Application”

1.2 Installing txtorcon

1.2.1 Latest Release txtorcon is on PyPI and in Debian since jessie (thanks to Lunar and now irl!). So, one of these should work: • install latest release: pip install txtorcon • Debian or Ubuntu: apt-get install python-txtorcon • Watch an asciinema demo for an overview.

4 Chapter 1. Documentation txtorcon Documentation, Release 1.0.0

Rendered documentation for the latest release is at txtorcon.readthedocs.org. What exists for release-notes are in “Releases”. If you’re still using wheezy, python-txtorcon is also in wheezy-backports. To install, do this as root: # echo "deb http://ftp.ca.debian.org/debian/ wheezy-backports main" >> /etc/apt/sources.list # apt-get update # apt-get install python-txtorcon

It also appears txtorcon is in Gentoo but I don’t use Gentoo (if anyone has a shell-snippet that installs it, send a pull-request). I am told this package also needs a maintainer; see XXX. Installing the wheel files requires a recent pip and setuptools. At least on Debian, it is important to upgrade setuptools before pip. This procedure appears to work fine: virtualenv foo . foo/bin/activate pip install --upgrade setuptools pip install --upgrade pip pip install path/to/txtorcon-*.whl

1.2.2 Compatibility txtorcon runs all tests cleanly under Python2, Python3 and PyPy on: • Debian: “squeeze”, “wheezy” and “jessie” • OS X: 10.4 (naif), 10.8 (lukas lueg), 10.9 (kurt neufeld) • Fedora 18 (lukas lueg) • FreeBSD 10 (enrique fynn) (needed to install “lsof”) • RHEL6 • Reports from other OSes appreciated.

1.2.3 Tor Configuration

Using Tor’s cookie authentication is the most convenient way to connect; this proves that your user can read a cookie file written by Tor. To enable this, you’ll want to have the following options on in your torrc: CookieAuthentication 1 CookieAuthFileGroupReadable 1

Note that “Tor BrowserBundle” is configured this way by default, on port 9151. If you want to use unix sockets to speak to tor (highly recommended) add this to your config (Debian is already set up like this): ControlSocketsGroupWritable 1 ControlSocket /var/run/tor/control

1.2.4 Source Code

Most people will use the code from https://github.com/meejah/txtorcon The canonical URI is http://timaq4ygg2iegci7.onion I sign tags with my public key (meejah.asc) • git clone https://github.com/meejah/txtorcon.git • torsocks git clone git://timaq4ygg2iegci7.onion/meejah/txtorcon.git

1.2. Installing txtorcon 5 txtorcon Documentation, Release 1.0.0

Rendered documentation for the latest release is at txtorcon.readthedocs.org. See Contributions if you wish to contribute back to txtorcon :)

1.2.5 Development Environment

I like to set up my Python development like this: $ git clone https://github.com/meejah/txtorcon.git $ echo "if you later clone it on github, do this:" $ git remote add -f github git+ssh://[email protected]//txtorcon.git $ cd txtorcon $ virtualenv venv $ source venv/bin/activate (venv)$ pip install --editable .[dev] # "dev" adds more deps, like Sphinx (venv)$ make doc (venv)$ make test (venv)$ tox # run all tests, in all supported configs

You can now edit code in the repository as normal. To submit a patch, the easiest way is to “clone” the txtorcon project, then “fork” on github and add a remote called “github” with your copy of the code to which you can push (git remote add -f github git+ssh://[email protected]//txtorcon.git). The -f is so you don’t have to run git fetch right after. Now, you can push a new branch you’ve made to GitHub with git push github branch-name and then examine it and open a pull-request. This will trigger Travis to run the tests, after which coverage will be produced (and a bot comments on the pull-request). If you require any more changes, the easiest thing to do is just commit them and push them. (If you know how, re-basing/re-arranging/squashing etc is nice to do too). See Contributions for more.

1.2.6 Integration Tests

There are a couple of simple integration tests using Docker in the integration/ directory; these make a debootstrap-built base image and then do the test inside containers cloned from this – no trusting https://docker.io required. See integration/README for more information. If you’re on Debian, there’s a decent chance running make txtorcon-tester followed by make integration from the root of the checkout will work (the first commands ultimately runs debootstrap and some apt commands besides docker things).

1.2.7 Dependencies / Requirements

These should have been installed by whichever method you chose above, but are listed here for completeness. You can get all the development requirements with e.g. pip install txtorcon[dev]. • twisted: txtorcon should work with any Twisted 11.1.0 or newer. Twisted 15.4.0+ works with Python3, and so does txtorcon (if you find something broken on Py3 please file a bug). • ipaddress: a standard module in Python3, but requires installing the backported package on Python2. • dev only: Sphinx if you want to build the documentation. In that case you’ll also need something called python-repoze.sphinx.autointerface (at least in Debian) to build the Interface-derived docs prop- erly. • dev only: coverage to run the code-coverage metrics. • dev only: Tox to run different library revisions.

6 Chapter 1. Documentation txtorcon Documentation, Release 1.0.0

• dev optional: GraphViz is used in the tests (and to generate state-machine diagrams, if you like) but those tests are skipped if “dot” isn’t in your path In any case, on a Debian wheezy, squeeze or Ubuntu system, this should work (as root): # apt-get install -y python-setuptools python-twisted python-ipaddress graphviz tor # echo "for development:" # apt-get install -y python-sphinx python-repoze.sphinx.autointerface python-coverage libgeoip-dev

Using pip this would be: $ pip install --user Twisted ipaddress pygeoip $ echo "for development:" $ pip install --user GeoIP Sphinx repoze.sphinx.autointerface coverage

or: $ pip install -r requirements.txt $ pip install -r dev-requirements.txt

1.3 Programming Guide

• API Stability • A Tor Instance – Connecting to a Running Tor – Launching a New Tor • A Note On Style • Tracking and Changing Tor’s Configuration • Monitor and Change Tor’s State • Making Connections Over Tor – SOCKS5 • Onion (Hidden) Services – Onion Services Endpoints API – Creating Onion Endpoints – Non-Authenticated Services – Authenticated Services – Onion Service Configuration • Custom Circuits – Building a Single Circuit – Building Many Circuits • Attaching Streams to Circuits

1.3.1 API Stability

In general, any method or class prefixed with an underscore (like _method or _ClassName) is private, and the API may change at any time. You SHOULD NOT use these. Any method in an interface class (which all begin with I, like IAnInterface) are stable, public APIs and will maintain backwards-compatibility between releases. There is one exception to this at the moment: the hidden- / onion- services APIs are NOT yet considered stable, and may still change somewhat. Any APIs that will go away will first be deprecated for at least one major release before being removed.

1.3. Programming Guide 7 txtorcon Documentation, Release 1.0.0

1.3.2 A Tor Instance

You will need a connection to a Tor instance for txtorcon to control. This can be either an already-running tor that you’re authorized to connect to, or a tor instance that has been freshly launched by txtorcon. We abstract “a tor instance” behind the txtorcon.Tor class, which provides a very high-level API for all the other things you might want to do with that Tor: • make client-type connections over tor (see “Making Connections Over Tor”); • change its configuration (see “Tracking and Changing Tor’s Configuration”); • monitor its state (see “Monitor and Change Tor’s State”); • offer hidden-/onion- services via tor (see ”:ref:‘‘”); • issue low-level commands (see “Low-Level Protocol Classes”) The actual control-protocol connection to tor is abstracted behind txtorcon.TorControlProtocol. This can usually be ignored by most users, but can be useful to issue protocol commands directly, listen to raw events, etc.

Connecting to a Running Tor

Tor can listen for control connections on TCP ports, or UNIX sockets. See “Tor Configuration” for information on how to configure tor to work with txtorcon. By default, “COOKIE” authentication is used; only if that is not available do we try password authentication. To connect, use txtorcon.connect() which returns a Deferred that will fire with a txtorcon.Tor instance. If you need access to the txtorcon.TorControlProtocol instance, it’s available via the .protocol property (there is always exactly one of these per txtorcon.Tor instance).

Launching a New Tor

It’s also possible to launch your own Tor instance. txtorcon keeps a “global” tor available for use by e.g. the .global_tor endpoint factory functions (like txtorcon.TCPHiddenServiceEndpoint.global_tor()). You can access it via txtorcon.get_global_tor(). There is exactly zero or one of these per Python process that uses txtorcon. To explicitly launch your own Tor instance, use txtorcon.launch(). You can pass a couple of minimal op- tions (data_directory being recommended). If you need to set other Tor options, use .config to retrieve the txtorcon.TorConfig instance associated with this tor and change configuration afterwards.

1.3.3 A Note On Style

Most of txtorcon tends towards “attribute-style access”. The guiding principle is that “mere data” that is immediately available will be an attribute, whereas things that “take work” or are async (and thus return Deferred s) will be functions. For example, txtorcon.Router.get_location() is a method because it potentially has to ask Tor for the country, whereas txtorcon.Router.hex_id is a plain attribute because it’s always available.

1.3.4 Tracking and Changing Tor’s Configuration

Instances of the txtorcon.TorConfig class represent the current, live state of a running Tor. There is a bit of attribute-magic to make it possible to simply get and set things easily:

8 Chapter 1. Documentation txtorcon Documentation, Release 1.0.0

tor= launch(..) print("SOCKS ports: {}".format(tor.config.SOCKSPort)) tor.config.ControlPort.append(4321) tor.config.save()

Only when .save() is called are any SETCONF commands issued – and then, all changed configuration values are sent in a single command. All TorConfig instances subscribe to configuration updates from Tor, so “live state” includes actions by any other controllers that may be connected. For some configuration items, the order they’re sent to Tor matters. Sometimes, if you change one config item, you have to set a series of related items. TorConfig handles these cases for you – you just manipulate the configuration, and wait for .save()‘s Deferred to fire and the running tor’s configuration is updated. Note there is a tiny window during which the state may appear slightly inconsistent if you have multiple TorConfig instances: after Tor has acknowledged a SETCONF command, but before a separate TorConfig instance has gotten all the CONF_CHANGED events (because they’re hung up in the networking stack for some reason). This shouldn’t concern most users. (I’m not even 100% sure this is possible; it may be that Tor doesn’t send the OK until after all the CONF_CHANGED events) Since txtorcon.TorConfig conforms to the Iterator protocol, you can easily find all the config-options that Tor supports: tor= launch(..) for config_key in tor.config: print("{} has value: {}".format(config_key, getattr(tor.config.config_key)))

These come from interrogating tor using GETINFO config/names and so represent the configuration options of the current connected Tor process. If the value “isn’t set” (i.e. is the default), the value from Tor will be txtorcon.DEFAULT_VALUE. When you set values into TorConfig, they are parsed according to control-spec for the different types given to the values, via information from GETINFO config/names. So, for example, setting .SOCKSPort to a "quux" won’t work. Of course, it would also fail the whole SETCONF command if txtorcon happens to allow some values that Tor doesn’t. Unfortunately, for any item that’s a list, Tor doesn’t tell us anything about each element so they’re all strings.

1.3.5 Monitor and Change Tor’s State

Instances of txtorcon.TorState prepresent a live, interactive version of all the relays/routers (txtorcon.Router instances), all circuits (txtorcon.Circuit instances) and streams (txtorcon.Stream instances) active in the underlying Tor instance. As the TorState instance has subscribed to various events from Tor, the “live” state represents an “as up-to-date as possible” view.

Note: If you need to be absolutely sure there’s nothing stuck in networking buffers, you can issue a do-nothing command to Tor via txtorcon.TorControlProtocol.queue_command() (e.g. yield queue_command("GETINFO version")). Most users shouldn’t have to worry about this edge-case.

You can modify the state of these things in a few simple ways. For example, you can call txtorcon.Stream.close() or txtorcon.Circuit.close() to cause a stream or circuit to be closed. You can wait for a circuit to become usable with txtorcon.Circuit.when_built(). For a lot of the read-only state, you can simply access interesting attributes. The relays through which a circuit traverses are in Circuit.path (a list of txtorcon.Router instances), Circuit.streams contains a list

1.3. Programming Guide 9 txtorcon Documentation, Release 1.0.0

of txtorcon.Stream instances, .state and .purpose are strings. .time_created returns a datetime instance. There are also some convenience functions like txtorcon.Circuit.age(). For sending streams over a particular circuit, txtorcon.Circuit.stream_to() returns an IStreamClientEnd- point implementation that will cause a subsequent .connect() on it to go via the given circuit in Tor. Combined with a txtorcon.CircuitBuilder this gives the power to do many things. Listening for certain events to happen can be done by implementing the interfaces txtorcon.interface.IStreamListener and txtorcon.interface.ICircuitListener. You can request notifications on a tor-wide basis with txtorcon.TorState.add_circuit_listener() or txtorcon.TorState.add_stream_listener(). If you are just interested in a single circuit, you can call txtorcon.Circuit.listen() directly on a Circuit instance. (XXX think about the composible-style API; e.g. circuit.on(’extend’, call_back) and/or state.on(’circuit_extend’, call_back)) The Tor relays are abstracted with txtorcon.Router instances. Again, these have read-only attributes for interest- ing information, e.g.: id_hex, ip, flags (a list of strings), bandwidth, policy, etc. Note that all information in these objects is from “microdescriptors”. If you’re doing a long-running iteration over relays, it may be important to remember that the collection of routers can change every hour (when a new “consensus” from the Directory Authori- ties is published) which may change the underlying collection (e.g. txtorcon.TorState.routers_by_hash) over which you’re iterating. Here’s a simple sketch that traverses all circuits printing their router IDs, and closing each stream and circuit after- wards: (XXX FIXME test this for realz; can we put it in a “listing”-type file?) @inlineCallbacks def main(reactor): tor= yield connect(reactor, UNIXClientEndpoint('/var/run/tor/control')) state= yield tor.get_state() for circuit in state.circuits.values(): path='->'.join(map(lambdar:r.id_hex, circuit.streams)) print("Circuit {} through {}".format(circuit.id, path)) for stream in circuit.streams: print(" Stream {} to {}".format(stream.id, stream.target_host)) yield stream.close() yield circuit.close()

1.3.6 Making Connections Over Tor

SOCKS5

Tor exposes a SOCKS5 interface to make client-type connections over the network. There are also a couple of custom extensions tor provides to do DNS resolution over a Tor circuit (txtorcon supports these, too). All client-side interactions are via instances that implement IStreamClientEndpoint. There are several factory func- tions used to create suitable instances. The recommended API is to acquire a txtorcon.Tor instance (see “A Tor Instance”) and then call txtorcon.Tor.create_client_endpoint(). To do DNS lookups (or reverse lookups) via a Tor circuit, use txtorcon.Tor.dns_resolve() and txtorcon.Tor.dns_resolve_ptr(). A common use-case is to download a Web resource; you can do so via Twisted’s built-in twisted.web.client package, or using the friendlier treq library. In both cases, you need a twisted.web.client.Agent instance which you can acquire with txtorcon.Tor.web_agent() or txtorcon.Circuit.web_agent(). The latter is used to

10 Chapter 1. Documentation txtorcon Documentation, Release 1.0.0

make the request over a specific circuit. Usually, txtorcon will simply use one of the available SOCKS ports configured in the Tor it is connected to – if you care which one, you can specify it as the optional socks_endpoint= argument.

Note: Tor supports SOCKS over Unix sockets. So does txtorcon. To take advantage of this, simply pass a valid SocksPort value for unix sockets (e.g. unix:/tmp/foo/socks) as the socks_config argument to either web_agent() call. If this doesn’t already exist in the underlying Tor, it will be added. Tor has particular require- ments for the directory in which the socket file is (0700).

If you need a stream to go over a specific circuit, see “Building Many Circuits”. (notes to self): • CircuitBuilder (for the the open ticket making a higher-level Attacher) - a factory/builder that creates Circuit instances • Circuit.create_client_endpoint() ? (i.e. makes an endpoint whose streams all go over this circuit) - hence can use via TorState or via CircuitBuilder You can also use Twisted’s clientFromString API as txtorcon registers a tor: plugin. This also implies that any Twisted-using program that supports configuring endpoint strings gets Tor support “for free”. For example, passing a string like tor:timaq4ygg2iegci7.onion:80 to clientFromString will return an endpoint that will connect to txtorcon’s hidden-service website. Note that these endpoints will use the “global to txtorcon” tor instance (available from txtorcon.get_global_tor()). Thus, if you want to control which tor instance your circuit goes over, this is not a suitable API. There are also lower-level APIs to create txtorcon.TorClientEndpoint instances directly if you have a txtorcon.TorConfig instance. These very APIs are used by the Tor object mentioned above. If you have a use-case that requires using this API, I’d be curious to learn why the txtorcon.Tor methods are un-suitable (as those are the suggested API).

1.3.7 Onion (Hidden) Services

An “Onion Service” (also called a “Hidden Service”) refers to a feature of Tor allowing servers (e.g. a Web site) to get additional security properties such as: hiding their network location; providing end-to-end encryption; self-certifying domain-names; or offering authentication. For details of how this works, please read Tor’s documentation on Hidden Services. For more background, the RiseUp Onion service best-practices guide is a good read as well. From an API perspective, here are the parts we care about: • each service has a secret, private key (with a corresponding public part): – these keys can be on disk (in the “hidden service directory”); – or, they can be “ephemeral” (only in memory); • the “host name” is a hash of the public-key (e.g. timaq4ygg2iegci7.onion); • a “Descriptor” (which tells clients how to connect) must be published; • a service has a list of port-mappings (public -> local) – e.g. "80 127.0.0.1:5432" says you can contact the service publically on port 80, which Tor will redirect to a daemon running locally on port 5432; – note that “Descriptors” don’t show this information • services can be “authenticated”, which means they have a list of client names for which Tor creates associated keys (.auth_token).

1.3. Programming Guide 11 txtorcon Documentation, Release 1.0.0

• Tor has two flavours of service authentication: basic and stealth – there’s no API-level difference, but the .hostname is unique for each client in the stealth case. • See Creating Onion Endpoints for details on how to choose which (if any) authentication method you’d like To summarize the above in a table format, here are the possible types of Onion Service interfaces classes you may interact with (ephemeral services don’t yet support any authentication). Keys on disk Keys in memory no authentication IFilesystemOnionService IOnionService basic/stealth authentication IOnionClients Note that it’s up to you to save the private keys of ephemeral services if you want to re-launch them later; the “ephemeral” refers to the fact that Tor doesn’t persist the private keys – when Tor shuts down, they’re gone and there will never be a service at the same URI again.

Onion Services Endpoints API

No matter which kind of service you need, you interact via Twisted’s IStreamServerEndpoint interface. There are various txtorcon methods (see “Creating Onion Endpoints”) which return some instance implementing that interface. These instances will also implement txtorcon.IProgressProvider – which is a hook to register listeners which get updates about Tor’s launching progress (if we started a new Tor) and Descriptor uploading. Fundamentally, “authenticated” services are different from non-authenticated services because they have a list of clients. Therefore, there are two different endpoint types: • txtorcon.TCPHiddenServiceEndpoint • txtorcon.TCPAuthenticatedHiddenServiceEndpoint In either case, the listen method will return an instance implementing IListeningPort. In addition to IListeningPort, these instances will implement one of: • txtorcon.IOnionService or; • txtorcon.IOnionClients The first one corresponds to a non-authenticated service, while the latter is authenticated. The latter manages a collec- tion of instances by (arbitrary) client names, where each of these instances implements txtorcon.IOnionClient (and therefore also txtorcon.IOnionService). Note that the .auth_token member is secret, private data which you need to give to one client; this information goes in the client’s Tor configuration as HidServAuth onion-address auth-cookie [service-name]. See the Tor manual for more information. Also note that Tor’s API for adding “ephemeral” services doesn’t yet support any type of authentication (however, it may in the future).

Creating Onion Endpoints

The easiest to use API are methods of txtorcon.Tor, which allow you to create IStreamServerEndpoint instances for the various Onion Service types. Both the main endpoint types have several factory-methods to return instances – so you first must decide whether to use an “authenticated” service or not. • if you want anyone with e.g. the URL http://timaq4ygg2iegci7.onion to be able to put it in Tor Browser Bundle and see a Web site, you do not want authentication; • if you want only people with the URL and a secret authentication token to see the Web site, you want basic authentication (these support many more clients than stealth auth);

12 Chapter 1. Documentation txtorcon Documentation, Release 1.0.0

• if you don’t even want anyone to be able to decrypt the descriptor without a unique URL and a secret authenti- cation token, you want stealth authentication (a lot less scalable; for only “a few” clients).

Non-Authenticated Services

For non-authenticated services, you want to create a txtorcon.TCPHiddenServiceEndpoint instance. You can do this via the txtorcon.create_onion_service() factory function or with txtorcon.Tor.create_onion_service(). It’s also possible to use Twisted’s serverFromString API with the onion: prefix. (Thus, any program supporting endpoint strings for configuration can use Tor Onion Services with no code changes). If you don’t want to manage launching or connecting to Tor yourself, you can use one of the three @classmethods on the class, which all return a new endpoint instance: • txtorcon.TCPHiddenSeviceEndpoint.global_tor(): uses a Tor instance launched at most once in this Python process (the underlying txtorcon.Tor instance for this is available via txtorcon.get_global_tor() if you need to make manual configuration adjustments); • txtorcon.TCPHiddenSeviceEndpoint.system_tor(): connects to the control-protocol endpoint you provide (a good choice on Debian would be UNIXClientEndpoint(’/var/run/tor/control’)); • txtorcon.TCPHiddenSeviceEndpoint.private_tor(): causes a fresh, private instance of Tor to be launched for this service alone. This uses a tempdir (honoring $TMP) which is deleted upon reactor shutdown or loss of the control connection. Note that nothing actually “happens” until you call .listen() on the IStreamServerEndpoint at which point Tor will possibly be launched, the Onion Service created, and the descriptor published.

Authenticated Services

To use authenticated services, you want to create a txtorcon.TCPAuthenticatedHiddenServiceEndpoint instance. This provides the very same factory methods as for non-authenticatd instances, but adds arguments for a list of clients (strings) and an authentication method ("basic" or "stealth"). For completeness, the methods to create authenticated endpoints are: • txtorcon.Tor.create_authenticated_onion_service(); • txtorcon.create_authenticated_onion_service(); • txtorcon.TCPAuthenticatedHiddenSeviceEndpoint.global_tor() • txtorcon.TCPAuthenticatedHiddenSeviceEndpoint.system_tor() • txtorcon.TCPAuthenticatedHiddenSeviceEndpoint.private_tor()

Onion Service Configuration

If you just want to “look at” the configuration of existing onion services, they are avaialble via txtorcon.TorConfig and the .HiddenServices attribute. This presents a “flattened” version of any authenticated services, so that each element in the list of .HiddenServices is itself at least a txtorcon.IOnionService (it may also implement other interfaces, but every one will implement IOnionService).

1.3. Programming Guide 13 txtorcon Documentation, Release 1.0.0

You can still set any settable attributes on these objects, and Tor’s configuration for them will be updated when you call txtorcon.TorConfig.save() with an important exception: “ephemeral” services cannot be updated after they’re created. Note that it’s possible for other controllers to create ephemeral services that your controller can’t enumerate.

1.3.8 Custom Circuits txtorcon provides a low-level interface over top of Tor’s circuit-attachment API, which allows you to specify which circuit any new streams use. Often, though, you also want to create custom circuits for streams – and so we also provide a more convenient higher-level API (see “Building Many Circuits”). For one-shot connections, use txtorcon.Circuit.create_client_endpoint() to acquire an IStreamClientEndpoint instance. Calling connect() on this endpoint instance causes the resulting stream to go via the particular txtorcon.Circuit instance. (If the circuit has closed by the time you call connect(), the connection will fail). See web_client_custom_circuit.py. Note that Tor doesn’t currently allow controllers to attach circuits destined for hidden-services (even over an otherwise suitable circuit).

Building a Single Circuit

If your use-case needs just a single circuit, it is probably easiest to call txtorcon.TorState.build_circuit(). This methods takes a list of txtorcon.Router instances, which you can get from the txtorcon.TorState instance by using one of the attributes: • .all_routers • .routers • .routers_by_name or • .routers_by_hash The last three are all hash-tables. For relays that have the Guard flag, you can access the hash-tables .guards (for all of them) or .entry_guards (for just the entry guards configured on this Tor client). If you don’t actually care which relays are used, but simply want a fresh circuit, you can call txtorcon.TorState.build_circuit() without any arguments (or, set routers=None).

Building Many Circuits

If you would like to build many circuits, you’ll want an instance that implements txtorcon.ICircuitBuilder (which is usually simply an instance of txtorcon.CircuitBuilder). Instances of this class can be created by calling one of the factory functions like txtorcon.circuit_builder_fixed_exit(). XXX what about a “config object” idea, e.g. could have keys: • guard_selection: one of entry_only (use one of the current entry guards) or random_guard (use any relay with the Guard flag, selected by XXX). • middle_selection: one of uniform (selected randomly from all relays), weighted (selected randomly, but weighted by consensus weight – basically same way as Tor would select).

14 Chapter 1. Documentation txtorcon Documentation, Release 1.0.0

1.3.9 Attaching Streams to Circuits

Tor allows the controller to decide how to attach new streams to circuits. This doesn’t work for hidden- service bound streams. The lower-level API is to implement an txtorcon.IStreamAttacher and call txtorcon.TorState.set_stream_attacher() on your TorState instance. Often, however, making low-level per-stream decisions isn’t what you want – you just want to create a stream that goes over a particular circuit. For this use-case, you use txtorcon.Circuit().

1.4 Examples

The examples are grouped by functionality and serve as mini-HOWTOs – if you have a use-case that is missing, it may be useful to add an example, so please file a bug. All files are in the examples/ sub-directory and are ready to run, usually with defaults designed to work with Tor Browser Bundle (localhost:9151). XXX maybe honour TOR_CONTROL env-var for just examples?

• Web: clients – web_client.py – web_client_custom_circuit.py • Web: servers (services) – web_onion_service.py – web_onion_service_endpoints.py – web_onion_service_klein.py • Starting Tor – launch_tor.py – launch_tor_endpoint.py • Circuits and Streams – disallow_streams_by_port.py – stream_circuit_logger.py – attach_streams_by_country.py – schedule_bandwidth.py • Configuration – dump_config.py • Events – monitor.py • Miscellaneous – stem_relay_descriptor.py – circuit_failure_rates.py – txtorcon.tac

1.4.1 Web: clients web_client.py

Download the example. Uses twisted.web.client to download a Web page using a twisted.web.client.Agent, via any circuit Tor chooses.

1.4. Examples 15 txtorcon Documentation, Release 1.0.0

# this example shows how to use Twisted's web client with Tor via # txtorcon from __future__ import print_function from twisted.internet.defer import inlineCallbacks from twisted.internet.task import react from twisted.internet.endpoints import TCP4ClientEndpoint from twisted.web.client import readBody import txtorcon from txtorcon.util import default_control_port

@inlineCallbacks def main(reactor): # use port 9051 for system tor instances, or: # ep = UNIXClientEndpoint(reactor, '/var/run/tor/control') # ep = UNIXClientEndpoint(reactor, '/var/run/tor/control') ep= TCP4ClientEndpoint(reactor,'127.0.0.1', default_control_port()) tor= yield txtorcon.connect(reactor, ep) print("Connected to {tor} via localhost:{port}".format( tor=tor, port=default_control_port(), ))

# create a web.Agent that will talk via Tor. If the socks port # given isn't yet configured, this will do so. It may also be # None, which means "the first configured SOCKSPort" # agent = tor.web_agent(u'9999') agent= tor.web_agent() uri=b'http://surely-this-has-not-been-registered-and-is-invalid.com' uri=b'https://www.torproject.org' uri=b'http://timaq4ygg2iegci7.onion/' # txtorcon documentation print("Downloading {}".format(uri)) resp= yield agent.request(b'GET', uri)

print("Response has {} bytes".format(resp.length)) body= yield readBody(resp) print("received body ({} bytes)".format(len(body))) print("{}\n[...]\n{}\n".format(body[:200], body[-200:])) if __name__ =='__main__': react(main) web_client_custom_circuit.py

Download the example. Builds a custom circuit, and then uses twisted.web.client to download a Web page using the circuit created. # this example shows how to use Twisted's web client with Tor via # txtorcon from __future__ import print_function

16 Chapter 1. Documentation txtorcon Documentation, Release 1.0.0

from twisted.internet.defer import inlineCallbacks from twisted.internet.task import react from twisted.internet.endpoints import TCP4ClientEndpoint from twisted.web.client import readBody import txtorcon from txtorcon.util import default_control_port

@inlineCallbacks def main(reactor): # use port 9051 for system tor instances, or: # ep = UNIXClientEndpoint(reactor, '/var/run/tor/control') ep= TCP4ClientEndpoint(reactor,'127.0.0.1', default_control_port()) tor= yield txtorcon.connect(reactor, ep) print("Connected:", tor)

state= yield tor.create_state() socks= tor.config.socks_endpoint(reactor)

# create a custom circuit; in this case we're just letting Tor # decide the path -- but this could be done several other ways # e.g. with txtorcon.CircuitBuilder circ= yield state.build_circuit() print("Building a circuit:", circ)

# at this point, the circuit will be "under way" but may not yet # be in BUILT state -- and hence usable. So, we wait. yield circ.when_built() print("Circuit is ready:", circ)

# create a web.Agent that will use this circuit (or fail) agent= circ.web_agent(reactor, socks)

uri='https://www.torproject.org' print("Downloading {}".format(uri)) resp= yield agent.request('GET', uri)

print("Response has {} bytes".format(resp.length)) body= yield readBody(resp) print("received body ({} bytes)".format(len(body))) print("{}\n[...]\n{}\n".format(body[:200], body[-200:])) react(main)

1.4.2 Web: servers (services) web_onion_service.py

Download the example. Set up a twisted.web.server listening as a onion service. This uses the ADD_ONION API from Tor. If you don’t know what that means, see the spec. If you know you want to keep the private key for your onion service on disk somewhere, see the next example.

1.4. Examples 17 txtorcon Documentation, Release 1.0.0

#!/usr/bin/env python

# This shows how to leverage the endpoints API to get a new hidden # service up and running quickly. You can pass along this API to your # users by accepting endpoint strings as per Twisted recommendations. # # http://twistedmatrix.com/documents/current/core/howto/endpoints.html#maximizing-the-return-on-your-endpoint-investment # # note that only the progress-updates needs the "import txtorcon" -- # you do still need it installed so that Twisted finds the endpoint # parser plugin but code without knowledge of txtorcon can still # launch a Tor instance using it. cool! from __future__ import print_function from twisted.internet import defer, task, endpoints from twisted.web import server, static, resource import txtorcon from txtorcon.util import default_control_port

@defer.inlineCallbacks def main(reactor): tor= yield txtorcon.connect( reactor, endpoints.TCP4ClientEndpoint(reactor,'127.0.0.1', default_control_port()), # endpoints.UNIXClientEndpoint(reactor, "/var/run/tor/control"), ) ep= tor.create_onion_endpoint(80) res= resource.Resource() res.putChild( b'', static.Data("Hello, onion-service world!",'text/html'), )

def on_progress(percent, tag, msg): print('%03d: %s'%(percent, msg)) txtorcon.IProgressProvider(ep).add_progress_listener(on_progress)

port= yield ep.listen(server.Site(res)) print("Site listening: {}".format(port.getHost())) print("Private key:\n{}".format(port.getHost().onion_key)) yield defer.Deferred() # wait forever if __name__ =='__main__': task.react(main) web_onion_service_endpoints.py

Download the example. This uses a server endpoint string via the serverFromString API in Twisted to do “whatever it takes” to set up a new Onion (location-hidden) service. If a Twisted application lets you configure server endpoint strings to listen on, you may get hidden-service support without having to change any code. If, instead, you’re writing Python code and wish to have more control over the endpoint used (and e.g. whether a new

18 Chapter 1. Documentation txtorcon Documentation, Release 1.0.0

Tor instance is launched or not) use one of the following examples. #!/usr/bin/env python

# This shows how to leverage the endpoints API to get a new hidden # service up and running quickly. You can pass along this API to your # users by accepting endpoint strings as per Twisted recommendations. # # http://twistedmatrix.com/documents/current/core/howto/endpoints.html#maximizing-the-return-on-your-endpoint-investment # # note that only the progress-updates needs the "import txtorcon" -- # you do still need it installed so that Twisted finds the endpoint # parser plugin but code without knowledge of txtorcon can still # launch a Tor instance using it. cool! from __future__ import print_function from twisted.internet import defer, task, endpoints from twisted.web import server, resource import txtorcon from txtorcon.util import default_control_port class Simple(resource.Resource): """ A really simple Web site. """ isLeaf= True

def render_GET(self, request): return"Hello, world! I'm a hidden service!"

@defer.inlineCallbacks def main(reactor): # "onion:" is for Tor Onion Services, and the only required # argument is the public port we advertise. You can pass # "controlPort=9051" for example, to connect to a system Tor # (accepts paths, too, e.g. "controlPort=/var/run/tor/control") # ep = endpoints.serverFromString(reactor, "onion:80:controlPort=9151")

# to re-create a previous hidden-service, pass the private key # blob you retrieved earler via port.getHost().onion_key like so: # ep = endpoints.serverFromString(reactor, "onion:80:privateKey=") # Beware that you have to escape the ":" after RSA1024 because of # reasons (the endpoint syntax uses them). For this reason, you # can omit the "RSA1024:" part if you wish. # ep = endpoints.serverFromString(reactor, "onion:80")

# this one should be "nyfnesplt3f5nvn3.onion"

ep= endpoints.serverFromString(reactor,"onion:80:controlPort={port}:privateKey=RSA1024\\:MIICWwIBAAKBgQDml0L1Btxe1QIs88mvKvcgAEd19bUorzMndfXXBbPt2y1lTjm+vGldJRCXb/RArfCb9F2q7IWL4ScuJBiUCqpKVG2aGK8yOxw4c5WKvnLW8MRf5+jAPlR3h7idBdrVGCY/9gXf9JzWfpIhMfFidM4Xq6VpzMvignss6FB6i9zhOwIDAQABAoGAXuWjVamUKabp9UwDFYbOGypiPmZ3Pp4TpErEeNBNAzdvUEDIPPnXNtEZKemWEMREwDnqDny2XSG0+SU7xDk7aQGTFxipo+NAl18QMW2XcBjWrIG5P0L9E+j58k5Nq6EEaMQ8G8X3hsnX7EwRqnJYOwUWUQ4emi6TvNScSMS251kCQQD6KJXltkSfwU3d5hOh37x3pOp4ZcpI6eKwwfgqP+1pVfOwjvXfLqLgLRf9+NtmG+cU5HRDwmf9rbJNCOE++11HAkEA6/mz/L+54NRk9tPN4vYfn969v7fz9CQndQUsTTrtArqtjg7baKts3ndagj+/itJfY6qV/OonN9XdntQXTWWGbQJAY244TmrJEfqieZ2WlhO49JFPRPWolpyoJvuiKSDpu6GXT8ky/zepM5OY4rDEe+yBR/OaJsihztn4cdgit4bvxwJAFsnZiOoXEFBSo8eWlXmBWlYPawlfxM8NBG8IdTjglKfkhNiIddZAQEe0dOmlHMnuLljV/UO7n9fGfEUtLutEDQJAC3c9gSe2of41TaZhQ+aHzQ8E9cs7fg3gXXUgWlocQK6fYq+tC0CF7dDmydShF8vI8oEcgJGhgtUAXQDwH9eD0A==".format(port=default_control_port()))

def on_progress(percent, tag, msg): print('%03d: %s'%(percent, msg)) txtorcon.IProgressProvider(ep).add_progress_listener(on_progress) print("Note: descriptor upload can take several minutes")

port= yield ep.listen(server.Site(Simple()))

1.4. Examples 19 txtorcon Documentation, Release 1.0.0

print("Site listening: {}".format(port.getHost())) print("Private key:\n{}".format(port.getHost().onion_key)) yield defer.Deferred() # wait forever task.react(main) web_onion_service_klein.py

Download the example. Set up a ‘twisted.web.server <>‘_ listening as a onion service. This uses the ADD_ONION API from Tor. If you don’t know what that means, see the spec. If you know you want to keep the private key for your onion service on disk somewhere, see the next example. #!/usr/bin/env python

# This shows how to leverage the endpoints API to get a new hidden # service up and running quickly. You can pass along this API to your # users by accepting endpoint strings as per Twisted recommendations. # # http://twistedmatrix.com/documents/current/core/howto/endpoints.html#maximizing-the-return-on-your-endpoint-investment # # note that only the progress-updates needs the "import txtorcon" -- # you do still need it installed so that Twisted finds the endpoint # parser plugin but code without knowledge of txtorcon can still # launch a Tor instance using it. cool! from __future__ import print_function import sys from twisted.internet import defer, task, endpoints from twisted.web import server import txtorcon from txtorcon.util import default_control_port try: from klein import Klein except ImportError: print("To use this example, you must install Klein:") print(" pip install klein") sys.exit(1) app= Klein()

@app.route('/') def home(request): return'Hello from Klein, Onion World'

@defer.inlineCallbacks def main(reactor): ep= endpoints.serverFromString( reactor,

20 Chapter 1. Documentation txtorcon Documentation, Release 1.0.0

"onion:80:controlPort={port}".format(port=default_control_port()) )

def on_progress(percent, tag, msg): print('%03d: %s'%(percent, msg)) txtorcon.IProgressProvider(ep).add_progress_listener(on_progress) print("Note: descriptor upload can take several minutes")

port= yield ep.listen(server.Site(app.resource())) print("Site listening: {}".format(port.getHost())) print("Private key:\n{}".format(port.getHost().onion_key)) print("\nVisit using Tor Browser: http://{}\n".format(port.getHost().onion_uri)) yield defer.Deferred() # wait forever if __name__ =='__main__': task.react(main)

1.4.3 Starting Tor launch_tor.py

Download the example. Launch a new Tor instance. This takes care of setting Tor’s notion ownership so that when the control connection goes away the running Tor exits. from __future__ import print_function

""" Launch a private Tor instance. """ import sys import txtorcon from twisted.web.client import readBody from twisted.internet.task import react from twisted.internet.defer import inlineCallbacks

@inlineCallbacks def main(reactor): # note that you can pass a few options as kwargs # (e.g. data_directory=, or socks_port= ). For other torrc # changes, see below. tor= yield txtorcon.launch( reactor, data_directory="./tordata", stdout=sys.stdout, socks_port='unix:/tmp/tor2/socks', ) # tor = yield txtorcon.connect( # reactor, # clientFromString(reactor, "unix:/var/run/tor/control"), #) print("Connected to Tor version'{}'".format(tor.protocol.version))

state= yield tor.create_state()

1.4. Examples 21 txtorcon Documentation, Release 1.0.0

# or state = yield txtorcon.TorState.from_protocol(tor.protocol)

print("This Tor has PID {}".format(state.tor_pid)) print("This Tor has the following {} Circuits:".format(len(state.circuits))) forc in state.circuits.values(): print("{}".format(c))

agent= tor.web_agent(u'unix:/tmp/tor2/socks') uri='https://www.torproject.org' print("Downloading {}".format(uri)) resp= yield agent.request('GET', uri) print("Response has {} bytes".format(resp.length)) body= yield readBody(resp) print("received body ({} bytes)".format(len(body))) print("{}\n[...]\n{}\n".format(body[:200], body[-200:])) if __name__ =='__main__': react(main) launch_tor_endpoint.py

Download the example. Using the txtorcon.TCP4HiddenServiceEndpoint class to start up a Tor with a hidden service pointed to an IStreamServerEndpoint. from __future__ import print_function

# Here we set up a Twisted Web server and then launch our own tor with # a configured hidden service directed at the Web server we set # up. This uses serverFromString to translate the "onion" endpoint # descriptor into a TCPHiddenServiceEndpoint object... from twisted.web import server, resource from twisted.internet.defer import inlineCallbacks from twisted.internet.task import react, deferLater from twisted.internet.endpoints import serverFromString import txtorcon class Simple(resource.Resource): """ A really simple Web site. """ isLeaf= True

def render_GET(self, request): return"Hello, world! I'm a hidden service!"

@inlineCallbacks def main(reactor): # several ways to proceed here and what they mean: # # "onion:80": # launch a new Tor instance, configure a hidden service on some # port and pubish descriptor for port 80

22 Chapter 1. Documentation txtorcon Documentation, Release 1.0.0

# # "onion:80:controlPort=9051:localPort=8080:socksPort=9089:hiddenServiceDir=/home/human/src/txtorcon/hidserv": # connect to existing Tor via control-port 9051, configure a hidden # service listening locally on 8080, publish a descriptor for port # 80 and use an explicit hiddenServiceDir (where "hostname" and # "private_key" files are put by Tor). We set SOCKS port # explicitly, too. # # "onion:80:localPort=8080:socksPort=9089:hiddenServiceDir=/home/human/src/txtorcon/hidserv": # all the same as above, except we launch a new Tor (because no # "controlPort=9051")

ep="onion:80:controlPort=9051:localPort=8080:socksPort=9089:hiddenServiceDir=/home/human/src/txtorcon/hidserv" ep="onion:80:localPort=8080:socksPort=9089:hiddenServiceDir=/home/human/src/txtorcon/hidserv" ep="onion:80" hs_endpoint= serverFromString(reactor, ep)

def progress(percent, tag, message): bar= int(percent/ 10) print("[{}{}] {}".format("#" * bar,"." * (10- bar), message)) txtorcon.IProgressProvider(hs_endpoint).add_progress_listener(progress)

# create our Web server and listen on the endpoint; this does the # actual launching of (or connecting to) tor. site= server.Site(Simple()) port= yield hs_endpoint.listen(site) hs= port.onion_service

# "port" is an IAddress implementor, in this case TorOnionAddress # so you can get most useful information from it -- but you can # also access .onion_service (see below) print( "I have set up a hidden service, advertised at:\n" "http://{host}:{port}\n" "locally listening on {local_address}\n" "Will stop in 60 seconds...".format( host=port.getHost().onion_uri, # or hs.hostname port=port.public_port, # port.local_address will be a twisted.internet.tcp.Port # or a twisted.internet.unix.Port -- both have .getHost() local_address=port.local_address.getHost(), ) )

# if you prefer, hs (port.onion_service) is an instance providing # IOnionService (there's no way to do authenticated services via # endpoints yet, but if there was then this would implement # IOnionClients instead) print("private key:\n{}".format(hs.private_key))

def sleep(s): return deferLater(reactor,s, lambda: None)

yield sleep(50) fori in range(10): print("Stopping in {}...".format(10-i)) yield sleep(1)

1.4. Examples 23 txtorcon Documentation, Release 1.0.0

react(main)

1.4.4 Circuits and Streams disallow_streams_by_port.py

Download the example. An example using IStreamAttacher which is very simple and does just what it sounds like: never attaches Streams exiting to a port in the “disallowed” list (it also explicitly closes them). Note that Tor already has this feature; this is just to illustrate how to use IStreamAttacher and that you may close streams. XXX keep this one? from __future__ import print_function # # This uses a very simple custom txtorcon.IStreamAttacher to disallow # certain streams based solely on their port; by default it closes # all streams on port 80 or 25 without ever attaching them to a # circuit. # # For a more complex IStreamAttacher example, see # attach_streams_by_country.py # from twisted.python import log from twisted.internet.task import react from twisted.internet.defer import inlineCallbacks, Deferred from twisted.internet.endpoints import clientFromString from .interface import implementer import txtorcon

@implementer(txtorcon.IStreamAttacher) class PortFilterAttacher:

def __init__(self, state): self.state= state self.disallow_ports=[80, 25] print( "Disallowing all streams to ports: {ports}".format( ports=",".join(map(str, self.disallow_ports)), ) )

def attach_stream(self, stream, circuits): """ IStreamAttacher API """

def stream_closed(x): print("Stream closed:",x)

if stream.target_port in self.disallow_ports: print( "Disallowing {stream} to port {stream.target_port}".format( stream=stream,

24 Chapter 1. Documentation txtorcon Documentation, Release 1.0.0

) ) d= self.state.close_stream(stream) d.addCallback(stream_closed) d.addErrback(log.err) return txtorcon.TorState.DO_NOT_ATTACH

# Ask Tor to assign stream to a circuit by itself return None

@inlineCallbacks def main(reactor): control_ep= clientFromString(reactor,"tcp:localhost:9051") tor= yield txtorcon.connect(reactor, control_ep) print("Connected to a Tor version={version}".format( version=tor.protocol.version, )) state= yield tor.create_state() yield state.add_attacher(PortFilterAttacher(state), reactor)

print("Existing streams:") fors in state.streams.values(): print("",s) yield Deferred() react(main) stream_circuit_logger.py

Download the example. For listening to changes in the Circuit and State objects, this example is the easiest to understand as it just prints out (some of) the events that happen. Run this, then visit some Web sites via Tor to see what’s going on. #!/usr/bin/env python

# This uses an IStreamListener and an ICircuitListener to log all # built circuits and all streams that succeed. import sys from twisted.python import log from twisted.internet import reactor import txtorcon def logCircuit(circuit): path='->'.join(map(lambdax: str(x.location.countrycode), circuit.path)) log.msg('Circuit %d(%s) is %s for purpose"%s"'% (circuit.id, path, circuit.state, circuit.purpose)) def logStream(stream, state): circ='' if stream.circuit: path='->'.join(map(lambdax:x.location.countrycode, stream.circuit.path)) circ=' via circuit %d(%s)'%(stream.circuit.id, path)

1.4. Examples 25 txtorcon Documentation, Release 1.0.0

proc= txtorcon.util.process_from_address( stream.source_addr, stream.source_port, state ) if proc: proc=' from process"%s"'%(proc,)

elif stream.source_addr =='(Tor_internal)': proc=' for Tor internal use'

else: proc=' from remote"%s:%s"'%(str(stream.source_addr), str(stream.source_port)) log.msg('Stream %d to %s:%d attached%s%s'% (stream.id, stream.target_host, stream.target_port, circ, proc)) class StreamCircuitLogger(txtorcon.StreamListenerMixin, txtorcon.CircuitListenerMixin):

def __init__(self, state): self.state= state

def stream_attach(self, stream, circuit): logStream(stream, self.state)

def stream_failed(self, stream, reason='', remote_reason='', **kw): print'Stream %d failed because"%s"'%(stream.id, remote_reason)

def circuit_built(self, circuit): logCircuit(circuit)

def circuit_failed(self, circuit, **kw): log.msg('Circuit %d failed"%s"'%(circuit.id, kw['REASON'])) def setup(state): log.msg('Connected to a Tor version %s'% state.protocol.version)

listener= StreamCircuitLogger(state) state.add_circuit_listener(listener) state.add_stream_listener(listener)

state.protocol.add_event_listener('STATUS_GENERAL', log.msg) state.protocol.add_event_listener('STATUS_SERVER', log.msg) state.protocol.add_event_listener('STATUS_CLIENT', log.msg)

log.msg('Existing state when we connected:') fors in state.streams.values(): logStream(s, state)

log.msg('Existing circuits:') forc in state.circuits.values(): logCircuit(c) def setup_failed(arg):

26 Chapter 1. Documentation txtorcon Documentation, Release 1.0.0

print"SETUP FAILED", arg log.err(arg) reactor.stop() log.startLogging(sys.stdout) d= txtorcon.build_local_tor_connection(reactor) d.addCallback(setup).addErrback(setup_failed) reactor.run() attach_streams_by_country.py

Download the example. This is one of the more complicated examples. It uses a custom Stream attacher (via IStreamAttacher) to only attach Streams to a Circuit with an exit node in the same country as the server to which the Stream is going (as determined by GeoIP). Caveat: the DNS lookups go via a Tor-assigned stream, so for sites which use DNS trickery to get you to a “close” server, this won’t be as interesting. For bonus points, if there is no Circuit exiting in the correct country, one is created before the Stream is attached. schedule_bandwidth.py

Download the example. This is pretty similar to a feature Tor already has and is basically useless as-is since what it does is toggle the amount of relay bandwidth you’re willing to carry from 0 to 20KiB/s every 20 minutes. A slightly-more-entertaining way to illustate config changes. (This is useless because your relay takes at least an hour to appear in the consensus). #!/usr/bin/env python

# Here, we do something possible-useful and schedule changes to the # "BandWidthRate" and optionally "BandWidthBurst" settings in Tor. import datetime from twisted.internet import reactor from twisted.internet.interfaces import IReactorTime from txtorcon import build_local_tor_connection, TorConfig class BandwidthUpdater:

def __init__(self, config, scheduler): self.bandwidth=0 self.config= config self.scheduler= IReactorTime(scheduler) self.generator= self.next_update()

def next_update(self): """ Generator that gives out the next time to do a bandwidth update, as well as what the new bandwidth value should be. Here, we toggle the bandwidth every 20 minutes. """

while True: if self.bandwidth: self.bandwidth=0

1.4. Examples 27 txtorcon Documentation, Release 1.0.0

self.burst=0 else: self.bandwidth= 20 * 1024 * 1024 self.burst= self.bandwidth yield(datetime.datetime.now()+ datetime.timedelta(minutes=20), self.bandwidth, self.burst)

def do_update(self): x= self.generator.next() future=x[0] self.new_bandwidth=x[1] self.new_burst=x[2]

tm=(future- datetime.datetime.now()).seconds self.scheduler.callLater(tm, self.really_update) print"waiting", tm,"seconds to adjust bandwidth"

def really_update(self): print"setting bandwidth + burst to", self.new_bandwidth, self.new_burst self.config.set_config('BandWidthBurst', self.new_burst, 'BandWidthRate', self.new_bandwidth) self.doUpdate() def setup_complete(conf): print"Connected." bwup= BandwidthUpdater(conf, reactor) bwup.do_update() def setup_failed(arg): print"SETUP FAILED", arg reactor.stop() def bootstrap(proto): config= TorConfig(proto) config.post_bootstrap.addCallback(setup_complete).addErrback(setup_failed) print"Connection is live, bootstrapping config..." d= build_local_tor_connection(reactor, build_state=False, wait_for_proto=False) d.addCallback(bootstrap).addErrback(setup_failed) reactor.run()

1.4.5 Configuration dump_config.py

Download the example. Very simple read-only use of txtorcon.TorConfig

28 Chapter 1. Documentation txtorcon Documentation, Release 1.0.0

1.4.6 Events monitor.py

Download the example. Use a plain txtorcon.TorControlProtocol instance to listen for some simple events – in this case marginally useful, as it listens for logging at level INFO, NOTICE, WARN and ERR. #!/usr/bin/env python

# Just listens for a few EVENTs from Tor (INFO NOTICE WARN ERR) and # prints out the contents, so functions like a log monitor. from twisted.internet import task, defer from twisted.internet.endpoints import UNIXEndpoint import txtorcon

@defer.inlineCallbacks def main(reactor): ep= UNIXEndpoint(reactor,'/var/run/tor/control') tor= yield txtorcon.connect(reactor, ep)

def log(msg): print msg print"Connected to a Tor version", tor.protocol.version for event in['INFO','NOTICE','WARN','ERR']: tor.protocol.add_event_listener(event, log) is_current= yield tor.protocol.get_info('status/version/current') version= yield tor.protocol.get_info('version') print("Version'{}', is_current={}".format(version, is_current)) task.react(main)

1.4.7 Miscellaneous stem_relay_descriptor.py

Download the example. Get information about a relay descriptor with the help of Stem’s Relay Descriptor class. We need to specify the nickname or the fingerprint to get back the details. #!/usr/bin/env python

# This shows how to get the detailed information about a # relay descriptor and parse it into Stem's Relay Descriptor # class. More about the class can be read from # # https://stem.torproject.org/api/descriptor/server_descriptor.html#stem.descriptor.server_descriptor.RelayDescriptor # # We need to pass the nickname or the fingerprint of the onion # router for which we need the the descriptor information, from twisted.internet.task import react

1.4. Examples 29 txtorcon Documentation, Release 1.0.0

from twisted.internet.defer import inlineCallbacks import txtorcon

@inlineCallbacks def main(reactor): proto= yield txtorcon.build_local_tor_connection(reactor, build_state=False)

or_nickname="moria1" print"Trying to get decriptor information about", or_nickname # If the fingerprint is used in place of nickname then, desc/id/ # should be used. descriptor_info= yield proto.get_info('desc/name/'+ or_nickname)

descriptor_info= descriptor_info['desc/name/'+ or_nickname] try: from stem.descriptor.server_descriptor import RelayDescriptor relay_info= RelayDescriptor(descriptor_info) print"The relay's fingerprint is:", relay_info.fingerprint print"Time in UTC when the descriptor was made:", relay_info.published except ImportError ase: print"Error:",e if __name__ =='__main__': react(main) circuit_failure_rates.py

Download the example. txtorcon.tac

Download the example Create your own twisted Service for deploying using twistd. import functools from os.path import dirname import sys from tempfile import mkdtemp import txtorcon from twisted.application import service, internet from twisted.internet import reactor from twisted.internet.endpoints import TCP4ClientEndpoint from twisted.python import log from twisted.web import static, server from zope.interface import implements class TorService(service.Service): implements(service.IService) directory= dirname(__file__) port= 8080

30 Chapter 1. Documentation txtorcon Documentation, Release 1.0.0

def __init__(self): self.torfactory= txtorcon.TorProtocolFactory() self.connection= TCP4ClientEndpoint(reactor,'localhost', 9052) self.resource= server.Site(static.File(self.directory))

def startService(self): service.Service.startService(self)

reactor.listenTCP(self.port, self.resource) self._bootstrap().addCallback(self._complete)

def _bootstrap(self): self.config= txtorcon.TorConfig() self.config.HiddenServices=[ txtorcon.HiddenService(self.config, mkdtemp(), ['%d 127.0.0.1:%d'%(80, self.port)]) ] self.config.save() return txtorcon.launch_tor(self.config, reactor, progress_updates=self._updates, tor_binary='tor')

def _updates(self, prog, tag, summary): log.msg('%d%%: %s'%(prog, summary))

def _complete(self, proto): log.msg(self.config.HiddenServices[0].hostname) application= service.Application("Txtorcon Application") torservice= TorService() torservice.setServiceParent(application)

1.5 Contributions

You can help contribute to txtorcon by reporting bugs, sending success stories, by adding a feature or fixing a bug. Even asking that “silly” question helps me with documentation writing.

1.5.1 Contact Information

Discussing txtorcon is welcome in the following places: • IRC: #tor-dev on OFTC (please prefix lines with meejah: to get my attention, and be patient for replies). • email: preferably on the tor-dev list, or see meejah.ca for other ways to contact me. • bugs: use txtorcon’s issues tracker on GitHub. • @txtorcon on Twitter (announcements only)

1.5.2 Public Key

You can download my key from a keyserver (0xC2602803128069A7) or see meejah.asc in the repository. The fingerprint is 9D5A 2BD5 688E CB88 9DEB CD3F C260 2803 1280 69A7.

1.5. Contributions 31 txtorcon Documentation, Release 1.0.0

Also available at https://meejah.ca/meejah.asc. For convenience: curl https://meejah.ca/meejah.asc | gpg --import

1.5.3 Pull Requests

Yes, please! If you have a new feature or a bug fix, the very best way is to submit a pull-request on GitHub. Since we have 100% coverage, all new lines of code should at least be covered by unit-tests. You can also include a note about the change in docs/releases.rst if you like (or I can make one up after the merge). I prefer if you rebase/squash commits into logical chunks. Discussion of any implementation details can simply occur on the pull-request itself. Force-pushing to the same branch/PR is fine by me if you want to re-order commits etcetera (but, it’s also fine if you just want to push new “fix issues” commits instead). Some example pull-requests: • good discussion + more commits: PR #150; • a simple one that was “ready-to-go”: PR #51.

1.5.4 Making a Release

Mostly a note-to-self, but here is my release checklist.

Release Checklist

• ensure local copy is on master, up-to-date: – git checkout master – git pull • double-check version updated, sadly in a few places: – Makefile – txtorcon/_metadata.py • run all tests, on all configurations – “tox” • “make pep8” should run cleanly (ideally) • update docs/releases.rst to reflect upcoming reality – blindly make links to the signatures – update heading, date • on both signing-machine and build-machine shells: – export VERSION=0.18.0 • (if on signing machine) “make dist” and “make dist-sigs” – creates: dist/txtorcon-${VERSION}.tar.gz.asc dist/txtorcon-${VERSION}-py2-none-any.whl.asc – add the signatures to “signatues/” – add ALL FOUR files to dist/ (OR fix twine commands)

32 Chapter 1. Documentation txtorcon Documentation, Release 1.0.0

• (if not on signing machine) do “make dist” * scp dist/txtorcon-${VERSION}.tar.gz dist/txtorcon-${VERSION}- py2-none-any.whl signingmachine: * sign both, with .asc detached signatures – gpg –no-version –detach-sign –armor –local-user [email protected] txtorcon-${VERSION}-py2-none- any.whl – gpg –no-version –detach-sign –armor –local-user [email protected] txtorcon-${VERSION}.tar.gz – copy signatures back to build machine, in dist/ – double-check that they validate

* gpg –verify dist/txtorcon-${VERSION}-py2-none-any.whl.asc * gpg –verify dist/txtorcon-${VERSION}.tar.gz.asc • generate sha256sum for each: sha256sum dist/txtorcon-${VERSION}.tar.gz dist/txtorcon-${VERSION}- py2-none-any.whl • copy signature files to /signatures and commit them along with the above changes for versions, etc. • draft email to tor-dev (and probably twisted-python): – example: https://lists.torproject.org/pipermail/tor-dev/2014-January/006111.html – example: https://lists.torproject.org/pipermail/tor-dev/2014-June/007006.html – copy-paste release notes, un-rst-format them – include above sha256sums – clear-sign the announcement – gpg –armor –clearsign -u [email protected] release-announce-${VERSION} – Example boilerplate: I’m [adjective] to announce txtorcon 0.10.0. This adds several amazing features, including levitation. Full list of improvements:

* take from releases.rst * ...but un-rST them You can download the release from PyPI or GitHub (or of course “pip install txtorcon”): https://pypi.python.org/pypi/txtorcon/0.10.0 https://github.com/meejah/txtorcon/releases/tag/v0.10.0 Releases are also available from the hidden service: http://timaq4ygg2iegci7.onion/txtorcon-0.12.0.tar.gz http://timaq4ygg2iegci7.onion/txtorcon- 0.12.0.tar.gz.asc You can verify the sha256sum of both by running the following 4 lines in a shell wherever you have the files downloaded: cat <

1.5. Contributions 33 txtorcon Documentation, Release 1.0.0

– git pull • create signed tag – git tag -s -u [email protected] -F release-announce-${VERSION} v${VERSION} • copy dist/* files + signatures to hidden-service machine • copy them to the HTML build directory! (docs/_build/html/) • git pull and build docs there – FIXME: why aren’t all the dist files copied as part of doc build (only .tar.gz) • download both distributions + signatures from hidden-service – verify sigs – verify sha256sums versus announcement text – verify tag (git tag –verify v${VERSION}) on machine other than signing-machine – run: ./scripts/download-release-onion.sh • upload release – to PyPI: “make release” (which uses twine so this isn’t the same step as “sign the release”)

* make sure BOTH the .tar.gz and .tar.gz.asc (ditto for .whl) are in the dist/ directory first!!) * ls dist/txtorcon-${VERSION}* * note this depends on a ~/.pypirc file with [server-login] section containing “username:” and “password:” – git push origin master – git push origin v${VERSION} – to github: use web-upload interface to upload the 4 files (both dists, both signature) • make announcement – post to tor-dev@ the clear-signed release announcement – post to twisted-python@ the clear-signed release announcement – tweet as @txtorcon – tell #tor-dev??

34 Chapter 1. Documentation CHAPTER 2

Official Releases:

All official releases are tagged in Git, and signed by my key. All official releases on PyPI have a corresponding GPG signature of the build. Please be aware that pip does not check GPG signatures by default; please see this ticket if you care. The most reliable way to verify you got what I intended is to clone the Git repository, git checkout a tag and verify its signature. The second-best would be to download a release + tag from PyPI and verify that.

2.1 Releases

There isn’t a “release schedule” in any sense. If there is something in master your project depends upon, let me know and I’ll do a release.

2.1.1 upcoming: 1.0.0 branch release-1.x will become release 1.0.0 in the future. Some APIs will break, but wherever possible backwards- compatiblity with 0.x will be kept. Rendered docs are available already, at txtorcon.readthedocs. • full Python3 support • re-work (re-do?) most documentation, adding a Programming Guide and extensive re-organization • a bunch of API fixups, improvements and better-ments. • new high-level API txtorcon.Tor abstracts “a single running Tor process” (that we either connected to or launched). • twisted.web.client support, via txtorcon.Tor.web_agent() and txtorcon.Circuit.web_agent() • txtorcon.Circuit.stream_via(): easily attach client-type streams to specific Circuits • txtorcon.Tor.stream_via(): create client-type streams that go via Tor • SOCKS5: built-in support, including for Tor custom extensions (see txtorcon.Tor.resolve() and txtorcon.Tor.resolve_ptr()) • SOCKS5: see also txtorcon.TorConfig.socks_endpoint() • txtorcon.TorControlProtocol.get_info() now returns a list of values, in the same order as the keys. (Used to return a dict) (XXX is this true yet?) • many things which “should” have been “private” all along grew an underscore prefix.

35 txtorcon Documentation, Release 1.0.0

• txtorcon.Router no longer has a .location attribute. Instead, use txtorcon.Router.get_location() (which returns a Deferred) • dropped GeoIP dependency entirely (no GeoIP support).

2.1.2 unreleased

git master will likely become v0.19.0 • Full Python3 support • Drop txsocksx and use a custom implementation (this also implements the custom Tor SOCKS5 methods RE- SOLVE and RESOLVE_PTR • Drop support for older Twisted releases (12, 13 and 14 are no longer supported).

2.1.3 v0.18.0

January 11, 2017 • txtorcon-0.18.0.tar.gz(PyPI( local-sig or github-sig)(source) • issue 200: better feedback if the cookie data can’t be read

2.1.4 v0.17.0

October 4, 2016 • txtorcon-0.17.0.tar.gz(PyPI( local-sig or github-sig)(source) • issue 187: fix unix-socket control endpoints • sometimes mapping streams to hostnames wasn’t working properly • backwards-compatibility API for socks_hostname was incorrectly named

2.1.5 v0.16.1

August 31, 2016 • txtorcon-0.16.1.tar.gz(PyPI( local-sig or github-sig)(source) • issue 172: give TorProcessProtocol a .quit method • issue 181: enable SOCKS5-over-unix-sockets for TorClientEndpoint (thanks to david415

2.1.6 v0.16.0

• there wasn’t one, because reasons.

2.1.7 v0.15.1

• txtorcon-0.15.1.tar.gz(PyPI( local-sig or github-sig)(source) • fix issue 179 with Circuit.age.

36 Chapter 2. Official Releases: txtorcon Documentation, Release 1.0.0

2.1.8 v0.15.0

July 26, 2016 • txtorcon-0.15.0.tar.gz(PyPI( local-sig or github-sig)(source) • added support for NULL control-port-authentication which is often appropriate when used with a UNIX domain socket • switched to ipaddress instead of Google’s ipaddr; the API should be the same from a user perspective but packagers and tutorials will want to change their instructions slightly (pip install ipaddress or apt-get install python-ipaddress are the new ways). • support the new ADD_ONION and DEL_ONION “ephemeral hidden services” commands in TorConfig • a first stealth-authentication implementation (for “normal” hidden services, not ephemeral) • bug-fix from david415 to raise ConnectionRefusedError instead of StopIteration when running out of SOCKS ports. • new feature from david415 adding a build_timeout_circuit method which provides a Deferred that callbacks only when the circuit is completely built and errbacks if the provided timeout expires. This is useful because txtorcon.TorState.build_circuit() callbacks as soon as a Circuit instance can be pro- vided (and then you’d use txtorcon.Circuit.when_built() to find out when it’s done building). • new feature from coffeemakr falling back to password authentication if cookie authentication isn’t available (or fails, e.g. because the file isn’t readable). • both TorState and TorConfig now have a .from_protocol class-method. • spec-compliant string-un-escaping from coffeemakr • a proposed new API: txtorcon.connect() • fix issue 176

2.1.9 v0.14.1

October 25, 2015 • subtle bug with .is_built on Circuit; changing the API (but with backwards-compatibility until 0.15.0 at least)

2.1.10 v0.14.0

September 26, 2015 • txtorcon-0.14.0.tar.gz(PyPI( local-sig or github-sig)(source) • txtorcon.interface.IStreamAttacher handling was missing None and DO_NOT_ATTACH cases if a Deferred was returned. • add .is_built Deferred to txtorcon.Circuit that gets ‘callback()‘d when the circuit becomes BUILT • david415 ported his tor: endpoint parser so now both client and server endpoints are supported. This means any Twisted program using endpoints can use Tor as a client. For example, to connect to txtorcon’s Web site: ep = clientFromString("tor:timaq4ygg2iegci7.onion:80"). (In the future, I’d like to automatically launch Tor if required, too). • Python3 fixes from isis (note: needs Twisted 15.4.0+)

2.1. Releases 37 txtorcon Documentation, Release 1.0.0

2.1.11 v0.13.0

May 10, 2015 • txtorcon-0.13.0.tar.gz(PyPI( local-sig or github-sig)(source) • support basic and stealth hidden service authorization, and parse client_keys files. • 2x speedup for TorState parsing (mostly by lazy-parsing timestamps) • can now parse ~75000 microdescriptors/second per core of 3.4GHz Xeon E3 • launch_tor now doesn’t use a temporary torrc (command-line options instead) • tons of pep8 cleanups • several improvements to hidden-service configuration from sambuddhabasu1. • populated valid signals from GETINFO signals/names from sambuddhabasu1.

2.1.12 v0.12.0

February 3, 2015 • txtorcon-0.12.0.tar.gz(PyPI( local-sig or github-sig)(source) • doc, code and import cleanups from Kali Kaneko • HiddenServiceDirGroupReadable support • Issue #80: honour ControlPort 0 in incoming TorConfig instance. The caller owns both pieces: you have to figure out when it’s bootstraped, and are responsible for killing it off. • Issue #88: clarify documentation and fix appending to some config lists • If GeoIP data isn’t loaded in Tor, it sends protocol errors; if txtorcon also hasn’t got GeoIP data, the queries for country-code fail; this error is now ignored. • 100% unit-test coverage! (line coverage) • PyPy support (well, at least all tests pass) • TCP4HiddenServiceEndpoint now waits for descriptor upload before the listen() call does its callback (this means when using onion: endpoint strings, or any of the endpoints APIs your hidden service is 100% ready for action when you receive the callback) • TimeIntervalCommaList from Tor config supported • TorControlProtocol now has a .all_routers member (a set() of all Routers) • documentation fix from sammyshj

2.1.13 v0.11.0

August 16, 2014 • September 6, 2015. bugfix release: txtorcon-0.11.1.tar.gz(PyPI( local-sig or github-sig)(source) • fixed Debian bug 797261 causing 3 tests to fail • txtorcon-0.11.0.tar.gz(PyPI( local-sig or github-sig)(source) • More control for launch_tor: access stdout, stderr in real-time and control whether we kill Tor on and stderr output. See issue #79.

38 Chapter 2. Official Releases: txtorcon Documentation, Release 1.0.0

• Warning about build_circuit being called without a guard first is now optional (default is still warn) (from arlolra) • available_tcp_port() now in util (from arlolra) • TorState now has a .routers_by_hash member (from arlolra)

2.1.14 v0.10.1

July 20, 2014 • txtorcon-0.10.1.tar.gz(PyPI( local-sig or github-sig)(source) • fix bug incorrectly issuing RuntimeError in brief window of time on event-listeners • issue #78: Add tox tests and fix for Twisted 12.0.0 (and prior), as this is what Debian squeeze ships • issue #77: properly expand relative and tilde paths for hiddenServiceDir via endpoints

2.1.15 v0.10.0

June 15, 2014 • txtorcon-0.10.0.tar.gz(PyPI( local-sig or github-sig)(source) • In collaboration with David Stainton after a pull-request, we have endpoint parser plugins for Twisted! This means code like serverFromString("onion:80").listen(...) is enough to start a service. • The above also means that any endpoint-using Twisted program can immediately offer its TCP services via Hidden Service with no code changes. For example, using Twisted Web to serve a WSGI web application would be simply: twistd web --port onion:80 --wsgi web.app • switch to a slightly-modified Alabaster Sphinx theme • added howtos to documentation

2.1.16 v0.9.2

April 23, 2014 • txtorcon-0.9.2.tar.gz( local-sig or github-sig)(source) • add on_disconnect callback for TorControlProtocol (no more monkey-patching Protocol API) • add age() method to Circuit • add time_created property to Circuit • don’t incorrectly listen for NEWDESC events in TorState • add .flags dict to track flags in Circuit, Stream • build_circuit() can now take hex IDs (as well as Router instances) • add unique_name property to Router (returns the hex id, unless Named then return name) • add location property to Router • TorState.close_circuit now takes either a Circuit ID or Circuit instance • TorState.close_stream now takes either a Stream ID or Stream instance • support both GeoIP API versions

2.1. Releases 39 txtorcon Documentation, Release 1.0.0

• more test-coverage • small patch from enriquefynn improving tor binary locating • strip OK lines in TorControlProtocol (see issue #8) • use TERM not KILL when Tor launch times out (see issue #68) from hellais

2.1.17 v0.9.1

January 20, 2014 • txtorcon-0.9.1.tar.gz( local-sig or github-sig)(source) • put test/ directory at the top level • using “coverage” tool instead of custom script • using coveralls.io and travis-ci for test coverage and continuous integration • issue #56: added Circuit.close() and Stream.close() starting from aagbsn’s patch • parsing issues with multi-line keyword discovered and resolved • preserve router nicks from long-names if consensus lacks an entry (e.g. bridges) • using Twine for releases • Wheel release now also available • issue #57: “python setup.py develop” now supported • issue #59: if tor_launch() times out, Tor is properly killed (starting with pull-request from Ryman) • experimental docker.io-based tests (for HS listening, and tor_launch() timeouts) • issue #55: pubkey link on readthedocs • issue #63 • clean up GeoIP handling, and support pygeoip both pre and post 0.3 • slightly improve unit-test coverage (now at 97%, 61 lines of 2031 missing) • added a Walkthrough to the documentation

2.1.18 v0.8.2

November 22, 2013 • txtorcon-0.8.2.tar.gz( local-sig or github-sig)(source) • ensure hidden service server-side endpoints listen only on 127.0.0.1

2.1.19 v0.8.1

May 13, 2013 • txtorcon-0.8.1.tar.gz( local-sign or github-sig)(source) • fixed improper import in setup.py preventing 0.8.0 from installing • signatures with proper subkey this time

40 Chapter 2. Official Releases: txtorcon Documentation, Release 1.0.0

• Proper file-flushing in tests and PyPy fixes from Lukas Lueg • docs build issue from isis

2.1.20 v0.8.0

April 11, 2013 (actually uploaded May 11) • Please use 0.8.1; this won’t install due to import problem in setup.py (unless you have pypissh). • following semantic versioning; • slight API change ICircuitListener.circuit_failed(), circuit_closed() and IStreamListener.stream_failed(), stream_closed() and stream_detach() all now include any keywords in the notification method (some of these lacked flags, or only included some) (issue #18); • launch_tor() can take a timeout (starting with a patch from hellais); • cleanup from aagbsn; • more test coverage; • run tests cleanly without graphviz (from lukaslueg); • issue #26 fix from lukaslueg; • pep8 and whitespace targets plus massive cleanup (now pep8 clean, from lukaslueg); • issue #30 fix reported by webmeister making ipaddr actually-optional; • example using synchronous web server (built-in SimpleHTTPServer) with txtorcon (from lukaslueg); • TorState can now create circuits without an explicit path; • passwords for non-cookie authenticated sessions use a password callback (that may return a Deferred) instead of a string (issue #44); • fixes for AddrMap in case #8596 is implemented;

2.1.21 v0.7

November 21, 2012 • txtorcon-0.7.tar.gz( local-sig or github-sig)(source) • issue #20 config object now hooked up correctly after launch_tor(); • patch from hellais for properly handling data_dir given to TCPHiddenServiceEndpoint; • .tac example from mmaker; • allow TorConfig().hiddenservices.append(hs) to work properly with no attached protocol

2.1.22 v0.6

October 10, 2012 • txtorcon-0.6.tar.gz( local-sig or github-sig)(source) • debian packaging (mmaker); • psutil fully gone;

2.1. Releases 41 txtorcon Documentation, Release 1.0.0

• changed API for launch_tor() to use TorConfig instead of args; • TorConfig.save() works properly with no connected Tor; • fix incorrect handling of 650 immediately after connect; • pep8 compliance; • use assertEqual in tests; • messages with embdedded keywords work properly; • fix bug with setup.py + pip; • issue #15 reported along with patch by Isis Lovecruft; • consolidate requirements (from aagbsn); • increased test coverage and various minor fixes; • https URIs for ReadTheDocs;

2.1.23 v0.5

June 20, 2012 • txtorcon-0.5.tar.gz (txtorcon-0.5.tar.gz.sig) (source) • remove psutil as a dependency, including from util.process_from_address

2.1.24 v0.4

June 6, 2012 • txtorcon-0.4.tar.gz (txtorcon-0.4.tar.gz.sig) • remove built documentation from distribution; • fix PyPI problems (“pip install txtorcon” now works)

2.1.25 v0.3

• 0.3 was broken when released (docs couldn’t build).

2.1.26 v0.2

June 1, 2012 • txtorcon-0.2.tar.gz (txtorcon-0.2.tar.gz.sig) • incremental parsing; • faster TorState startup; • SAFECOOKIE support; • several bug fixes; • options to circuit_failure_rates.py example to make it actually-useful; • include built documentation + sources in tarball;

42 Chapter 2. Official Releases: txtorcon Documentation, Release 1.0.0

• include tests in tarball; • improved logging; • patches from mmaker and kneufeld;

2.1.27 v0.1 march, 2012 • txtorcon-0.1.tar.gz (txtorcon-0.1.tar.gz.sig)

2.1. Releases 43 txtorcon Documentation, Release 1.0.0

44 Chapter 2. Official Releases: CHAPTER 3

API Documentation

These are the lowest-level documents, directly from the doc-strings in the code with some minimal organization; if you’re just getting started with txtorcon the “Programming Guide” is a better place to start.

3.1 API Documentation

These are the lowest-level documents, directly from the doc-strings in the code with some minimal organization; if you’re just getting started with txtorcon the “Programming Guide” is a better place to start.

3.1.1 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, tor_config, _process_proto=None) Bases: object 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 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())

45 txtorcon Documentation, Release 1.0.0

don’t instantiate this class yourself – instead use the factory methods txtorcon.launch() or txtorcon.connect() quit(*args, **kwargs) Closes the control connection, and if we launched this Tor instance we’ll send it a TERM and wait until it exits. process protocol The TorControlProtocol instance that is communicating with this Tor instance. config The TorConfig instance associated with the tor instance we launched. This instance represents up-to-date configuration of the tor instance (even if another controller is connected). web_agent(socks_config=None, pool=None) Parameters • socks_config – If None (the default), a suitable SOCKS port is chosen from our config (or added). If supplied, should be either a string which is a valid option for Tor’s SocksPort option or a Deferred which fires an IStreamClientEndpoint (e.g. the return- value from txtorcon.TorConfig.socks_endpoint()) • pool – passed on to the Agent (as pool=) dns_resolve(*args, **kwargs) Parameters hostname – a string Returns a Deferred that calbacks with the hostname as looked-up via Tor (or errback). This uses Tor’s custom extension to the SOCKS5 protocol. dns_resolve_ptr(*args, **kwargs) Parameters ip – a string, like “127.0.0.1” Returns a Deferred that calbacks with the IP address as looked-up via Tor (or errback). This uses Tor’s custom extension to the SOCKS5 protocol. stream_via(host, port, tls=False, socks_port=None) This returns an IStreamClientEndpoint instance that will use this Tor (via SOCKS) to visit the (host, port) indicated. Parameters • host – The host to connect to. You MUST pass host-names to this. If you absolutely know that you’ve not leaked DNS (e.g. you save IPs in your app’s configuration or similar) then you can pass an IP. • port – Port to connect to. • tls – If True, it will wrap the return endpoint in one that does TLS (default: False). • socks_port – Normally not needed (default: None) but you can pass any valid Tor “SocksPort” option here, and one will be configured into this Tor instance (when you call .connect on the endpoint) create_onion_endpoint(port, private_key=None) Returns an object that implements IStreamServerEndpoint, which will create an “ephemeral” Onion ser- vice when .listen() is called. This uses the ADD_ONION tor control-protocol command. Parameters private_key – if not None (the default), this should be the same blob of key material that you received from a previous call to this method.

46 Chapter 3. API Documentation txtorcon Documentation, Release 1.0.0

“Retrieved” here means by accessing the .onion_private_key attribute of the object returned from .listen() (see txtorcon.IHiddenService and txtorcon.TCPHiddenServiceEndpoint.listen()) which will be a txtorcon.TorOnionListeningPort – and therefore implments txtorcon.IOnionService (XXX FIXME it implements IHiddenService). create_onion_disk_endpoint(port, hs_dir=None, group_readable=False) create_state(*args, **kwargs) returns a Deferred that fires with a ready-to-go txtorcon.TorState instance. connect

# FIXME why doesn’t “txtorcon.connect” work here with automethod?? txtorcon.connect() launch txtorcon.launch()

3.1.2 Tracking and Changing Live Tor State

TorState class txtorcon.TorState(protocol, bootstrap=True) Bases: object This tracks the current state of Tor using a TorControlProtocol. On setup it first queries the initial state of streams and circuits. It then asks for updates via the listeners. It requires an ITorControlProtocol instance. The control protocol doesn’t need to be bootstrapped yet. The Deferred .post_boostrap is driggered when the TorState instance is fully ready to go. The easiest way is to use the helper method txtorcon.build_tor_connection(). For details, see the implementation of that. You may add an txtorcon.interface.IStreamAttacher to provide a custom mapping for Strams to Circuits (by default Tor picks by itself). This is also a good example of the various listeners, and acts as an txtorcon.interface.ICircuitContainer and txtorcon.interface.IRouterContainer. Variables DO_NOT_ATTACH – Constant to return from an IAttacher indicating you don’t want to attach this stream at all. classmethod from_protocol(protocol, **kw) Create a new, boot-strapped TorState from a TorControlProtocol instance. Returns a Deferred that fires with a TorState instance circuits = None keys on id (integer) streams = None keys on id (integer) all_routers = None list of unique routers

3.1. API Documentation 47 txtorcon Documentation, Release 1.0.0

routers = None keys by hexid (string) and by unique names routers_by_name = None keys on name, value always list (many duplicate “Unnamed” routers, for example) routers_by_hash = None keys by hexid (string) guards = None potentially-usable as entry guards, I think? (any router with ‘Guard’ flag) entry_guards = None from GETINFO entry-guards, our current entry guards unusable_entry_guards = None list of entry guards we didn’t parse out authorities = None keys by name add_attacher(attacher, myreactor) Provide an txtorcon.interface.IStreamAttacher to associate streams to circuits. This won’t get turned on until after bootstrapping is completed. (‘__LeaveStreamsUnattached’ needs to be set to ‘1’ and the existing circuits list needs to be populated). attachers are called in the order they’re added until one of them returns non-None. If all attachers return None, we just ask Tor to attach the stream. If you don’t want to attach the stream at all (e.g. reject it) then call txtorcon.Stream.close() on the stream. remove_attacher(attacher, myreactor) Returns a Deferred that fires when we’ve successfully removed the give attacher. Note: this must be async becase we may have to issue cleanup commands to Tor if this is the last attacher. XXX probably just put reactor arg into TorState ctor? stream_close_reasons = {‘REASON_EXITPOLICY’: 4, ‘REASON_TORPROTOCOL’: 13, ‘REASON_DONE’: 6, ‘REASON_CONNRESET’: 12, ‘REASON_RESOLVEFAILED’: 2, ‘REASON_RESOURCELIMIT’: 11, ‘REASON_NOROUTE’: 8, ‘REASON_NOTDIRECTORY’: 14, ‘REASON_HIBERNATING’: 9, ‘REASON_MISC’: 1, ‘REASON_TIMEOUT’: 7, ‘REASON_DESTROY’: 5, ‘REASON_INTERNAL’: 10, ‘REASON_CONNECTREFUSED’: 3} close_stream(stream, reason=’REASON_MISC’, **kwargs) This sends a STREAMCLOSE command, using the specified reason (either an int or one of the 14 strings in section 6.3 of tor-spec.txt if the argument is a string). Any kwards are passed through as flags if they evaluated to true (e.g. “SomeFlag=True”). Currently there are none that Tor accepts. close_circuit(circid, **kwargs) This sends a CLOSECIRCUIT command, using any keyword arguments passed as the Flags (currently, that is just ‘IfUnused’ which means to only close the circuit when it is no longer used by any streams). Parameters circid – Either a circuit-id (int) or a Circuit instance Returns a Deferred which callbacks with the result of queuing the command to Tor (usually “OK”). If you want to instead know when the circuit is actually-gone, see Circuit.close on_circuit_new(callback) experimental Callback should take same args as txtorcon.interface.ICircuitListener.circuit_new() See also txtorcon.TorState.add_circuit_listener() on_circuit_launched(callback) experimental Callback should take same args as txtorcon.interface.ICircuitListener.circuit_launched()

48 Chapter 3. API Documentation txtorcon Documentation, Release 1.0.0

See also txtorcon.TorState.add_circuit_listener() on_circuit_extend(callback) experimental Callback should take same args as txtorcon.interface.ICircuitListener.circuit_extend() See also txtorcon.TorState.add_circuit_listener() on_circuit_built(callback) experimental Callback should take same args as txtorcon.interface.ICircuitListener.circuit_built() See also txtorcon.TorState.add_circuit_listener() on_circuit_closed(callback) experimental Callback should take same args as txtorcon.interface.ICircuitListener.circuit_closed() See also txtorcon.TorState.add_circuit_listener() on_circuit_failed(callback) experimental Callback should take same args as txtorcon.interface.ICircuitListener.circuit_failed() See also txtorcon.TorState.add_circuit_listener() add_circuit_listener(icircuitlistener) experimental Adds a new instance of txtorcon.interface.ICircuitListener which will receive updates for all existing and new circuits. add_stream_listener(istreamlistener) experimental Adds a new instance of txtorcon.interface.IStreamListener which will receive updates for all existing and new streams. build_circuit(routers=None, using_guards=True) Builds a circuit consisting of exactly the routers specified, in order. This issues an EXTENDCIRCUIT call to Tor with all the routers specified. Parameters • routers – a list of Router instances which is the path desired. To allow Tor to choose the routers itself, pass None (the default) for routers. • using_guards – A warning is issued if the first router isn’t in self.entry_guards. Returns A Deferred that will callback with a Circuit instance (with the .id member being valid, and probably nothing else). DO_NOT_ATTACH = event_map = {‘CIRC’: , ‘NS’: , ‘ADDRMAP’: , ‘STREAM’: , ‘NEWCONSENSUS’: } event_map used by add_events to map event_name -> unbound method find_circuit(circid) ICircuitContainer API router_from_id(routerid) IRouterContainer API

3.1. API Documentation 49 txtorcon Documentation, Release 1.0.0

stream_new(stream) IStreamListener: a new stream has been created stream_succeeded(stream) IStreamListener: stream has succeeded stream_attach(stream, circuit) IStreamListener: the stream has been attached to a circuit. It seems you get an attach to None followed by an attach to real circuit fairly frequently. Perhaps related to __LeaveStreamsUnattached? stream_detach(stream, **kw) IStreamListener stream_closed(stream, **kw) IStreamListener: stream has been closed (won’t be in controller’s list anymore) stream_failed(stream, **kw) IStreamListener: stream failed for some reason (won’t be in controller’s list anymore) circuit_launched(circuit) ICircuitListener API circuit_extend(circuit, router) ICircuitListener API circuit_built(circuit) ICircuitListener API circuit_new(circuit) ICircuitListener API circuit_destroy(circuit) Used by circuit_closed and circuit_failed (below) circuit_closed(circuit, **kw) ICircuitListener API circuit_failed(circuit, **kw) ICircuitListener API

Circuit class txtorcon.Circuit(routercontainer) Bases: object Used by txtorcon.TorState to represent one of Tor’s circuits. This is kept up-to-date by the :class‘txtorcon.TorState‘ that owns it, and individual circuits can be listened to for updates (or listen to every one using txtorcon.TorState.add_circuit_listener()) Variables • path – contains a list of txtorcon.Router objects representing the path this Circuit takes. Mostly this will be 3 or 4 routers long. Note that internally Tor uses single-hop paths for some things. See also the purpose instance-variable. • streams – contains a list of Stream objects representing all streams currently attached to this circuit. • state – contains a string from Tor describing the current state of the stream. From control- spec.txt section 4.1.1, these are: – LAUNCHED: circuit ID assigned to new circuit

50 Chapter 3. API Documentation txtorcon Documentation, Release 1.0.0

– BUILT: all hops finished, can now accept streams – EXTENDED: one more hop has been completed – FAILED: circuit closed (was not built) – CLOSED: circuit closed (was built) • purpose – The reason this circuit was built. Values can currently be one of (but see control-spec.txt 4.1.1): – GENERAL – HS_CLIENT_INTRO – HS_CLIENT_REND – HS_SERVICE_INTRO – HS_SERVICE_REND – TESTING – CONTROLLER For most purposes, you’ll want to look at GENERAL circuits only. Variables id – The ID of this circuit, a number (or None if unset). Parameters routercontainer – should implement txtorcon.interface.IRouterContainer. is_built when_built() Returns a Deferred that is callback()’d (with this Circuit instance) when this circuit hits BUILT. If it’s already BUILT when this is called, you get an already-successful Deferred; otherwise, the state must change to BUILT. web_agent(reactor, socks_endpoint=None, pool=None) Parameters • socks_endpoint – create one with txtorcon.TorState.socks_endpoint(). Can be a Deferred. Can be None for a default one. • pool – passed on to the Agent (as pool=) stream_via(reactor, host, port, socks_endpoint, use_tls=False) This returns an IStreamClientEndpoint that wraps the passed-in endpoint such that it goes via Tor, and via this parciular circuit. We match the streams up using their source-ports, so even if there are many streams in-flight to the same destination they will align correctly. For example, to cause a stream to go to torproject.org:443 via a particular circuit: from twisted.internet.endpoints import HostnameEndpoint

dest= HostnameEndpoint(reactor,"torproject.org", 443) circ= yield torstate.build_circuit() # lets Tor decide the path tor_ep= circ.stream_via(dest) # 'factory' is for your protocol proto= yield tor_ep.connect(factory)

3.1. API Documentation 51 txtorcon Documentation, Release 1.0.0

Note that if you’re doing client-side Web requests, you probably want to use treq or Agent directly so call txtorcon.Circuit.web_agent() instead. Parameters socks_endpoint – should be a Deferred firing a valid IStreamClientEndpoint pointing at a Tor SOCKS port (or an IStreamClientEndpoint already). time_created listen(listener) unlisten(listener) close(**kw) This asks Tor to close the underlying circuit object. See txtorcon.torstate.TorState.close_circuit() for details. You may pass keyword arguments to take care of any Flags Tor accepts for the CLOSECIRCUIT com- mand. Currently, this is only “IfUnused”. So for example: circ.close(IfUnused=True) Returns Deferred which callbacks with this Circuit instance ONLY after Tor has confirmed it is gone (not simply that the CLOSECIRCUIT command has been queued). This could be a while if you included IfUnused. age(now=None) Returns an integer which is the difference in seconds from ‘now’ to when this circuit was created. Returns None if there is no created-time. update(args) maybe_call_closing_deferred() Used internally to callback on the _closing_deferred if it exists. update_path(path) There are EXTENDED messages which don’t include any routers at all, and any of the EXTENDED messages may have some arbitrary flags in them. So far, they’re all upper-case and none start with $ luckily. The routers in the path should all be LongName-style router names (this depends on them starting with $). For further complication, it’s possible to extend a circuit to a router which isn’t in the consensus. nickm via #tor thought this might happen in the case of hidden services choosing a rendevouz point not in the current consensus.

Stream class txtorcon.Stream(circuitcontainer, addrmap=None) Bases: object Represents an active stream in Tor’s state (txtorcon.TorState). Variables • circuit – Streams will generally be attached to circuits pretty quickly. If they are at- tached, circuit will be a txtorcon.Circuit instance or None if this stream isn’t yet attached to a circuit. • state – Tor’s idea of the stream’s state, one of: – NEW: New request to connect – NEWRESOLVE: New request to resolve an address

52 Chapter 3. API Documentation txtorcon Documentation, Release 1.0.0

– REMAP: Address re-mapped to another – SENTCONNECT: Sent a connect cell along a circuit – SENTRESOLVE: Sent a resolve cell along a circuit – SUCCEEDED: Received a reply; stream established – FAILED: Stream failed and not retriable – CLOSED: Stream closed – DETACHED: Detached from circuit; still retriable • target_host – Something like www.example.com – the host the stream is destined for. • target_port – The port the stream will exit to. • target_addr – Target address, looked up (usually) by Tor (e.g. 127.0.0.1). • id – The ID of this stream, a number (or None if unset). Parameters circuitcontainer – an object which implements interface.ICircuitContainer id = None An int, Tor’s ID for this txtorcon.Circuit state = None A string, Tor’s idea of the state of this txtorcon.Stream target_host = None Usually a hostname, but sometimes an IP address (e.g. when we query existing state from Tor) target_addr = None If available, the IP address we’re connecting to (if None, see target_host instead). target_port = None The port we’re connecting to. circuit = None If we’ve attached to a txtorcon.Circuit, this will be an instance of txtorcon.Circuit (other- wise None). listeners = None A list of all connected txtorcon.interface.IStreamListener instances. source_addr = None If available, the address from which this Stream originated (e.g. local process, etc). See get_process() also. source_port = None If available, the port from which this Stream originated. See get_process() also. flags = None All flags from last update to this Stream. str->str listen(listen) Attach an txtorcon.interface.IStreamListener to this stream. See also txtorcon.TorState.add_stream_listener() to listen to all streams. Parameters listen – something that knows txtorcon.interface.IStreamListener unlisten(listener)

3.1. API Documentation 53 txtorcon Documentation, Release 1.0.0

close(**kw) This asks Tor to close the underlying stream object. See txtorcon.interface.ITorControlProtocol.close_stream() for details. Although Tor currently takes no flags, it allows you to; any keyword arguments are passed through as flags. NOTE that the callback delivered from this method only callbacks after the underlying stream is really destroyed (not just when the CLOSESTREAM command has successfully completed). update(args) maybe_call_closing_deferred() Used internally to callback on the _closing_deferred if it exists.

Router class txtorcon.Router(controller) Bases: object Represents a Tor Router, including location. The controller you pass in is really only used to do get_info calls for ip-to-country/IP in case the txtorcon.util.NetLocation stuff fails to find a country. After an .update() call, the id_hex attribute contains a hex-encoded long hash (suitable, for example, to use in a GETINFO ns/id/* call). After setting the policy property you may call accepts_port() to find out if the router will accept a given port. This works with the reject or accept based policies. unique_name has the hex id if this router’s name is not unique, or its name otherwise modified update(name, idhash, orhash, modified, ip, orport, dirport) get_location() Returns a Deferred that fires with a NetLocation object for this router. location A NetLocation instance with some GeoIP or pygeoip information about location, asn, city (if available). flags A list of all the flags for this Router, each one an all-lower-case string. bandwidth The reported bandwidth of this Router. policy Port policies for this Router. :return: a string describing the policy accepts_port(port) Query whether this Router will accept the given port.

3.1.3 Reading and Writing Live Tor Configuration

TorConfig class txtorcon.TorConfig(control=None) Bases: object

54 Chapter 3. API Documentation txtorcon Documentation, Release 1.0.0

This class abstracts out Tor’s config, and can be used both to create torrc files from nothing and track live configuration of a Tor instance. Also, it gives easy access to all the configuration options present. This is initialized at “bootstrap” time, pro- viding attribute-based access thereafter. Note that after you set some number of items, you need to do a save() before these are sent to Tor (and then they will be done as one SETCONF). You may also use this class to construct a configuration from scratch (e.g. to give to txtorcon.launch_tor()). In this case, values are reflected right away. (If we’re not bootstrapped to a Tor, this is the mode). Note that you do not need to call save() if you’re just using TorConfig to create a .torrc file or for input to launch_tor(). This class also listens for CONF_CHANGED events to update the cached data in the event other controllers (etc) changed it. There is a lot of magic attribute stuff going on in here (which might be a bad idea, overall) but the intent is that you can just set Tor options and it will all Just Work. For config items that take multiple values, set that to a list. For example: conf= TorConfig(...) conf.SOCKSPort=[9050, 1337] conf.HiddenServices.append(HiddenService(...))

(Incoming objects, like lists, are intercepted and wrapped). FIXME: when is CONF_CHANGED introduced in Tor? Can we do anything like it for prior versions? FIXME: •HiddenServiceOptions is special: GETCONF on it returns several (well, two) values. Besides adding the two keys ‘by hand’ do we need to do anything special? Can’t we just depend on users doing ‘conf.hiddenservicedir = foo’ AND ‘conf.hiddenserviceport = bar’ before a save() ? •once I determine a value is default, is there any way to actually get what this value is? static from_protocol(*args, **kwargs) This creates and returns a ready-to-go TorConfig instance from the given protocol, which should be an instance of TorControlProtocol. config = None Current configuration, by keys. unsaved = None Configuration that has been changed since last save(). parsers = None Instances of the parser classes, subclasses of TorConfigType list_parsers = None All the names (keys from .parsers) that are a List of something. socks_endpoint(reactor, port=None) Returns a TorSocksEndpoint configured to use an already-configured SOCKSPort from the Tor we’re connected to. By default, this will be the very first SOCKSPort. Parameters port – a str, the first part of the SOCKSPort line (that is, a port like “9151” or a Unix socket config like “unix:/path”. You may also specify a port as an int. If you need to use a particular port that may or may not already be configured, see the async method txtorcon.TorConfig.create_socks_endpoint()

3.1. API Documentation 55 txtorcon Documentation, Release 1.0.0

create_socks_endpoint(*args, **kwargs) Creates a new TorSocksEndpoint instance given a valid configuration line for SocksPort; if this configuration isn’t already in the underlying tor, we add it. Note that this method may call txtorcon.TorConfig.save() on this instance. Note that calling this with socks_config=None is equivalent to calling .socks_endpoint (which is not async). XXX socks_config should be .. i dunno, but there’s fucking options and craziness, e.g. default Tor Browser Bundle is: [‘9150 IPv6Traffic PreferIPv6 KeepAliveIsolateSOCKSAuth’, ‘9155’] XXX maybe we should say “socks_port” as the 3rd arg, insist it’s an int, and then allow/support all the other options (e.g. via kwargs) XXX we could avoid the “maybe call .save()” thing; worth it? (actually, no we can’t or the Tor won’t have it config’d) onion_create(ports, auth=None, directory=None, private_key=None) Creates a new Onion service. Parameters • ports – list of strings like “80 127.0.0.1:80” • auth – None, or an IOnionAuthentication provider (in practice, an instance of OnionAuthBasic or OnionAuthStealth) • directory – None means an ephemeral hidden service (the default). Otherwise, a “nor- mal”, persistent hidden-service using data in the provided directory (if the directory is empty, a new private key will be written there by Tor). • private_key – If creating an ephemeral service, this can be provided. This will be something previously retrieved from the .private_key attribute of a HiddenService instance. Returns Deferred that fires with the HiddenService instance once it is configured. protocol tor_protocol attach_protocol(proto) returns a Deferred that fires once we’ve set this object up to track the protocol. Fails if we already have a protocol. get_type(name) return the type of a config key. Param name the key FIXME can we do something more-clever than this for client code to determine what sort of thing a key is? bootstrap(arg=None) This only takes args so it can be used as a callback. Don’t pass an arg, it is ignored. do_post_errback(f ) do_post_bootstrap(arg) needs_save() mark_unsaved(name)

56 Chapter 3. API Documentation txtorcon Documentation, Release 1.0.0

save() Save any outstanding items. This returns a Deferred which will errback if Tor was unhappy with anything, or callback with this TorConfig object on success. config_args() Returns an iterator of 2-tuples (config_name, value), one for each configuration option in this config. This is more-or-less an internal method, but see, e.g., launch_tor()’s implementation if you think you need to use this for something. See txtorcon.TorConfig.create_torrc() which returns a string which is also a valid torrc file create_torrc()

HiddenService txtorcon.HiddenService alias of FilesystemOnionService

3.1.4 Endpoints and Related Classes

TCPHiddenServiceEndpoint class txtorcon.TCPHiddenServiceEndpoint(reactor, config, public_port, hid- den_service_dir=None, local_port=None, stealth_auth=None, ephemeral=None, pri- vate_key=None, group_readable=False) Bases: object This represents something listening on an arbitrary local port that has a Tor configured with a Hidden Service pointing at it. TCP4ServerEndpoint is used under the hood to do the local listening. There are three main ways to use this class, and you are encouraged to use the @classmethod ways of creating instances: system_tor, global_tor, and private_tor 1.system_tor(...) connects to an already-started tor on the endpoint you specify; stricly speaking not a “system” tor since you could have spawned it some other way. See Tor bug 11291 however. 2.global_tor(...) refers to a single possible Tor instance per python process. So the first call to this launches a new Tor, and subsequent calls re-use the existing Tor (that is, add more hidden services to it). 3.private_tor(...) launches a new Tor instance no matter what, so it will have just the one hidden serivce on it. If you need to set configuration options that are not reflected in any of the method signatures above, you’ll have to construct an instance of this class yourself (i.e. with a TorConfig instance you’ve created). No matter how you came by your instance, calling listen() on it causes Tor to be launched or connected-to, your hidden service to be added, checks that the descriptor is uploaded and you get a Deferred with an IListeningPort whose getHost() will return a txtorcon.TorOnionAddress. The port object will also implement txtorcon.IHiddenService so you can get the locally-listening address and hidden serivce directory: endpoint=... port= yield endpoint.listen(...) uri= port.getHost().onion_uri port= port.getHost().onion_port

3.1. API Documentation 57 txtorcon Documentation, Release 1.0.0

addr= IHiddenService(port).local_address hsdir= IHiddenService(port).hidden_service_dir

returns (via Deferred) an object that implements twisted.internet.interfaces.IStreamServerEndpoint Variables • onion_uri – the public key, like timaq4ygg2iegci7.onion which came from the hidden_service_dir’s hostname file • onion_private_key – the contents of hidden_service_dir/private_key • hiddenServiceDir – the data directory, either passed in or created with tempfile.mkdtemp Parameters • reactor – twisted.internet.interfaces.IReactorTCP provider • config – txtorcon.TorConfig instance. • public_port – The port number we will advertise in the hidden serivces directory. • local_port – The port number we will perform our local tcp listen on and receive in- coming connections from the tor process. • hidden_service_dir – If not None, point to a HiddenServiceDir directory (i.e. with “hostname” and “private_key” files in it). If not provided, one is created with temp.mkdtemp() AND DELETED when the reactor shuts down. • stealth_auth – A list of strings, one name for each stealth authenticator you want. Like: [’alice’, ’bob’] • endpoint_generator – A callable that generates a new instance of something that implements IServerEndpoint (by default TCP4ServerEndpoint) classmethod system_tor(reactor, control_endpoint, public_port, hidden_service_dir=None, lo- cal_port=None, ephemeral=None, private_key=None) This returns a TCPHiddenServiceEndpoint connected to the endpoint you specify in control_endpoint. Af- ter connecting, a single hidden service is added. The endpoint can be a Unix socket if Tor’s ControlSocket option was used (instead of ControlPort).

Note: If Tor bug #11291 is not yet fixed, this won’t work if you only have Group access. XXX FIXME re-test

classmethod global_tor(reactor, public_port, hidden_service_dir=None, local_port=None, control_port=None, stealth_auth=None, ephemeral=None, pri- vate_key=None) This returns a TCPHiddenServiceEndpoint connected to a txtorcon global Tor instance. The first time you call this, a new Tor will be launched. Subsequent calls will re-use the same connection (in fact, the very same TorControlProtocol and TorConfig instances). If the options you pass are incompatible with an already-launched Tor, RuntimeError will be thrown. It’s probably best to not specify any option besides public_port, hidden_service_dir, and maybe local_port unless you have a specific need to. You can also access this global txtorcon instance via txtorcon.get_global_tor() (which is pre- cisely what this method uses to get it). All keyword options have defaults (e.g. random ports, or tempdirs).

58 Chapter 3. API Documentation txtorcon Documentation, Release 1.0.0

Parameters stealth_auth – None, or a list of strings – one for each stealth authenticator you require. classmethod private_tor(reactor, public_port, hidden_service_dir=None, local_port=None, con- trol_port=None, ephemeral=None, private_key=None) This returns a TCPHiddenServiceEndpoint that’s always connected to its own freshly-launched Tor in- stance. All keyword options have defaults (e.g. random ports, or tempdirs). retries = None for IProgressProvider to add_progress_listener onion_uri onion_private_key add_progress_listener(listener) IProgressProvider API listen(*args, **kwargs) Implement IStreamServerEndpoint. Returns a Deferred that delivers an twisted.internet.interfaces.IListeningPort implementation. This port instance can also be adapted to txtorcon.IOnionService, which provides all details of the service (e.g. .private_key, .hostname, .ports) At this point, Tor will have fully started up and successfully accepted the hidden service’s config. Also, at least one Hidden Service Directory will have successfully received the Descriptor so the service should be reachable by other Tor clients. txtorcon.get_global_tor(*args, **kwargs) See description of txtorcon.TCPHiddenServiceEndpoint‘s class-method global_tor Parameters • control_port – a TCP port upon which to run the launched Tor’s control-protocol (se- lected by the OS by default). • progress_updates – A callable that takes 3 args: percent, tag, message which is called when Tor announcing some progress setting itself up. Returns a Deferred that fires a txtorcon.TorConfig which is bootstrapped. The _tor_launcher keyword arg is internal-only.

TCPHiddenServiceEndpointParser class txtorcon.TCPHiddenServiceEndpointParser Bases: object This provides a twisted IPlugin and IStreamServerEndpointsStringParser so you can call serverFromString with a string argument like: onion:80:localPort=9876:controlPort=9052:hiddenServiceDir=/dev/shm/foo ...or simply: onion:80 If controlPort is specified, it means connect to an already-running Tor on that port and add a hidden-serivce to it. localPort is optional and if not specified, a port is selected by the OS.

3.1. API Documentation 59 txtorcon Documentation, Release 1.0.0

If hiddenServiceDir is not specified, one is created with tempfile.mkdtemp(). The IStream- ServerEndpoint returned will be an instance of txtorcon.TCPHiddenServiceEndpoint prefix = ‘onion’ parseStreamServer(reactor, public_port, localPort=None, controlPort=None, hiddenSer- viceDir=None, privateKey=None) twisted.internet.interfaces.IStreamServerEndpointStringParser

TorOnionAddress class txtorcon.TorOnionAddress(port, hs) Bases: twisted.python.util.FancyEqMixin, object A TorOnionAddress represents the public address of a Tor hidden service. Variables • type – A string describing the type of transport, ‘onion’. • port – The public port we’re advertising • clients – A list of IHiddenServiceClient instances, at least 1. compareAttributes = (‘type’, ‘onion_port’, ‘onion_key’) type = ‘onion’ onion_key

TorOnionListeningPort class txtorcon.TorOnionListeningPort(listening_port, public_port, hiddenservice, tor_config) Bases: object Our TCPHiddenServiceEndpoint’s listen method will return a deferred which fires an instance of this object. The getHost method will return a TorOnionAddress instance... which can be used to determine the onion address of a newly created Tor Hidden Service. startListening and stopListening methods proxy to the “TCP ListeningPort” object... which implements ILis- teningPort interface but has many more responsibilities we needn’t worry about here. startListening() IListeningPort API stopListening() IListeningPort API getHost() IListeningPort API tor_config onion_service

IProgressProvider interface txtorcon.IProgressProvider FIXME move elsewhere? think harder?

60 Chapter 3. API Documentation txtorcon Documentation, Release 1.0.0

add_progress_listener(listener) Adds a progress listener. The listener is a callable that gets called with 3 arguments corresponding to Tor’s updates: (percent, tag, message). percent is an integer from 0 to 100, tag and message are both strings. (message is the human-readable one)

IHiddenService interface txtorcon.IHiddenService

local_address The actual local machine address we are listening on. public_port The port our service can be contacted on tor_config The TorConfig object attached to the Tor hosting this hidden service (in turn has .protocol for TorControl- Protocol). clients List of IHiddenServiceClient instances.Unauthenticated services will have 0 clients.Basic-auth services will have 1 client, called “default”.

3.1.5 Low-Level Protocol Classes build_tor_connection txtorcon.build_tor_connection(connection, build_state=True, wait_for_proto=True, pass- word_function=>) This is used to build a valid TorState (which has .protocol for the TorControlProtocol). For example: from twisted.internet import reactor from twisted.internet.endpoints import TCP4ClientEndpoint import txtorcon

def example(state): print"Fully bootstrapped state:",state print" with bootstrapped protocol:",state.protocol

d= txtorcon.build_tor_connection(TCP4ClientEndpoint(reactor, "localhost", 9051)) d.addCallback(example) reactor.run()

Parameters • password_function – See txtorcon.TorControlProtocol • build_state – If True (the default) a TorState object will be built as well. If False, just a TorControlProtocol will be returned via the Deferred. Returns a Deferred that fires with a TorControlProtocol or, if you specified build_state=True, a TorState. In both cases, the object has finished bootstrapping (i.e. TorControlProto- col.post_bootstrap or TorState.post_bootstap has fired, as needed)

3.1. API Documentation 61 txtorcon Documentation, Release 1.0.0 build_local_tor_connection txtorcon.build_local_tor_connection(reactor, host=‘127.0.0.1’, port=9051, socket=’/var/run/tor/control’, *args, **kwargs) This builds a connection to a local Tor, either via 127.0.0.1:9051 (which is tried first) or /var/run/tor/control (by default). See also build_tor_connection for other key-word arguments that are accepted here also. Parameters • host – An IP address to find Tor at. Corresponds to the ControlListenAddress torrc option. • port – The port to use with the address when trying to contact Tor. This corresponds to the ControlPort option in torrc (default is 9051).

TorControlProtocol class txtorcon.TorControlProtocol(password_function=None) Bases: twisted.protocols.basic.LineOnlyReceiver This is the main class that talks to a Tor and implements the “raw” procotol. This instance does not track state; see txtorcon.TorState for the current state of all Circuits, Streams and Routers. txtorcon.TorState.build_circuit() allows you to build custom circuits. txtorcon.TorControlProtocol.add_event_listener() can be used to listen for specific events. To see how circuit and stream listeners are used, see txtorcon.TorState, which is also the place to go if you wish to add your own stream or circuit listeners. Parameters password_function – A zero-argument callable which returns a password (or De- ferred). It is only called if the Tor doesn’t have COOKIE authentication turned on. Tor’s default is COOKIE. add_event_listener(evt, callback) Parameters evt – event name, see also :var:‘txtorcon.TorControlProtocol.events‘ .keys() Add a listener to an Event object. This may be called multiple times for the same event. If it’s the first listener, a new SETEVENTS call will be initiated to Tor. Currently the callback is any callable that takes a single argument, that is the text collected for the event from the tor control protocol. For more information on the events supported, see control-spec section 4.1

Note: this is a low-level interface; if you want to follow circuit or stream creation etc. see TorState and methods like add_circuit_listener

Return None

Todo need an interface for the callback show how to tie in Stem parsing if you want

62 Chapter 3. API Documentation txtorcon Documentation, Release 1.0.0

authenticate(passphrase) Call the AUTHENTICATE command. Quoting torspec/control-spec.txt: “The authentication token can be specified as either a quoted ASCII string, or as an unquoted hexadecimal encoding of that same string (to avoid escaping issues).” connectionLost(reason) Protocol API connectionMade() Protocol API get_conf(*args) Uses GETCONF to obtain configuration values from Tor. Parameters args – any number of strings which are keys to get. To get all valid configuraiton names, you can call: get_info(’config/names’) Returns a Deferred which callbacks with one or many configuration values (depends on what you asked for). See control-spec for valid keys (you can also use TorConfig which will come set up with all the keys that are valid). The value will be a dict. Note that Tor differentiates between an empty value and a default value; in the raw protocol one looks like ‘250 MyFamily’ versus ‘250 MyFamily=’ where the latter is set to the empty string and the former is a default value. We differentiate these by setting the value in the dict to DEFAULT_VALUE for the default value case, or an empty string otherwise. get_conf_raw(*args) Same as get_conf, except that the results are not parsed into a dict get_info(*args) Uses GETINFO to obtain informatoin from Tor. Parameters args – should be a list or tuple of strings which are valid information keys. For valid keys, see control-spec.txt from torspec.

Todo make some way to automagically obtain valid keys, either from running Tor or parsing control-spec

Returns a Deferred which will callback with a list containing the results of the keys you asked for (in order). If you asked for just one key, there will be no list – merely the answer. get_info_incremental(key, line_cb) Mostly for internal use; calls GETINFO for a single key and calls line_cb with each line received, as it is received. See getinfo get_info_raw(*args) Mostly for internal use; gives you the raw string back from the GETINFO command. See getinfo graphviz_data() lineReceived(line) twisted.protocols.basic.LineOnlyReceiver API protocolinfo() Returns a Deferred which will give you PROTOCOLINFO; see control-spec

3.1. API Documentation 63 txtorcon Documentation, Release 1.0.0

queue_command(cmd, arg=None) returns a Deferred which will fire with the response data when we get it Note that basically every request is ultimately funelled through this command. quit() Sends the QUIT command, which asks Tor to hang up on this controller connection. If you’ve taken ownership of the Tor to which you’re connected, this should also cause it to exit. Otherwise, it won’t. remove_event_listener(evt, cb) set_conf(*args) set configuration values. see control-spec for valid keys. args is treated as a list containing name then value pairs. For example, set_conf(’foo’, ’bar’) will (attempt to) set the key ‘foo’ to value ‘bar’. Returns a Deferred that will callback with the response (‘OK’) or errback with the error code and message (e.g. "552 Unrecognized option: Unknown option ’foo’. Failing.") signal(nm) Issues a signal to Tor. See control-spec or txtorcon.TorControlProtocol.valid_signals for which ones are available and their return values. Returns a Deferred which callbacks with Tor’s response (OK or something like 552 Unrecognized signal code "foo"). start_debug() stop_debug()

TorProtocolFactory class txtorcon.TorProtocolFactory(password_function=>) Bases: object Builds TorControlProtocol objects. Implements IProtocolFactory for Twisted interaction. If your running Tor doesn’t support COOKIE authentication, then you should supply a password callback. Builds protocols to talk to a Tor client on the specified address. For example: ep= TCP4ClientEndpoint(reactor,"localhost", 9051) ep.connect(TorProtocolFactory()) reactor.run()

By default, COOKIE authentication is used if available. Parameters password_function – If supplied, this is a zero-argument method that returns a password (or a Deferred). By default, it returns None. This is only queried if the Tor we connect to doesn’t support (or hasn’t enabled) COOKIE authentication. buildProtocol(addr) twisted.internet.interfaces.IProtocolFactory API doStart() twisted.internet.interfaces.IProtocolFactory API doStop() twisted.internet.interfaces.IProtocolFactory API

64 Chapter 3. API Documentation txtorcon Documentation, Release 1.0.0

TorProcessProtocol class txtorcon.TorProcessProtocol(connection_creator, progress_updates=None, config=None, ireactortime=None, timeout=None, kill_on_stderr=True, std- out=None, stderr=None) Bases: twisted.internet.protocol.ProcessProtocol This will read the output from a Tor process and attempt a connection to its control port when it sees any ‘Boot- strapped’ message on stdout. You probably don’t need to use this directly except as the return value from the txtorcon.launch_tor() method. tor_protocol contains a valid txtorcon.TorControlProtocol instance by that point. connection_creator is a callable that should return a Deferred that callbacks with a txtorcon.TorControlProtocol; see txtorcon.launch_tor() for the default one which is a functools.partial that will call connect(TorProtocolFactory()) on an appropriate twisted.internet.endpoints.TCP4ClientEndpoint Parameters • connection_creator – A no-parameter callable which returns a Deferred which promises a IStreamClientEndpoint. If this is None, we do NOT attempt to connect to the underlying Tor process. • progress_updates – A callback which received progress updates with three args: per- cent, tag, summary • config – a TorConfig object to connect to the TorControlProtocl from the launched tor (should it succeed) • ireactortime – An object implementing IReactorTime (i.e. a reactor) which needs to be supplied if you pass a timeout. • timeout – An int representing the timeout in seconds. If we are unable to reach 100% by this time we will consider the setting up of Tor to have failed. Must supply ireactortime if you supply this. • kill_on_stderr – When True, kill subprocess if we receive anything on stderr • stdout – Anything subprocess writes to stdout is sent to .write() on this • stderr – Anything subprocess writes to stderr is sent to .write() on this Variables tor_protocol – The TorControlProtocol instance connected to the Tor this ProcessProtocol>‘ is speaking to. Will be valid after the Deferred returned from TorProcessProtocol.when_connected() is triggered. when_connected() quit() This will terminate (with SIGTERM) the underlying Tor process. Returns a Deferred that callback()’s (with None) when the process has actually exited. outReceived(data) ProcessProtocol API errReceived(data) ProcessProtocol API cleanup() Clean up my temporary files. processExited(reason)

3.1. API Documentation 65 txtorcon Documentation, Release 1.0.0

processEnded(status) ProcessProtocol API progress(percent, tag, summary) Can be overridden or monkey-patched if you want to get progress updates yourself.

3.1.6 txtorcon.interface Module interface.IStreamAttacher interface txtorcon.interface.IStreamAttacher Used by txtorcon.TorState to map streams to circuits (see txtorcon.TorState.set_attacher()). Each time a new txtorcon.Stream is created, this interface will be queried to find out which txtorcon.Circuit it should be attached to. attach_stream(stream, circuits) Parameters • stream – The stream to attach, which will be in NEW or NEWRESOLVE state. • circuits – all currently available txtorcon.Circuit objects in the txtorcon.TorState in a dict indexed by id. Note they are not limited to BUILT circuits. You should return a txtorcon.Circuit instance which should be at state BUILT in the currently running Tor. You may also return a Deferred which will callback with the desired circuit. In this case, you will probably need to be aware that the callback from txtorcon.TorState.build_circuit() does not wait for the circuit to be in BUILT state. See attach_streams_by_country.py for a complete example of using a Deferred in an IStreamAttacher. Alternatively, you may return None in which case the Tor controller will be told to choose a circuit itself. Note that Tor will refuse to attach to any circuit not in BUILT state; see ATTACHSTREAM in control- spec.txt Note also that although you get a request to attach a stream that ends in .onion Tor doesn’t currently let you specify how to attach .onion addresses and will always give a 551 error. attach_stream_failure(stream, fail) If we get any errors while attaching a stream, this method is called with a Failure instance. interface.IStreamListener interface txtorcon.interface.IStreamListener Notifications about changes to a txtorcon.Stream. If you wish for your listener to be added to all new streams, see txtorcon.TorState.add_stream_listener(). stream_new(stream) a new stream has been created stream_succeeded(stream) stream has succeeded stream_attach(stream, circuit) the stream has been attached to a circuit

66 Chapter 3. API Documentation txtorcon Documentation, Release 1.0.0

stream_detach(stream, **kw) the stream has been detached from its circuit Parameters kw – provides any flags for this event, which will include at least REASON (but may include anything). See control-spec. stream_closed(stream, **kw) stream has been closed (won’t be in controller’s list anymore). Parameters kw – provides any flags for this event, which will include at least REASON (but may include anything). See control-spec. stream_failed(stream, **kw) stream failed for some reason (won’t be in controller’s list anymore). Parameters kw – a dict of all the flags for the stream failure; see control-spec but these will include REASON and sometimes REMOTE_REASON (if the remote Tor closed the con- nection). Both an all-uppercase and all-lowercase version of each keyword is supplied (by the library; Tor provides all-uppercase only). Others may include BUILD_FLAGS, PUR- POSE, HS_STATE, REND_QUERY, TIME_CREATED (or anything else). interface.ICircuitListener interface txtorcon.interface.ICircuitListener An interface to listen for updates to Circuits. circuit_new(circuit) A new circuit has been created. You’ll always get one of these for every Circuit even if it doesn’t go through the “launched” state. circuit_launched(circuit) A new circuit has been started. circuit_extend(circuit, router) A circuit has been extended to include a new router hop. circuit_built(circuit) A circuit has been extended to all hops (usually 3 for user circuits). circuit_closed(circuit, **kw) A circuit has been closed cleanly (won’t be in controller’s list any more). Parameters kw – A dict of additional args. REASON is alsways included, and often REMOTE_REASON also. See the control-spec documentation. As of this writing, REASON is one of the following strings: MISC, RESOLVEFAILED, CONNECTRE- FUSED, EXITPOLICY, DESTROY, DONE, TIMEOUT, NOROUTE, HIBERNATING, IN- TERNAL,RESOURCELIMIT, CONNRESET, TORPROTOCOL, NOTDIRECTORY, END, PRIVATE_ADDR. However, don’t depend on that: it could be anything. To facilitate declaring args you want in the method (e.g. circuit_failed(self, circuit, rea- son=None, remote_reason=None, **kw)) lower-case versions of all the keys are also pro- vided (pointing to the same – usually UPPERCASE – strings as the upper-case keys). circuit_failed(circuit, **kw) A circuit has been closed because something went wrong. The circuit won’t be in the TorState’s list anymore. Parameters kw – A dict of additional args. REASON is alsways included, and often REMOTE_REASON also. See the control-spec documentation. As of this writing,

3.1. API Documentation 67 txtorcon Documentation, Release 1.0.0

REASON is one of the following strings: MISC, RESOLVEFAILED, CONNECTRE- FUSED, EXITPOLICY, DESTROY, DONE, TIMEOUT, NOROUTE, HIBERNATING, IN- TERNAL,RESOURCELIMIT, CONNRESET, TORPROTOCOL, NOTDIRECTORY, END, PRIVATE_ADDR. However, don’t depend on that: it could be anything. To facilitate declaring args you want in the method (e.g. circuit_failed(self, circuit, rea- son=None, remote_reason=None, **kw)) lower-case versions of all the keys are also pro- vided (pointing to the same – usually UPPERCASE – strings as the upper-case keys). interface.ICircuitContainer interface txtorcon.interface.ICircuitContainer An interface that contains a bunch of Circuit objects and can look them up by id. find_circuit(circ_id) Returns a circuit for the cird_id, or exception. close_circuit(circuit, **kwargs) Close a circuit. :return: a Deferred which callbacks when the closing process is started (not necessarily finished inside Tor). close_stream(stream, **kwargs) Close a stream. :return: a Deferred which callbacks when the closing process is started (not necessarily finished inside Tor). interface.IRouterContainer interface txtorcon.interface.IRouterContainer

unique_routers contains a list of all the Router instances router_from_id(routerid) Note that this method MUST always return a Router instance – if you ask for a router ID that didn’t yet exist, it is created (although without IP addresses and such because it wasn’t in the consensus). You may find out if a Router came from the ‘GETINFO ns/all’ list by checking the from_consensus attribute. This is to simplify code like in Circuit.update() that needs to handle the case where an EXTENDED circuit event is the only time we’ve seen a Router – it’s possible for Tor to do things with routers not in the consensus (like extend circuits to them). Returns a router by its ID. interface.ITorControlProtocol interface txtorcon.interface.ITorControlProtocol This defines the API to the TorController object. This is the usual entry-point to this library, and you shouldn’t need to call methods outside this interface. get_info(info) Returns a Deferred which will callback with the info keys you asked for. For values ones, see control-spec.

68 Chapter 3. API Documentation txtorcon Documentation, Release 1.0.0

get_conf(*args) Returns one or many configuration values via Deferred. See control-spec for valid keys. The value will be a dictionary. signal(signal_name) Issues a signal to Tor. See control-spec or .valid_signals for which ones are available and their return values. build_circuit(routers) Builds a circuit consisting of exactly the routers specified, in order. This issues a series of EXTENDCIR- CUIT calls to Tor; the deferred returned from this is for the final EXTEND. FIXME: should return the Circuit instance, but currently returns final extend message ‘EXTEND 1234’ for example. close_circuit(circuit) Asks Tor to close the circuit. Note that the Circuit instance is only removed as a result of the next CIRC CLOSED event. The Deferred returned from this method callbacks when the CLOSECIRCUIT command has successfully executed, not when the circuit is actually gone. If you wish to know when this circuit is actually gone, add an ICircuitListener and wait for circuit_closed() add_event_listener(evt, callback) Add a listener to an Event object. This may be called multiple times for the same event. Every time the event happens, the callback method will be called. The callback has one argument (a string, the contents of the event, minus the ‘650’ and the name of the event) FIXME: should have an interface for the callback.

3.1.7 txtorcon.util Module util.NetLocation class txtorcon.util.NetLocation(ipaddr) Bases: object Represents the location of an IP address, either city or country level resolution depending on what GeoIP database was loaded. If the ASN database is available you get that also. ipaddr should be a dotted-quad util.process_from_address util.process_from_address(addr, port, torstate=None) Determines the PID from the address/port provided by using lsof and returns it as an int (or None if it couldn’t be determined). In the special case the addr is ‘(Tor_internal)’ then the PID of the Tor process (as gotten from the torstate object) is returned (or 0 if unavailable, e.g. a Tor which doesn’t implement ‘GETINFO process/pid’). In this case if no TorState instance is given, None is returned. util.delete_file_or_tree util.delete_file_or_tree(*args) For every path in args, try to delete it as a file or a directory tree. Ignores deletion errors.

3.1. API Documentation 69 txtorcon Documentation, Release 1.0.0

70 Chapter 3. API Documentation CHAPTER 4

Indices and tables

• genindex • modindex • search

71 txtorcon Documentation, Release 1.0.0

72 Chapter 4. Indices and tables Index

A circuit_failed() (txtorcon.TorState method), 50 accepts_port() (txtorcon.Router method), 54 circuit_launched() (ICircuitListener method), 67 add_attacher() (txtorcon.TorState method), 48 circuit_launched() (txtorcon.TorState method), 50 add_circuit_listener() (txtorcon.TorState method), 49 circuit_new() (ICircuitListener method), 67 add_event_listener() (ITorControlProtocol method), 69 circuit_new() (txtorcon.TorState method), 50 add_event_listener() (txtorcon.TorControlProtocol circuits (txtorcon.TorState attribute), 47 method), 62 cleanup() (txtorcon.TorProcessProtocol method), 65 add_progress_listener() (IProgressProvider method), 60 clients (IHiddenService attribute), 61 add_progress_listener() (txtor- close() (txtorcon.Circuit method), 52 con.TCPHiddenServiceEndpoint method), close() (txtorcon.Stream method), 53 59 close_circuit() (ICircuitContainer method), 68 add_stream_listener() (txtorcon.TorState method), 49 close_circuit() (ITorControlProtocol method), 69 age() (txtorcon.Circuit method), 52 close_circuit() (txtorcon.TorState method), 48 all_routers (txtorcon.TorState attribute), 47 close_stream() (ICircuitContainer method), 68 attach_protocol() (txtorcon.TorConfig method), 56 close_stream() (txtorcon.TorState method), 48 attach_stream() (IStreamAttacher method), 66 compareAttributes (txtorcon.TorOnionAddress attribute), attach_stream_failure() (IStreamAttacher method), 66 60 authenticate() (txtorcon.TorControlProtocol method), 62 config (txtorcon.Tor attribute), 46 authorities (txtorcon.TorState attribute), 48 config (txtorcon.TorConfig attribute), 55 config_args() (txtorcon.TorConfig method), 57 B connect() (txtorcon method), 47 bandwidth (txtorcon.Router attribute), 54 connectionLost() (txtorcon.TorControlProtocol method), bootstrap() (txtorcon.TorConfig method), 56 63 build_circuit() (ITorControlProtocol method), 69 connectionMade() (txtorcon.TorControlProtocol build_circuit() (txtorcon.TorState method), 49 method), 63 create_onion_disk_endpoint() (txtorcon.Tor method), 47 build_local_tor_connection() (in module txtorcon), 62 create_onion_endpoint() (txtorcon.Tor method), 46 build_tor_connection() (in module txtorcon), 61 buildProtocol() (txtorcon.TorProtocolFactory method), 64 create_socks_endpoint() (txtorcon.TorConfig method), 55 create_state() (txtorcon.Tor method), 47 C create_torrc() (txtorcon.TorConfig method), 57 Circuit (class in txtorcon), 50 D circuit (txtorcon.Stream attribute), 53 delete_file_or_tree() (txtorcon.util method), 69 circuit_built() (ICircuitListener method), 67 dns_resolve() (txtorcon.Tor method), 46 circuit_built() (txtorcon.TorState method), 50 dns_resolve_ptr() (txtorcon.Tor method), 46 circuit_closed() (ICircuitListener method), 67 DO_NOT_ATTACH (txtorcon.TorState attribute), 49 circuit_closed() (txtorcon.TorState method), 50 do_post_bootstrap() (txtorcon.TorConfig method), 56 circuit_destroy() (txtorcon.TorState method), 50 do_post_errback() (txtorcon.TorConfig method), 56 circuit_extend() (ICircuitListener method), 67 doStart() (txtorcon.TorProtocolFactory method), 64 circuit_extend() (txtorcon.TorState method), 50 doStop() (txtorcon.TorProtocolFactory method), 64 circuit_failed() (ICircuitListener method), 67

73 txtorcon Documentation, Release 1.0.0

E listen() (txtorcon.Stream method), 53 entry_guards (txtorcon.TorState attribute), 48 listen() (txtorcon.TCPHiddenServiceEndpoint method), errReceived() (txtorcon.TorProcessProtocol method), 65 59 event_map (txtorcon.TorState attribute), 49 listeners (txtorcon.Stream attribute), 53 local_address (IHiddenService attribute), 61 F location (txtorcon.Router attribute), 54 find_circuit() (ICircuitContainer method), 68 M find_circuit() (txtorcon.TorState method), 49 flags (txtorcon.Router attribute), 54 mark_unsaved() (txtorcon.TorConfig method), 56 flags (txtorcon.Stream attribute), 53 maybe_call_closing_deferred() (txtorcon.Circuit from_protocol() (txtorcon.TorConfig static method), 55 method), 52 from_protocol() (txtorcon.TorState class method), 47 maybe_call_closing_deferred() (txtorcon.Stream method), 54 G modified (txtorcon.Router attribute), 54 get_conf() (ITorControlProtocol method), 68 N get_conf() (txtorcon.TorControlProtocol method), 63 needs_save() (txtorcon.TorConfig method), 56 get_conf_raw() (txtorcon.TorControlProtocol method), NetLocation (class in txtorcon.util), 69 63 get_global_tor() (in module txtorcon), 59 O get_info() (ITorControlProtocol method), 68 get_info() (txtorcon.TorControlProtocol method), 63 on_circuit_built() (txtorcon.TorState method), 49 get_info_incremental() (txtorcon.TorControlProtocol on_circuit_closed() (txtorcon.TorState method), 49 method), 63 on_circuit_extend() (txtorcon.TorState method), 49 get_info_raw() (txtorcon.TorControlProtocol method), 63 on_circuit_failed() (txtorcon.TorState method), 49 get_location() (txtorcon.Router method), 54 on_circuit_launched() (txtorcon.TorState method), 48 get_type() (txtorcon.TorConfig method), 56 on_circuit_new() (txtorcon.TorState method), 48 getHost() (txtorcon.TorOnionListeningPort method), 60 onion_create() (txtorcon.TorConfig method), 56 global_tor() (txtorcon.TCPHiddenServiceEndpoint class onion_key (txtorcon.TorOnionAddress attribute), 60 method), 58 onion_private_key (txtorcon.TCPHiddenServiceEndpoint graphviz_data() (txtorcon.TorControlProtocol method), attribute), 59 63 onion_service (txtorcon.TorOnionListeningPort at- guards (txtorcon.TorState attribute), 48 tribute), 60 onion_uri (txtorcon.TCPHiddenServiceEndpoint at- H tribute), 59 outReceived() (txtorcon.TorProcessProtocol method), 65 HiddenService (in module txtorcon), 57 P I parsers (txtorcon.TorConfig attribute), 55 ICircuitContainer (interface in txtorcon.interface), 68 parseStreamServer() (txtor- ICircuitListener (interface in txtorcon.interface), 67 con.TCPHiddenServiceEndpointParser id (txtorcon.Stream attribute), 53 method), 60 IHiddenService (interface in txtorcon), 61 policy (txtorcon.Router attribute), 54 IProgressProvider (interface in txtorcon), 60 prefix (txtorcon.TCPHiddenServiceEndpointParser at- IRouterContainer (interface in txtorcon.interface), 68 tribute), 60 is_built (txtorcon.Circuit attribute), 51 private_tor() (txtorcon.TCPHiddenServiceEndpoint class IStreamAttacher (interface in txtorcon.interface), 66 method), 59 IStreamListener (interface in txtorcon.interface), 66 process (txtorcon.Tor attribute), 46 ITorControlProtocol (interface in txtorcon.interface), 68 process_from_address() (txtorcon.util method), 69 processEnded() (txtorcon.TorProcessProtocol method), L 65 launch() (txtorcon method), 47 processExited() (txtorcon.TorProcessProtocol method), lineReceived() (txtorcon.TorControlProtocol method), 63 65 list_parsers (txtorcon.TorConfig attribute), 55 progress() (txtorcon.TorProcessProtocol method), 66 listen() (txtorcon.Circuit method), 52 protocol (txtorcon.Tor attribute), 46

74 Index txtorcon Documentation, Release 1.0.0 protocol (txtorcon.TorConfig attribute), 56 stream_via() (txtorcon.Circuit method), 51 protocolinfo() (txtorcon.TorControlProtocol method), 63 stream_via() (txtorcon.Tor method), 46 public_port (IHiddenService attribute), 61 streams (txtorcon.TorState attribute), 47 system_tor() (txtorcon.TCPHiddenServiceEndpoint class Q method), 58 queue_command() (txtorcon.TorControlProtocol method), 63 T quit() (txtorcon.Tor method), 46 target_addr (txtorcon.Stream attribute), 53 quit() (txtorcon.TorControlProtocol method), 64 target_host (txtorcon.Stream attribute), 53 quit() (txtorcon.TorProcessProtocol method), 65 target_port (txtorcon.Stream attribute), 53 TCPHiddenServiceEndpoint (class in txtorcon), 57 R TCPHiddenServiceEndpointParser (class in txtorcon), 59 remove_attacher() (txtorcon.TorState method), 48 time_created (txtorcon.Circuit attribute), 52 remove_event_listener() (txtorcon.TorControlProtocol Tor (class in txtorcon), 45 method), 64 tor_config (IHiddenService attribute), 61 retries (txtorcon.TCPHiddenServiceEndpoint attribute), tor_config (txtorcon.TorOnionListeningPort attribute), 60 59 tor_protocol (txtorcon.TorConfig attribute), 56 Router (class in txtorcon), 54 TorConfig (class in txtorcon), 54 router_from_id() (IRouterContainer method), 68 TorControlProtocol (class in txtorcon), 62 router_from_id() (txtorcon.TorState method), 49 TorOnionAddress (class in txtorcon), 60 routers (txtorcon.TorState attribute), 47 TorOnionListeningPort (class in txtorcon), 60 routers_by_hash (txtorcon.TorState attribute), 48 TorProcessProtocol (class in txtorcon), 65 routers_by_name (txtorcon.TorState attribute), 48 TorProtocolFactory (class in txtorcon), 64 TorState (class in txtorcon), 47 S type (txtorcon.TorOnionAddress attribute), 60 save() (txtorcon.TorConfig method), 56 U set_conf() (txtorcon.TorControlProtocol method), 64 signal() (ITorControlProtocol method), 69 unique_name (txtorcon.Router attribute), 54 signal() (txtorcon.TorControlProtocol method), 64 unique_routers (IRouterContainer attribute), 68 socks_endpoint() (txtorcon.TorConfig method), 55 unlisten() (txtorcon.Circuit method), 52 source_addr (txtorcon.Stream attribute), 53 unlisten() (txtorcon.Stream method), 53 source_port (txtorcon.Stream attribute), 53 unsaved (txtorcon.TorConfig attribute), 55 start_debug() (txtorcon.TorControlProtocol method), 64 unusable_entry_guards (txtorcon.TorState attribute), 48 startListening() (txtorcon.TorOnionListeningPort update() (txtorcon.Circuit method), 52 method), 60 update() (txtorcon.Router method), 54 state (txtorcon.Stream attribute), 53 update() (txtorcon.Stream method), 54 stop_debug() (txtorcon.TorControlProtocol method), 64 update_path() (txtorcon.Circuit method), 52 stopListening() (txtorcon.TorOnionListeningPort method), 60 W Stream (class in txtorcon), 52 web_agent() (txtorcon.Circuit method), 51 stream_attach() (IStreamListener method), 66 web_agent() (txtorcon.Tor method), 46 stream_attach() (txtorcon.TorState method), 50 when_built() (txtorcon.Circuit method), 51 stream_close_reasons (txtorcon.TorState attribute), 48 when_connected() (txtorcon.TorProcessProtocol stream_closed() (IStreamListener method), 67 method), 65 stream_closed() (txtorcon.TorState method), 50 stream_detach() (IStreamListener method), 66 stream_detach() (txtorcon.TorState method), 50 stream_failed() (IStreamListener method), 67 stream_failed() (txtorcon.TorState method), 50 stream_new() (IStreamListener method), 66 stream_new() (txtorcon.TorState method), 49 stream_succeeded() (IStreamListener method), 66 stream_succeeded() (txtorcon.TorState method), 50

Index 75