Tracking and Changing Live Tor State


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

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


Shouldn’t Tor handle this by turning this back to 0 if the controller that twiddled it disconnects?

set_attacher(attacher, myreactor)

Provide an txtorcon.interface.IStreamAttacher to associate streams to circuits.

You are Strongly Encouraged to not use this API directly, and instead use txtorcon.Circuit.stream_via() or txtorcon.Circuit.web_agent() instead. If you do need to use this API, it’s an error if you call either of the other two methods.

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).

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
Parameters:callback – will be called (with ‘circuit’ instance) when a CIRC NEW event happens
Parameters:callback – will be called (with ‘circuit’ instance) when a CIRC LAUNCHED event happens
Parameters:callback – will be called (with ‘circuit’ and ‘router’ instances) when a CIRC EXTENDED event happens
Parameters:callback – will be called (with ‘circuit’ instance) when a CIRC BUILT event happens
Parameters:callback – will be called (with ‘circuit’ instance, and arbitrary kwargs) when a CIRC CLOSED event happens
Parameters:callback – will be called (with ‘circuit’ instance, and arbitrary kwargs) when a CIRC FAILED event happens

Adds a new instance of txtorcon.interface.ICircuitListener which will receive updates for all existing and new circuits.

Parameters:callback – will be called (with ‘stream’ instance) when a STREAM NEW event happens.
Parameters:callback – will be called (with ‘stream’ instance) when a STREAM SUCCEEDED event happens.
Parameters:callback – will be called (with ‘stream’ and ‘circuit’ instances) when a stream is attached to a circuit
Parameters:callback – will be called (with ‘stream’ instance and arbitrary kwargs) when a STREAM DETACHED happens
Parameters:callback – will be called (with ‘stream’ instance and arbitrary kwargs) when a STREAM CLOSED event happens.
Parameters:callback – will be called (with ‘stream’ instance and arbitrary kwargs) when a STREAM FAILED event happens.

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, purpose=None)

Builds a circuit consisting of exactly the routers specified, in order. This issues an EXTENDCIRCUIT call to Tor with all the routers specified.

  • 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.

A Deferred that will callback with a Circuit instance (with the .id member being valid, and probably nothing else).

DO_NOT_ATTACH = <object object>
event_map = {'ADDRMAP': '_addr_map', 'CIRC': '_circuit_update', 'NEWCONSENSUS': '_update_network_status', 'STREAM': '_stream_update'}

ICircuitContainer API


IRouterContainer API


IStreamListener: a new stream has been created


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)


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)


ICircuitListener API

circuit_extend(circuit, router)

ICircuitListener API


ICircuitListener API


ICircuitListener API


Used by circuit_closed and circuit_failed (below)

circuit_closed(circuit, **kw)

ICircuitListener API

circuit_failed(circuit, **kw)

ICircuitListener API


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())

  • 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
    • 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. For most purposes, you’ll want to look at GENERAL circuits only. Values can currently be one of (but see control-spec.txt 4.1.1):

  • id – The ID of this circuit, a number (or None if unset).

routercontainer – should implement txtorcon.interface.IRouterContainer.


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.

If the circuit will never hit BUILT (e.g. it is abandoned by Tor before it gets to BUILT) you will receive an errback


Returns a Deferred that callback()’s (with this Circuit instance) when this circuit hits CLOSED or FAILED.

web_agent(reactor, socks_endpoint, pool=None)
stream_via(reactor, host, port, socks_endpoint, use_tls=False)

This returns an IStreamClientEndpoint that will connect to the given host, port 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 via a particular circuit:

def main(reactor):
    circ = yield torstate.build_circuit()  # lets Tor decide the path
    yield circ.when_built()
    tor_ep = circ.stream_via(reactor, '', 443)
    # 'factory' is for your protocol
    proto = yield tor_ep.connect(factory)

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).

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 command. 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.

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.


Used internally to callback on the _closing_deferred if it exists.


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.


class txtorcon.Stream(circuitcontainer, addrmap=None)

Bases: object

Represents an active stream in Tor’s state (txtorcon.TorState).

  • circuit – Streams will generally be attached to circuits pretty quickly. If they are attached, 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
    • 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 – 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.
  • id – The ID of this stream, a number (or None if unset).

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 (otherwise 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


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

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).


Used internally to callback on the _closing_deferred if it exists.


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.


has the hex id if this router’s name is not unique, or its name otherwise


This is the time of ‘the publication time of its most recent descriptor’ (in UTC).

See also dir-spec.txt.

update(name, idhash, orhash, modified, ip, orport, dirport)

Returns a Deferred that fires with a NetLocation object for this router.


A NetLocation instance with some GeoIP or pygeoip information about location, asn, city (if available).

Deprecated in txtorcon 18.0.0.


A list of all the flags for this Router, each one an all-lower-case string.


The reported bandwidth of this Router.


Requests the ‘details’ document from via the given twisted.web.iweb.IAgent – you can get a suitable instance to pass here by calling either txtorcon.Tor.web_agent() or txtorcon.Circuit.web_agent().


Port policies for this Router. :return: a string describing the policy


Query whether this Router will accept the given port.