# -*- mode: python -*- | |
from __future__ import print_function | |
import sys | |
import functools | |
from tempfile import mkdtemp | |
from os.path import dirname, exists, join | |
from os import mkdir | |
from StringIO import StringIO | |
import txtorcon | |
from twisted.application import service, internet | |
from twisted.internet import reactor, defer | |
from twisted.internet.endpoints import TCP4ClientEndpoint | |
from twisted.logger import Logger | |
from twisted.web import static, server | |
from zope.interface import implementer | |
@implementer(service.IService) | |
class TorService(service.Service): | |
""" | |
This IService launches a Tor instance and keeps it running. | |
""" | |
log = Logger() | |
def __init__(self, base_dir): | |
if not exists(base_dir): | |
raise ValueError( | |
"'{}' does not exist".format(base_dir) | |
) | |
data_dir = join(base_dir, 'tor_datadir') | |
if not exists(data_dir): | |
mkdir(data_dir, 0700) | |
self._data_directory = data_dir | |
self._control_path = join(base_dir, 'tor_controlsocket') | |
self._launched = txtorcon.util.SingleObserver() | |
@defer.inlineCallbacks | |
def startService(self): | |
service.Service.startService(self) | |
stdout = StringIO() | |
stderr = StringIO() | |
try: | |
self._tor = yield txtorcon.launch( | |
reactor, | |
stdout=stdout, | |
stderr=stderr, | |
data_directory=self._data_directory, | |
control_port='unix:{}'.format(self._control_path), | |
progress_updates=self._updates, | |
) | |
except ValueError as e: | |
print("Failed to launch Tor: {}".format(e)) | |
reactor.stop() | |
except RuntimeError as e: | |
print("Failed to launch Tor:", file=sys.stderr) | |
print(stdout.getvalue(), file=sys.stderr) | |
print(stderr.getvalue(), file=sys.stderr) | |
print("Tor successfully launched in '{}'".format(self._data_directory)) | |
self._launched.fire(self._tor) | |
def when_launched(self): | |
return self._launched.when_fired() | |
def _updates(self, prog, tag, summary): | |
self.log.info('%d%%: %s' % (prog, summary)) | |
@implementer(service.IService) | |
class TorOnionService(service.Service): | |
""" | |
""" | |
def __init__(self, basedir, tor_service): | |
# should we have *this* class create its own TorService? (in | |
# startService, not here though) | |
self._basedir = basedir | |
self._tor = tor_service | |
self._private_key = None | |
key_filename = join(basedir, 'service_key') | |
if exists(key_filename): | |
print("found {}".format(key_filename)) | |
with open(key_filename, 'r') as f: | |
self._private_key = f.read().strip() | |
@defer.inlineCallbacks | |
def startService(self): | |
tor = yield self._tor.when_launched() | |
print("Creating hidden-service") | |
ep = tor.create_onion_endpoint(80, private_key=self._private_key) | |
res = static.File( | |
join(self._basedir, 'git', 'doc', '_build', 'html'), | |
ignoredExts=('~'), | |
) | |
site = server.Site( | |
resource=res, | |
# logFormatter=lambda *args, **kw: 'request served', | |
) | |
port = yield ep.listen(site) | |
if self._private_key is None: | |
privkey = port.onion_service.private_key | |
privkey_fname = join(self._basedir, 'service_key') | |
with open(privkey_fname, 'w') as f: | |
f.write(privkey) | |
print("Wrote private key to '{}'".format(privkey_fname)) | |
application = service.Application("Txtorcon Application") | |
torservice = TorService('/tmp/foo') | |
docservice = TorOnionService('/tmp/foo', torservice) | |
torservice.setServiceParent(application) | |
docservice.setServiceParent(application) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment