txtorcon

repository·main·Indexed 42 days ago

https://github.com/meejah/txtorcon

A Python library for interacting with the Tor control protocol using the Twisted networking engine. It enables event-driven applications to control Tor, manage circuits, create onion services, and perform web requests via Tor using Twisted Agent or treq.

Tokens
12.4K
Snippets
32
Records
84
Agent score
54%

What's inside txtorcon

  1. Overview of txtorcon

    main

    txtorcon is a Python library (supporting Py2, PyPy, and Py3) that implements the Tor control-spec. It uses the Twisted networking library to provide a live view of Tor state and allow control over most aspects of Tor's operation.

    Key capabilities include:

    • Launching and controlling Tor instances (including Tor Browser Bundle).
    • Connecting to existing Tor instances.
    • Using Tor as a client (via SOCKS5) or setting up Onion services.
    • Tracking live state (active circuits, streams, and routers/relays).
    • Performing DNS via Tor.
    • Building custom circuits and listening for Tor EVENTs.
    • Providing Twisted endpoint support via tor: and onion: plugins.
  2. Track and change live Tor state with txtorcon

    main
    Use txtorcon to interact with a running Tor instance to monitor and manipulate its live state. The library provides high-level abstractions for the core components of a Tor connection: TorState, Circuit, Stream, and Router.
  3. Use non-authenticated Onion services

    main

    For Onion services that do not require authentication, use the following interfaces:

    • IOnionService: The base interface for Onion services.
    • IFilesystemOnionService: An interface for Onion services that persist their configuration and keys to the filesystem.
  4. How to interact with Tor using the Tor class

    main

    The primary way to interact with Tor via txtorcon is through the Tor class. You should avoid instantiating other classes directly; instead, obtain a Tor instance and use it to access or create other components.

    To get a Tor instance, use one of two methods:

    1. txtorcon.connect(): Connects to an already-running Tor instance (supports COOKIE authentication by default, falling back to password authentication).
    2. txtorcon.launch(): Launches a new Tor instance managed by txtorcon.

    Once you have a Tor instance, you can use it to:

    • Make client-type connections over Tor.
    • Change Tor's configuration.
    • Monitor Tor's state.
    • Offer hidden-/onion- services.
    • Create and use custom circuits.
    • Issue low-level protocol commands.
    import txtorcon
    
    # To connect to an existing Tor instance
    defree = txtorcon.connect()
    # Returns a Deferred that fires with a Tor instance
  5. Understand API stability in txtorcon

    main

    To ensure your code remains compatible across releases, follow these rules regarding the txtorcon API:

    • Stable APIs: Any method or class in an interface class (prefixed with I, e.g., IAnInterface) is a stable, public API and is guaranteed to maintain backwards compatibility.
    • Private APIs: Any method or class prefixed with an underscore (e.g., _method or _ClassName) is private. The implementation and API for these may change at any time and should not be used.
    • Unstable APIs: Currently, the hidden-/onion-services APIs are not considered stable and may change.
    • Deprecation: APIs scheduled for removal will be marked as deprecated for at least one major release before being removed. Some attributes without underscores may be deprecated via the @property decorator to maintain compatibility.
  6. Understand Onion Service types and interfaces

    main

    Onion Services (formerly Hidden Services) in txtorcon are categorized by how their private keys are stored and whether they require authentication.

    Key Storage Types

    • Ephemeral: Keys are kept in memory only. If Tor shuts down, the keys are lost and the service URI will change. You must manually save these keys if you want to persist them.
    • On-disk (Filesystem): Keys are stored in a 'hidden service directory' on disk, allowing the service to persist across Tor restarts.

    Authentication Types

    • No Authentication: Anyone with the .onion address can connect.
    • Basic Authentication: Requires a secret authentication token. Supports many clients.
    • Stealth Authentication: Requires a unique URL and a secret token for each client. Highly secure but less scalable (limited to ~16 clients in recent Tor versions).

    Interface Mapping

    When you call .listen() on an endpoint, the returned IListeningPort object has an .onion_service property. The interface implemented by that property depends on your configuration:

    Service TypeKeys on DiskKeys in Memory
    No Authenticationtxtorcon.IFilesystemOnionServicetxtorcon.IOnionService
    Basic/Stealth Authtxtorcon.IAuthenticatedOnionClientstxtorcon.IAuthenticatedOnionClients
  7. Understand txtorcon's attribute vs method style

    main

    The library follows a specific convention to distinguish between data access and asynchronous operations:

    • Attributes: Used for "mere data" that is immediately available. If the information is already known and doesn't require a network call to Tor, it is an attribute (e.g., .hex_id).
    • Methods: Used for things that "take work" or are asynchronous. If the operation requires querying Tor via the control protocol (and thus returns a Deferred), it is a method (e.g., .get_location()).
  8. Monitor and change Tor's state with TorState

    main

    A TorState instance provides a live, interactive view of all active relays (Router), circuits (Circuit), and streams (Stream) in the underlying Tor instance. Because TorState subscribes to events, it represents an "as up-to-date as possible" view.

    Accessing State

    A Tor instance does not have a TorState by default. You must create one using the asynchronous method get_state().

    Note on Synchronization: If you require absolute certainty that networking buffers are clear and the state is fully up-to-date, you can issue a no-op command via TorControlProtocol.queue_command (e.g., yield queue_command("GETINFO version")).

    Modifying State

    You can interactively modify Tor's state:

    • Close connections: Call Stream.close() or Circuit.close().
    • Wait for readiness: Use Circuit.when_built() to wait for a circuit to become usable.
    • Force specific paths: Use Circuit.stream_via() to return an IStreamClientEndpoint that forces a subsequent .connect() to use a specific circuit. Similarly, use Circuit.web_agent() for Web requests.
    # Accessing the live state
    state = await tor_instance.get_state()
    
    # Example: Closing a circuit
    await circuit.close()
    
    # Example: Waiting for a circuit to be ready
    await circuit.when_built()
  9. Use authenticated Onion services

    main

    For Onion services requiring authentication, the following interfaces are implemented by both ephemeral and disk-based services:

    • IAuthenticatedOnionClients: Interface for managing authenticated clients.
    • IOnionClient: Interface for interacting with an Onion service as an authenticated client.
  10. Implement custom stream-to-circuit mapping

    main

    If you need fine-grained control over how every new stream is assigned to a circuit, you can implement a low-level IStreamAttacher.

    How it works

    1. txtorcon sets __LeaveStreamsUnattached 1 in Tor's configuration.
    2. txtorcon listens for STREAM events.
    3. txtorcon calls your attacher's attach_stream method.
    4. You return the circuit you want the stream to use (or tell Tor to handle it normally).

    Implementation Steps

    1. Create a class implementing IStreamAttacher.
    2. Implement the attach_stream(stream, circuits) method. This method receives the Stream instance and a list of all available Circuit objects.
    3. Register it using TorState.set_attacher(attacher_instance).

    Important Constraints

    • Single Attacher: There can only be one active attacher. Calling set_attacher while an existing attacher is active (including the internal one used by high-level APIs) will result in an error.
    • Unsetting: To remove an attacher, call set_attacher(None). This resets __LeaveStreamsUnattached to 0.
    • Multiple Attachers: If you need multiple logic layers, use the PriorityAttacher utility class as the top-level attacher.
    • Onion Services: Tor does not currently allow controllers to attach circuits destined for onion services.
    # Conceptual implementation of a custom attacher
    from txtorcon.interface import IStreamAttacher
    
    class MyAttacher(IStreamAttacher):
        def attach_stream(self, stream, circuits):
            # Logic to pick a circuit from the list
            return circuits[0]
    
    # Registering the attacher
    tor_state.set_attacher(MyAttacher())
  11. Understand the txtorcon.interface module abstractions

    main

    The txtorcon.interface module defines the core abstract interfaces used to interact with Tor via the control protocol. These interfaces allow developers to implement custom logic for handling streams, circuits, and routing, or to create custom implementations of the Tor control protocol itself.

    Key interfaces include:

    • IStreamAttacher: For attaching to existing streams.
    • IStreamListener: For listening to stream events.
    • ICircuitListener: For listening to circuit events.
    • ICircuitContainer: For managing and accessing circuits.
    • IRouterContainer: For managing and accessing routers.
    • ITorControlProtocol: The high-level interface for the Tor control protocol.

    Note: For Onion-specific interfaces, refer to the onion_api documentation.