ckbunker/torsion.py
2020-02-14 10:42:15 -05:00

152 lines
4.7 KiB
Python

#!/usr/bin/env python
#
# Interface to STEM and from that to Tord and the Tor network.
#
# Refs:
# - <https://stem.torproject.org/tutorials/over_the_river.html#ephemeral-hidden-services>
#
import logging, asyncio
from utils import json_loads, json_dumps, Singleton
from concurrent.futures import ThreadPoolExecutor
from persist import settings
from status import STATUS
logging.getLogger(__name__).addHandler(logging.NullHandler())
executor = ThreadPoolExecutor(max_workers=10)
class TorViaStem(metaclass=Singleton):
def __init__(self):
self.controller = None
self.service = None
async def startup(self):
# just test if we can see tord
await self.connect(raise_on_error=False)
def get_current_addr(self):
# return onion address we are currently on, or None
if not self.service:
return None
return self.service.service_id + '.onion'
async def connect(self, raise_on_error=True):
from stem.connection import connect
if self.controller:
return self.controller
def doit():
self.controller = connect(control_port=('127.0.0.1', settings.TORD_PORT))
if self.controller:
logging.info("Tord version: " + str(self.controller.get_version()))
else:
logging.error("Unable to connect to local 'tord' server")
if raise_on_error:
raise RuntimeError("No local 'tord' server")
return self.controller
loop = asyncio.get_running_loop()
rv = await loop.run_in_executor(executor, doit)
STATUS.tord_good = bool(rv)
STATUS.notify_watchers()
async def pick_onion_addr(self):
c = await self.connect()
def doit():
# let Tor pick the key, since they don't document their tricky stuff
s = self.controller.create_ephemeral_hidden_service({80: 1},
detached=False,
await_publication=False, key_content='ED25519-V3')
rv = (s.service_id+'.onion', s.private_key)
# kill it immediately
self.controller.remove_ephemeral_hidden_service(s.service_id)
return rv
loop = asyncio.get_running_loop()
return await loop.run_in_executor(executor, doit)
async def stop_tunnel(self):
# hang up if running
if not self.service:
return
def doit():
if self.service:
logging.info(f"Disconnecting previous service at: {self.service.service_id}.onion")
self.controller.remove_ephemeral_hidden_service(self.service.service_id)
self.service = None
STATUS.onion_addr = None
STATUS.notify_watchers()
loop = asyncio.get_running_loop()
return await loop.run_in_executor(executor, doit)
async def start_tunnel(self):
from persist import BP, settings
c = await self.connect()
def doit():
if self.service:
logging.info(f"Disconnecting previous service at: {self.service.service_id}.onion")
self.controller.remove_ephemeral_hidden_service(self.service.service_id)
self.service = None
# give Tor the key from earlier run
k = BP['onion_pk']
s = self.controller.create_ephemeral_hidden_service({80: settings.PORT_NUMBER},
detached=False, discard_key=True,
await_publication=True, key_type='ED25519-V3', key_content=k)
addr = s.service_id+'.onion'
assert addr == BP['onion_addr'], f"Mismatch, got: {addr} not {BP.onion_addr} expected"
self.service = s
return addr
loop = asyncio.get_running_loop()
addr = await loop.run_in_executor(executor, doit)
STATUS.onion_addr = addr
STATUS.notify_watchers()
TOR = TorViaStem()
if __name__ == '__main__':
controller = connect()
if not controller:
sys.exit(1) # unable to get a connection
print('Your tord is version: %s' % controller.get_version())
#d = controller.get_hidden_service_descriptor('explorernuoc63nb.onion')
d = controller.get_hidden_service_descriptor('explorerzydxu5ecjrkwceayqybizmpjjznk5izmitf2modhcusuqlid.onion')
print("obj = %r" % d)
print("pubkey = %r" % d.permanent_key)
print("published = %r" % d.published)
service = controller.create_ephemeral_hidden_service({80: 5000}, await_publication = True, key_content = 'ED25519-V3')
print("Started a new hidden service with the address of %s.onion" % service.service_id)
print('%s %s' % (service.private_key_type, service.private_key))
controller.close()
# EOF