<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>Over the River and Through the Wood — Stem 1.8.1-maint documentation</title> <link rel="stylesheet" href="../_static/haiku.css" type="text/css" /> <link rel="stylesheet" href="../_static/pygments.css" type="text/css" /> <link rel="stylesheet" href="../_static/style.css" type="text/css" /> <script type="text/javascript"> var DOCUMENTATION_OPTIONS = { URL_ROOT: '../', VERSION: '1.8.1-maint', COLLAPSE_INDEX: false, FILE_SUFFIX: '.html', HAS_SOURCE: true }; </script> <script type="text/javascript" src="../_static/jquery.js"></script> <script type="text/javascript" src="../_static/underscore.js"></script> <script type="text/javascript" src="../_static/doctools.js"></script> <link rel="shortcut icon" href="../_static/favicon.png"/> <link rel="top" title="Stem 1.8.1-maint documentation" href="../index.html" /> </head> <body> <div class="header"><img class="rightlogo" src="../_static/logo.png" alt="Logo"/><h1 class="heading"><a href="../index.html"> <span>Stem Docs</span></a></h1> <h2 class="heading"><span>Over the River and Through the Wood</span></h2> </div> <div class="topnav"> <p> <ul id="navbar"> <li><a href="../index.html">Home</a></li> <li><a href="../tutorials.html">Tutorials</a> <ul> <li><a href="the_little_relay_that_could.html">Hello World</a></li> <li><a href="to_russia_with_love.html">Client Usage</a></li> <li><a href="tortoise_and_the_hare.html">Event Listening</a></li> <li><a href="#">Hidden Services</a></li> <li><a href="mirror_mirror_on_the_wall.html">Tor Descriptors</a></li> <li><a href="east_of_the_sun.html">Utilities</a></li> <li><a href="down_the_rabbit_hole.html">Interpreter</a></li> <li><a href="double_double_toil_and_trouble.html">Examples</a></li> </ul> </li> <li><a href="../api.html">API</a> <ul> <li><a href="../api/control.html">stem.control</a></li> <li><a href="../api/connection.html">stem.connection</a></li> <li><a href="../api/socket.html">stem.socket</a></li> <li><a href="../api/process.html">stem.process</a></li> <li><a href="../api/response.html">stem.response</a></li> <li><a href="../api/exit_policy.html">stem.exit_policy</a></li> <li><a href="../api/version.html">stem.version</a></li> <li><a href="../api.html#descriptors">Descriptors</a></li> <li><a href="../api.html#utilities">Utilities</a></li> </ul> </li> <li><a href="https://trac.torproject.org/projects/tor/wiki/doc/stem">Development</a> <ul> <li><a href="../faq.html">FAQ</a></li> <li><a href="../change_log.html">Change Log</a></li> <li><a href="https://github.com/torproject/stem/issues/">Bug Tracker</a></li> <li><a href="https://jenkins.torproject.org/job/stem-tor-ci/">Jenkins</a></li> <li><a href="../download.html">Download</a></li> </ul> </li> <li><a href="../faq.html#where-can-i-get-help">Contact</a> <ul> <li><a href="https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-dev">Email List</a></li> <li><a href="https://www.torproject.org/about/contact.html.en#irc">IRC</a></li> <li><a href="https://www.atagar.com/contact/">Author</a></li> </ul> </li> </ul> </p> </div> <div class="content"> <div class="section" id="over-the-river-and-through-the-wood"> <h1>Over the River and Through the Wood<a class="headerlink" href="#over-the-river-and-through-the-wood" title="Permalink to this headline">¶</a></h1> <p><a class="reference external" href="https://www.torproject.org/docs/hidden-services.html.en">Hidden services</a> give you a way of providing a service without exposing your address. These services are only accessible through Tor or <a class="reference external" href="https://tor2web.org/">Tor2web</a>, and useful for a surprising number of things...</p> <ul class="simple"> <li><strong>Hosting an anonymized site</strong>. This is usually the first thing that comes to mind, and something we'll demonstrate in a sec.</li> <li>Providing an <strong>endpoint Tor users can reach</strong> without exiting the Tor network. This eliminates the risk of an unreliable or malicious exit getting in the way. Great examples of this are <a class="reference external" href="http://arstechnica.com/security/2014/10/facebook-offers-hidden-service-to-tor-users/">Facebook</a> (<em>facebookcorewwwi.onion</em>) and <a class="reference external" href="https://lists.torproject.org/pipermail/tor-talk/2010-August/003095.html">DuckDuckGo</a> (<em>3g2upl4pq6kufc4m.onion</em>).</li> <li><strong>Personal services</strong>. For instance you can host your home SSH server as a hidden service to prevent eavesdroppers from knowing where you live while traveling abroad.</li> </ul> <p><a class="reference external" href="https://tor2web.org/">Tor2web</a> provides a quick and easy way of seeing if your hidden service is working. To use it simply replace the <strong>.onion</strong> of your address with <strong>.tor2web.org</strong>...</p> <a class="reference external image-reference" href="https://3g2upl4pq6kufc4m.tor2web.org/"><img alt="../_images/duck_duck_go_hidden_service.png" src="../_images/duck_duck_go_hidden_service.png" /></a> <div class="section" id="running-a-hidden-service"> <span id="id2"></span><h2>Running a hidden service<a class="headerlink" href="#running-a-hidden-service" title="Permalink to this headline">¶</a></h2> <p>Hidden services can be <a class="reference external" href="https://www.torproject.org/docs/tor-manual.html.en#_hidden_service_options">configured through your torrc</a>, but Stem also provides some methods to easily work with them...</p> <blockquote> <div><ul class="simple"> <li><tt class="xref py py-func docutils literal"><span class="pre">create_hidden_service()</span></tt></li> <li><tt class="xref py py-func docutils literal"><span class="pre">remove_hidden_service()</span></tt></li> <li><tt class="xref py py-func docutils literal"><span class="pre">get_hidden_service_conf()</span></tt></li> <li><tt class="xref py py-func docutils literal"><span class="pre">set_hidden_service_conf()</span></tt></li> </ul> </div></blockquote> <p>The main threat to your anonymity when running a hidden service is the service itself. Debug information for instance might leak your real address, undermining what Tor provides. This includes the following example, <strong>do not rely on it not to leak</strong>.</p> <p>But with that out of the way lets take a look at a simple <a class="reference external" href="http://flask.pocoo.org/">Flask</a> example based on one by <a class="reference external" href="https://jordan-wright.github.io/blog/2014/10/06/creating-tor-hidden-services-with-python/">Jordan Wright</a>...</p> <div class="highlight-python"><div class="highlight"><pre><span class="kn">import</span> <span class="nn">os</span> <span class="kn">import</span> <span class="nn">shutil</span> <span class="kn">from</span> <span class="nn">stem.control</span> <span class="kn">import</span> <span class="n">Controller</span> <span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">Flask</span> <span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span> <span class="nd">@app.route</span><span class="p">(</span><span class="s">'/'</span><span class="p">)</span> <span class="k">def</span> <span class="nf">index</span><span class="p">():</span> <span class="k">return</span> <span class="s">"<h1>Hi Grandma!</h1>"</span> <span class="k">print</span><span class="p">(</span><span class="s">' * Connecting to tor'</span><span class="p">)</span> <span class="k">with</span> <span class="n">Controller</span><span class="o">.</span><span class="n">from_port</span><span class="p">()</span> <span class="k">as</span> <span class="n">controller</span><span class="p">:</span> <span class="n">controller</span><span class="o">.</span><span class="n">authenticate</span><span class="p">()</span> <span class="c"># All hidden services have a directory on disk. Lets put ours in tor's data</span> <span class="c"># directory.</span> <span class="n">hidden_service_dir</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">controller</span><span class="o">.</span><span class="n">get_conf</span><span class="p">(</span><span class="s">'DataDirectory'</span><span class="p">,</span> <span class="s">'/tmp'</span><span class="p">),</span> <span class="s">'hello_world'</span><span class="p">)</span> <span class="c"># Create a hidden service where visitors of port 80 get redirected to local</span> <span class="c"># port 5000 (this is where Flask runs by default).</span> <span class="k">print</span><span class="p">(</span><span class="s">" * Creating our hidden service in </span><span class="si">%s</span><span class="s">"</span> <span class="o">%</span> <span class="n">hidden_service_dir</span><span class="p">)</span> <span class="n">result</span> <span class="o">=</span> <span class="n">controller</span><span class="o">.</span><span class="n">create_hidden_service</span><span class="p">(</span><span class="n">hidden_service_dir</span><span class="p">,</span> <span class="mi">80</span><span class="p">,</span> <span class="n">target_port</span> <span class="o">=</span> <span class="mi">5000</span><span class="p">)</span> <span class="c"># The hostname is only available when we can read the hidden service</span> <span class="c"># directory. This requires us to be running with the same user as tor.</span> <span class="k">if</span> <span class="n">result</span><span class="o">.</span><span class="n">hostname</span><span class="p">:</span> <span class="k">print</span><span class="p">(</span><span class="s">" * Our service is available at </span><span class="si">%s</span><span class="s">, press ctrl+c to quit"</span> <span class="o">%</span> <span class="n">result</span><span class="o">.</span><span class="n">hostname</span><span class="p">)</span> <span class="k">else</span><span class="p">:</span> <span class="k">print</span><span class="p">(</span><span class="s">" * Unable to determine our service's hostname, probably due to being unable to read the hidden service directory"</span><span class="p">)</span> <span class="k">try</span><span class="p">:</span> <span class="n">app</span><span class="o">.</span><span class="n">run</span><span class="p">()</span> <span class="k">finally</span><span class="p">:</span> <span class="c"># Shut down the hidden service and clean it off disk. Note that you *don't*</span> <span class="c"># want to delete the hidden service directory if you'd like to have this</span> <span class="c"># same *.onion address in the future.</span> <span class="k">print</span><span class="p">(</span><span class="s">" * Shutting down our hidden service"</span><span class="p">)</span> <span class="n">controller</span><span class="o">.</span><span class="n">remove_hidden_service</span><span class="p">(</span><span class="n">hidden_service_dir</span><span class="p">)</span> <span class="n">shutil</span><span class="o">.</span><span class="n">rmtree</span><span class="p">(</span><span class="n">hidden_service_dir</span><span class="p">)</span> </pre></div> </div> <p>Now if we run this...</p> <div class="highlight-python"><pre>% python example.py * Connecting to tor * Creating our hidden service in /home/atagar/.tor/hello_world * Our service is available at uxiuaxejc3sxrb6i.onion, press ctrl+c to quit * Running on http://127.0.0.1:5000/ 127.0.0.1 - - [15/Dec/2014 13:05:43] "GET / HTTP/1.1" 200 - * Shutting down our hidden service</pre> </div> <p>... we'll have a service we can visit via the <a class="reference external" href="https://www.torproject.org/download/download-easy.html.en">Tor Browser Bundle</a>...</p> <img alt="../_images/hidden_service.png" src="../_images/hidden_service.png" /> </div> <div class="section" id="hidden-service-authentication"> <span id="id3"></span><h2>Hidden service authentication<a class="headerlink" href="#hidden-service-authentication" title="Permalink to this headline">¶</a></h2> <p>Hidden services you create can restrict their access, requiring in essence a password...</p> <div class="highlight-python"><div class="highlight"><pre><span class="gp">>>> </span><span class="kn">from</span> <span class="nn">stem.control</span> <span class="kn">import</span> <span class="n">Controller</span> <span class="gp">>>> </span><span class="n">controller</span> <span class="o">=</span> <span class="n">Controller</span><span class="o">.</span><span class="n">from_port</span><span class="p">()</span> <span class="gp">>>> </span><span class="n">controller</span><span class="o">.</span><span class="n">authenticate</span><span class="p">()</span> <span class="gp">>>> </span><span class="n">response</span> <span class="o">=</span> <span class="n">controller</span><span class="o">.</span><span class="n">create_ephemeral_hidden_service</span><span class="p">({</span><span class="mi">80</span><span class="p">:</span> <span class="mi">8080</span><span class="p">},</span> <span class="n">await_publication</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span> <span class="n">basic_auth</span><span class="o">=</span><span class="p">{</span><span class="s">'bob'</span><span class="p">:</span> <span class="bp">None</span><span class="p">,</span> <span class="s">'alice'</span><span class="p">:</span> <span class="bp">None</span><span class="p">})</span> <span class="gp">>>> </span><span class="n">response</span><span class="o">.</span><span class="n">service_id</span><span class="p">,</span> <span class="n">response</span><span class="o">.</span><span class="n">client_auth</span> <span class="go">('l3lnorirzn7hrjnw', {'alice': 'I6AMKiay+UkM5MfrvdnF2A', 'bob': 'VLsbrSGyrb5JYEvZmQ3tMg'})</span> </pre></div> </div> <p>To access this service users simply provide this credential to tor via their torrc or SETCONF prior to visiting it...</p> <div class="highlight-python"><div class="highlight"><pre><span class="gp">>>> </span><span class="n">controller</span><span class="o">.</span><span class="n">set_conf</span><span class="p">(</span><span class="s">'HidServAuth'</span><span class="p">,</span> <span class="s">'l3lnorirzn7hrjnw.onion I6AMKiay+UkM5MfrvdnF2A'</span><span class="p">)</span> </pre></div> </div> </div> <div class="section" id="ephemeral-hidden-services"> <span id="id4"></span><h2>Ephemeral hidden services<a class="headerlink" href="#ephemeral-hidden-services" title="Permalink to this headline">¶</a></h2> <p>In the above example you may have noticed the note that said...</p> <div class="highlight-python"><div class="highlight"><pre><span class="c"># The hostname is only available when we can read the hidden service</span> <span class="c"># directory. This requires us to be running with the same user as tor.</span> </pre></div> </div> <p>This has been a limitation of hidden services for years. However, as of version 0.2.7.1 Tor offers another style for making services called <strong>ephemeral hidden services</strong>.</p> <p>Ephemeral services can only be created through the controller, and only exist as long as your controller is attached unless you provide the <strong>detached</strong> flag. Controllers can only see their own ephemeral services, and ephemeral services that are detached. In other words, attached ephemeral services can only be managed by their own controller.</p> <p>Stem provides three methods to work with ephemeral hidden services...</p> <blockquote> <div><ul class="simple"> <li><tt class="xref py py-func docutils literal"><span class="pre">list_ephemeral_hidden_services()</span></tt></li> <li><tt class="xref py py-func docutils literal"><span class="pre">create_ephemeral_hidden_service()</span></tt></li> <li><tt class="xref py py-func docutils literal"><span class="pre">remove_ephemeral_hidden_service()</span></tt></li> </ul> </div></blockquote> <p>For example, with a ephemeral service our earlier example becomes as simple as...</p> <div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">stem.control</span> <span class="kn">import</span> <span class="n">Controller</span> <span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">Flask</span> <span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span> <span class="nd">@app.route</span><span class="p">(</span><span class="s">'/'</span><span class="p">)</span> <span class="k">def</span> <span class="nf">index</span><span class="p">():</span> <span class="k">return</span> <span class="s">"<h1>Hi Grandma!</h1>"</span> <span class="k">print</span><span class="p">(</span><span class="s">' * Connecting to tor'</span><span class="p">)</span> <span class="k">with</span> <span class="n">Controller</span><span class="o">.</span><span class="n">from_port</span><span class="p">()</span> <span class="k">as</span> <span class="n">controller</span><span class="p">:</span> <span class="n">controller</span><span class="o">.</span><span class="n">authenticate</span><span class="p">()</span> <span class="c"># Create a hidden service where visitors of port 80 get redirected to local</span> <span class="c"># port 5000 (this is where Flask runs by default).</span> <span class="n">response</span> <span class="o">=</span> <span class="n">controller</span><span class="o">.</span><span class="n">create_ephemeral_hidden_service</span><span class="p">({</span><span class="mi">80</span><span class="p">:</span> <span class="mi">5000</span><span class="p">},</span> <span class="n">await_publication</span> <span class="o">=</span> <span class="bp">True</span><span class="p">)</span> <span class="k">print</span><span class="p">(</span><span class="s">" * Our service is available at </span><span class="si">%s</span><span class="s">.onion, press ctrl+c to quit"</span> <span class="o">%</span> <span class="n">response</span><span class="o">.</span><span class="n">service_id</span><span class="p">)</span> <span class="k">try</span><span class="p">:</span> <span class="n">app</span><span class="o">.</span><span class="n">run</span><span class="p">()</span> <span class="k">finally</span><span class="p">:</span> <span class="k">print</span><span class="p">(</span><span class="s">" * Shutting down our hidden service"</span><span class="p">)</span> </pre></div> </div> <p>Ephemeral hidden services do not touch disk, and as such are easier to work with but require you to persist your service's private key yourself if you want to reuse a '.onion' address...</p> <div class="highlight-python"><div class="highlight"><pre><span class="kn">import</span> <span class="nn">os</span> <span class="kn">from</span> <span class="nn">stem.control</span> <span class="kn">import</span> <span class="n">Controller</span> <span class="n">key_path</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">expanduser</span><span class="p">(</span><span class="s">'~/my_service_key'</span><span class="p">)</span> <span class="k">with</span> <span class="n">Controller</span><span class="o">.</span><span class="n">from_port</span><span class="p">()</span> <span class="k">as</span> <span class="n">controller</span><span class="p">:</span> <span class="n">controller</span><span class="o">.</span><span class="n">authenticate</span><span class="p">()</span> <span class="k">if</span> <span class="ow">not</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">exists</span><span class="p">(</span><span class="n">key_path</span><span class="p">):</span> <span class="n">service</span> <span class="o">=</span> <span class="n">controller</span><span class="o">.</span><span class="n">create_ephemeral_hidden_service</span><span class="p">({</span><span class="mi">80</span><span class="p">:</span> <span class="mi">5000</span><span class="p">},</span> <span class="n">await_publication</span> <span class="o">=</span> <span class="bp">True</span><span class="p">)</span> <span class="k">print</span><span class="p">(</span><span class="s">"Started a new hidden service with the address of </span><span class="si">%s</span><span class="s">.onion"</span> <span class="o">%</span> <span class="n">service</span><span class="o">.</span><span class="n">service_id</span><span class="p">)</span> <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">key_path</span><span class="p">,</span> <span class="s">'w'</span><span class="p">)</span> <span class="k">as</span> <span class="n">key_file</span><span class="p">:</span> <span class="n">key_file</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s">'</span><span class="si">%s</span><span class="s">:</span><span class="si">%s</span><span class="s">'</span> <span class="o">%</span> <span class="p">(</span><span class="n">service</span><span class="o">.</span><span class="n">private_key_type</span><span class="p">,</span> <span class="n">service</span><span class="o">.</span><span class="n">private_key</span><span class="p">))</span> <span class="k">else</span><span class="p">:</span> <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">key_path</span><span class="p">)</span> <span class="k">as</span> <span class="n">key_file</span><span class="p">:</span> <span class="n">key_type</span><span class="p">,</span> <span class="n">key_content</span> <span class="o">=</span> <span class="n">key_file</span><span class="o">.</span><span class="n">read</span><span class="p">()</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s">':'</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span> <span class="n">service</span> <span class="o">=</span> <span class="n">controller</span><span class="o">.</span><span class="n">create_ephemeral_hidden_service</span><span class="p">({</span><span class="mi">80</span><span class="p">:</span> <span class="mi">5000</span><span class="p">},</span> <span class="n">key_type</span> <span class="o">=</span> <span class="n">key_type</span><span class="p">,</span> <span class="n">key_content</span> <span class="o">=</span> <span class="n">key_content</span><span class="p">,</span> <span class="n">await_publication</span> <span class="o">=</span> <span class="bp">True</span><span class="p">)</span> <span class="k">print</span><span class="p">(</span><span class="s">"Resumed </span><span class="si">%s</span><span class="s">.onion"</span> <span class="o">%</span> <span class="n">service</span><span class="o">.</span><span class="n">service_id</span><span class="p">)</span> <span class="nb">raw_input</span><span class="p">(</span><span class="s">'press any key to shut the service down...'</span><span class="p">)</span> <span class="n">controller</span><span class="o">.</span><span class="n">remove_ephemeral_hidden_service</span><span class="p">(</span><span class="n">service</span><span class="o">.</span><span class="n">service_id</span><span class="p">)</span> </pre></div> </div> </div> <div class="section" id="hidden-service-descriptors"> <span id="id5"></span><h2>Hidden service descriptors<a class="headerlink" href="#hidden-service-descriptors" title="Permalink to this headline">¶</a></h2> <p>Like relays, hidden services publish documents about themselves called <strong>hidden service descriptors</strong>. These contain low level details for establishing connections. Hidden service descriptors are available from the tor process via its <tt class="xref py py-func docutils literal"><span class="pre">get_hidden_service_descriptor()</span></tt> method...</p> <div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">stem.control</span> <span class="kn">import</span> <span class="n">Controller</span> <span class="k">with</span> <span class="n">Controller</span><span class="o">.</span><span class="n">from_port</span><span class="p">(</span><span class="n">port</span> <span class="o">=</span> <span class="mi">9051</span><span class="p">)</span> <span class="k">as</span> <span class="n">controller</span><span class="p">:</span> <span class="n">controller</span><span class="o">.</span><span class="n">authenticate</span><span class="p">()</span> <span class="c"># descriptor of duck-duck-go's hidden service (http://3g2upl4pq6kufc4m.onion)</span> <span class="k">print</span><span class="p">(</span><span class="n">controller</span><span class="o">.</span><span class="n">get_hidden_service_descriptor</span><span class="p">(</span><span class="s">'3g2upl4pq6kufc4m'</span><span class="p">))</span> </pre></div> </div> <div class="highlight-python"><pre>% python print_duck_duck_go_descriptor.py rendezvous-service-descriptor e5dkwgp6vt7axoozixrbgjymyof7ab6u version 2 permanent-key -----BEGIN RSA PUBLIC KEY----- MIGJAoGBAJ/SzzgrXPxTlFrKVhXh3buCWv2QfcNgncUpDpKouLn3AtPH5Ocys0jE aZSKdvaiQ62md2gOwj4x61cFNdi05tdQjS+2thHKEm/KsB9BGLSLBNJYY356bupg I5gQozM65ENelfxYlysBjJ52xSDBd8C4f/p9umdzaaaCmzXG/nhzAgMBAAE= -----END RSA PUBLIC KEY----- secret-id-part bmsctib2pzirgo7cltlxdm5fxqcitt5e publication-time 2015-05-11 20:00:00 protocol-versions 2,3 introduction-points -----BEGIN MESSAGE----- aW50cm9kdWN0aW9uLXBvaW50IHZzcm4ycGNtdzNvZ21mNGo3dGpxeHptdml1Y2Rr NGtpCmlwLWFkZHJlc3MgMTc2LjkuNTkuMTcxCm9uaW9uLXBvcnQgOTAwMQpvbmlv ... etc...</pre> </div> <p>A hidden service's introduction points are a base64 encoded field that's possibly encrypted. These can be decoded (and decrypted if necessary) with the descriptor's <tt class="xref py py-func docutils literal"><span class="pre">introduction_points()</span></tt> method.</p> <div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">stem.control</span> <span class="kn">import</span> <span class="n">Controller</span> <span class="k">with</span> <span class="n">Controller</span><span class="o">.</span><span class="n">from_port</span><span class="p">(</span><span class="n">port</span> <span class="o">=</span> <span class="mi">9051</span><span class="p">)</span> <span class="k">as</span> <span class="n">controller</span><span class="p">:</span> <span class="n">controller</span><span class="o">.</span><span class="n">authenticate</span><span class="p">()</span> <span class="n">desc</span> <span class="o">=</span> <span class="n">controller</span><span class="o">.</span><span class="n">get_hidden_service_descriptor</span><span class="p">(</span><span class="s">'3g2upl4pq6kufc4m'</span><span class="p">)</span> <span class="k">print</span><span class="p">(</span><span class="s">"DuckDuckGo's introduction points are...</span><span class="se">\n</span><span class="s">"</span><span class="p">)</span> <span class="k">for</span> <span class="n">introduction_point</span> <span class="ow">in</span> <span class="n">desc</span><span class="o">.</span><span class="n">introduction_points</span><span class="p">():</span> <span class="k">print</span><span class="p">(</span><span class="s">' </span><span class="si">%s</span><span class="s">:</span><span class="si">%s</span><span class="s"> => </span><span class="si">%s</span><span class="s">'</span> <span class="o">%</span> <span class="p">(</span><span class="n">introduction_point</span><span class="o">.</span><span class="n">address</span><span class="p">,</span> <span class="n">introduction_point</span><span class="o">.</span><span class="n">port</span><span class="p">,</span> <span class="n">introduction_point</span><span class="o">.</span><span class="n">identifier</span><span class="p">))</span> </pre></div> </div> <div class="highlight-python"><pre>% python print_duck_duck_go_introduction_points.py DuckDuckGo's introduction points are... 176.9.59.171:9001 => vsrn2pcmw3ogmf4j7tjqxzmviucdk4ki 104.131.106.181:9001 => gcl2kpqx5qnkpgxjf6x7ulqncoqj7ghh 188.166.58.218:443 => jeymnbhs2d6l2oib7jjvweavg45m6gju</pre> </div> </div> </div> </div> <div class="bottomnav"> </div> <div class="footer"> </div> </body> </html>