stem_examples/docs/over_the_river.html
emdee@spm.plastiras.org e41515a8a7 second
2024-01-14 14:28:53 +00:00

362 lines
29 KiB
HTML

<!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 &mdash; 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">&#39;/&#39;</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">&quot;&lt;h1&gt;Hi Grandma!&lt;/h1&gt;&quot;</span>
<span class="k">print</span><span class="p">(</span><span class="s">&#39; * Connecting to tor&#39;</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&#39;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">&#39;DataDirectory&#39;</span><span class="p">,</span> <span class="s">&#39;/tmp&#39;</span><span class="p">),</span> <span class="s">&#39;hello_world&#39;</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">&quot; * Creating our hidden service in </span><span class="si">%s</span><span class="s">&quot;</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">&quot; * Our service is available at </span><span class="si">%s</span><span class="s">, press ctrl+c to quit&quot;</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">&quot; * Unable to determine our service&#39;s hostname, probably due to being unable to read the hidden service directory&quot;</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&#39;t*</span>
<span class="c"># want to delete the hidden service directory if you&#39;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">&quot; * Shutting down our hidden service&quot;</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">&gt;&gt;&gt; </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">&gt;&gt;&gt; </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">&gt;&gt;&gt; </span><span class="n">controller</span><span class="o">.</span><span class="n">authenticate</span><span class="p">()</span>
<span class="gp">&gt;&gt;&gt; </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">&#39;bob&#39;</span><span class="p">:</span> <span class="bp">None</span><span class="p">,</span> <span class="s">&#39;alice&#39;</span><span class="p">:</span> <span class="bp">None</span><span class="p">})</span>
<span class="gp">&gt;&gt;&gt; </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">(&#39;l3lnorirzn7hrjnw&#39;, {&#39;alice&#39;: &#39;I6AMKiay+UkM5MfrvdnF2A&#39;, &#39;bob&#39;: &#39;VLsbrSGyrb5JYEvZmQ3tMg&#39;})</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">&gt;&gt;&gt; </span><span class="n">controller</span><span class="o">.</span><span class="n">set_conf</span><span class="p">(</span><span class="s">&#39;HidServAuth&#39;</span><span class="p">,</span> <span class="s">&#39;l3lnorirzn7hrjnw.onion I6AMKiay+UkM5MfrvdnF2A&#39;</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">&#39;/&#39;</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">&quot;&lt;h1&gt;Hi Grandma!&lt;/h1&gt;&quot;</span>
<span class="k">print</span><span class="p">(</span><span class="s">&#39; * Connecting to tor&#39;</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">&quot; * Our service is available at </span><span class="si">%s</span><span class="s">.onion, press ctrl+c to quit&quot;</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">&quot; * Shutting down our hidden service&quot;</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">&#39;~/my_service_key&#39;</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">&quot;Started a new hidden service with the address of </span><span class="si">%s</span><span class="s">.onion&quot;</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">&#39;w&#39;</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">&#39;</span><span class="si">%s</span><span class="s">:</span><span class="si">%s</span><span class="s">&#39;</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">&#39;:&#39;</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">&quot;Resumed </span><span class="si">%s</span><span class="s">.onion&quot;</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">&#39;press any key to shut the service down...&#39;</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&#39;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">&#39;3g2upl4pq6kufc4m&#39;</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">&#39;3g2upl4pq6kufc4m&#39;</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s">&quot;DuckDuckGo&#39;s introduction points are...</span><span class="se">\n</span><span class="s">&quot;</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">&#39; </span><span class="si">%s</span><span class="s">:</span><span class="si">%s</span><span class="s"> =&gt; </span><span class="si">%s</span><span class="s">&#39;</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 =&gt; vsrn2pcmw3ogmf4j7tjqxzmviucdk4ki
104.131.106.181:9001 =&gt; gcl2kpqx5qnkpgxjf6x7ulqncoqj7ghh
188.166.58.218:443 =&gt; jeymnbhs2d6l2oib7jjvweavg45m6gju</pre>
</div>
</div>
</div>
</div>
<div class="bottomnav">
</div>
<div class="footer">
</div>
</body>
</html>