Pure python tor protocol implementation
A pure python Tor client implementation of the Tor protocol. Torpy can be used to communicate with clearnet hosts or hidden services through the Tor Network.
- No Stem or official Tor client required
- Support v2 hidden services (v2 specification)
- Support Basic and Stealth authorization protocol
- Provide simple TorHttpAdapter for requests library
- Provide simple urllib tor_opener for making requests without any dependencies
- Provide simple Socks5 proxy
If you find this project interesting, you can send some Bitcoins to address:
This product is produced independently from the Tor® anonymity software and carries no guarantee from The Tor Project about quality, suitability or anything else.
There are several console utilities to test the client.
A simple HTTP/HTTPS request:
$ torpy_cli --url https://ifconfig.me --header "User-Agent" "curl/7.37.0" Loading cached NetworkStatusDocument from TorCacheDirStorage: .local/share/torpy/network_status Loading cached DirKeyCertificateList from TorCacheDirStorage: .local/share/torpy/dir_key_certificates Connecting to guard node 220.127.116.11:443 (Poseidon; Tor 0.4.3.6)... (TorClient) Sending: GET https://ifconfig.me Creating new circuit #80000001 with 18.104.22.168:443 (Poseidon; Tor 0.4.3.6) router... ... Building 3 hops circuit... Extending the circuit #80000001 with 22.214.171.124:443 (kren; Tor 0.4.4.5)... ... Extending the circuit #80000001 with 126.96.36.199:443 (Quintex86; Tor 0.4.4.5)... ... Stream #4: creating attached to #80000001 circuit... Stream #4: connecting to ('ifconfig.me', 443) Stream #4: connected (remote ip '188.8.131.52') Stream #4: closing (state = Connected)... Stream #4: remote disconnected (reason = DONE) Response status: 200 Stream #4: closing (state = Closed)... Stream #4: closed already Closing guard connections (TorClient)... Destroy circuit #80000001 Closing guard connections (Router descriptor downloader)... Destroy circuit #80000002 > 184.108.40.206
Create Socks5 proxy to relay requests via the Tor Network:
$ torpy_socks -p 1050 --hops 3 Loading cached NetworkStatusDocument from TorCacheDirStorage: .local/share/torpy/network_status Connecting to guard node 220.127.116.11:9001 (spongebobness; Tor 0.3.5.8)... Creating new circuit #80000001 with 18.104.22.168:9001 (spongebobness; Tor 0.3.5.8) router... Building 3 hops circuit... Extending the circuit #80000001 with 22.214.171.124:9001 (torciusv; Tor 0.3.5.8)... Extending the circuit #80000001 with 126.96.36.199:9005 (che1; Tor 0.4.1.6)... Start socks proxy at 127.0.0.1:1050 ...
Torpy module also has a command-line interface:
$ python3.7 -m torpy --url https://facebookcorewwwi.onion --to-file index.html Loading cached NetworkStatusDocument from TorCacheDirStorage: .local/share/torpy/network_status Connecting to guard node 188.8.131.52:443 (cx10TorServer; Tor 0.4.0.5)... Sending: GET https://facebookcorewwwi.onion Creating new circuit #80000001 with 184.108.40.206:443 (cx10TorServer; Tor 0.4.0.5) router... Building 3 hops circuit... Extending the circuit #80000001 with 220.127.116.11:8447 (TonyBamanaboni; Tor 0.4.1.5)... Extending the circuit #80000001 with 18.104.22.168:9001 (father; Tor 0.4.0.5)... Creating stream #1 attached to #80000001 circuit... Stream #1: connecting to ('facebookcorewwwi.onion', 443) Extending #80000001 circuit for hidden service facebookcorewwwi.onion... Rendezvous established (CellRelayRendezvousEstablished()) Iterate over responsible dirs of the hidden service Iterate over introduction points of the hidden service Create circuit for hsdir Creating new circuit #80000002 with 22.214.171.124:443 (cx10TorServer; Tor 0.4.0.5) router... Building 0 hops circuit... Extending the circuit #80000002 with 126.96.36.199:9001 (toritounam; Tor 0.3.5.8)... Creating stream #2 attached to #80000002 circuit... Stream #2: connecting to hsdir Stream #2: closing... Destroy circuit #80000002 Creating new circuit #80000003 with 188.8.131.52:443 (cx10TorServer; Tor 0.4.0.5) router... Building 0 hops circuit... Extending the circuit #80000003 with 184.108.40.206:8443 (bauruine31; Tor 0.4.1.5)... Introduced (CellRelayIntroduceAck()) Destroy circuit #80000003 Creating stream #3 attached to #80000001 circuit... Stream #3: connecting to ('www.facebookcorewwwi.onion', 443) Extending #80000001 circuit for hidden service facebookcorewwwi.onion... Response status: 200 Writing to file index.html Stream #1: closing... Stream #3: closing... Closing guard connections... Destroy circuit #80000001
A basic example of how to send some data to a clearnet host or a hidden service:
from torpy import TorClient hostname = 'ifconfig.me' # It's possible use onion hostname here as well with TorClient() as tor: # Choose random guard node and create 3-hops circuit with tor.create_circuit(3) as circuit: # Create tor stream to host with circuit.create_stream((hostname, 80)) as stream: # Now we can communicate with host stream.send(b'GET / HTTP/1.0\r\nHost: %s\r\n\r\n' % hostname.encode()) recv = stream.recv(1024)
TorHttpAdapter is a convenient Tor adapter for the requests library. The following example shows the usage of TorHttpAdapter for multi-threaded HTTP requests:
from multiprocessing.pool import ThreadPool from torpy.http.requests import tor_requests_session with tor_requests_session() as s: # returns requests.Session() object links = ['http://nzxj65x32vh2fkhk.onion', 'http://facebookcorewwwi.onion'] * 2 with ThreadPool(3) as pool: pool.map(s.get, links)
For more examples see test_integration.py
pip3 install torpy
- Or for using TorHttpAdapter with requests library you need install extras:
pip3 install torpy[requests]
- Implement v3 hidden services specification
- Refactor Tor cells serialization/deserialization
- More unit tests
- Rewrite the library using asyncio
- Implement onion services
Licensed under the Apache License, Version 2.0
Release history Release notifications | RSS feed
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.