1#!/usr/local/bin/python3.8
2
3# Here we use some very new Tor configuration options to set up a
4# "single-hop" or "non-anonymous" onion service. These do NOT give the
5# server location-privacy, so may be appropriate for certain kinds of
6# services. Once you publish a service like this, there's no going
7# back to location-hidden.
8
9from __future__ import print_function
10from twisted.internet import defer, task, endpoints
11from twisted.web import server, resource
12
13import txtorcon
14
15
16class Simple(resource.Resource):
17    """
18    A really simple Web site.
19    """
20    isLeaf = True
21
22    def render_GET(self, request):
23        return b"<html>Hello, world! I'm a single-hop onion service!</html>"
24
25
26@defer.inlineCallbacks
27def main(reactor):
28    # For the "single_hop=True" below to work, the Tor we're
29    # connecting to must have the following options set:
30    # SocksPort 0
31    # HiddenServiceSingleHopMode 1
32    # HiddenServiceNonAnonymousMode 1
33
34    tor = yield txtorcon.connect(
35        reactor,
36        endpoints.TCP4ClientEndpoint(reactor, "localhost", 9351),
37    )
38    if False:
39        ep = tor.create_onion_endpoint(
40            80,
41            version=3,
42            single_hop=True,
43        )
44    else:
45        ep = endpoints.serverFromString(reactor, "onion:80:version=3:singleHop=true")
46
47    def on_progress(percent, tag, msg):
48        print('%03d: %s' % (percent, msg))
49    txtorcon.IProgressProvider(ep).add_progress_listener(on_progress)
50
51    port = yield ep.listen(server.Site(Simple()))
52    print("Private key:\n{}".format(port.getHost().onion_key))
53    hs = port.onion_service
54    print("Site on http://{}".format(hs.hostname))
55    yield defer.Deferred()  # wait forever
56
57
58task.react(main)
59