<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Dns on Idle Daylight</title><link>https://blog.kiserai.net/tags/dns/</link><description>Recent content in Dns on Idle Daylight</description><generator>Hugo</generator><language>en-us</language><lastBuildDate>Mon, 21 Aug 2023 00:00:00 +0000</lastBuildDate><atom:link href="https://blog.kiserai.net/tags/dns/index.xml" rel="self" type="application/rss+xml"/><item><title>Crafting a DNS-Over-HTTPS GET Request Manually</title><link>https://blog.kiserai.net/posts/crafting-a-dns-over-https-get-request-manually/</link><pubDate>Mon, 21 Aug 2023 00:00:00 +0000</pubDate><guid>https://blog.kiserai.net/posts/crafting-a-dns-over-https-get-request-manually/</guid><description>&lt;p&gt;Let&amp;rsquo;s say I want to create an external health check for &lt;a href="https://adguard.com/en/welcome.html"&gt;AdGuard&lt;/a&gt; that exercises both the HTTP interface and the DNS resolution, because I do. Unlike &lt;a href="https://developers.cloudflare.com/1.1.1.1/encryption/dns-over-https/make-api-requests/"&gt;Cloudflare&lt;/a&gt;, AdGuard doesn&amp;rsquo;t take an easy &lt;code&gt;name&lt;/code&gt; parameter, and I found it quite difficult to find existing documentation on how to craft a DoH request manually. Here&amp;rsquo;s how to do it in Python.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Let&rsquo;s say I want to create an external health check for <a href="https://adguard.com/en/welcome.html">AdGuard</a> that exercises both the HTTP interface and the DNS resolution, because I do.  Unlike <a href="https://developers.cloudflare.com/1.1.1.1/encryption/dns-over-https/make-api-requests/">Cloudflare</a>, AdGuard doesn&rsquo;t take an easy <code>name</code> parameter, and I found it quite difficult to find existing documentation on how to craft a DoH request manually. Here&rsquo;s how to do it in Python.</p>
<h2 id="requirements">Requirements</h2>
<ul>
<li>Python 3</li>
<li>Ideally <code>dnspython</code> &gt;= 2.2.0 (so, Ubuntu 22.04&rsquo;s <code>python3-dnspython</code> package will not work. Use pip or a <a href="https://hub.docker.com/_/python">python docker image</a>). Earlier versions will work without setting <code>id=0</code>.</li>
<li><code>requests</code> for testing</li>
</ul>
<h2 id="one-liner">One-liner</h2>
<p>Change <code>example.com</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>DOMAIN<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;example.com&#34;</span> python3 -c <span style="color:#e6db74">&#34;import os, dns.message, requests, base64; req = dns.message.make_query(os.environ[&#39;DOMAIN&#39;], &#39;A&#39;, id=0).to_wire(); print(base64.urlsafe_b64encode(req).decode(&#39;utf-8&#39;).rstrip(&#39;=&#39;))&#34;</span>
</span></span></code></pre></div><h2 id="script">Script</h2>
<p>Set DOMAIN environment variable before running:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#f92672">import</span> os
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> dns.message
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> requests
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> base64
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>req_raw <span style="color:#f92672">=</span> dns<span style="color:#f92672">.</span>message<span style="color:#f92672">.</span>make_query(os<span style="color:#f92672">.</span>environ[<span style="color:#e6db74">&#39;DOMAIN&#39;</span>], <span style="color:#e6db74">&#39;A&#39;</span>, id<span style="color:#f92672">=</span><span style="color:#ae81ff">0</span>)<span style="color:#f92672">.</span>to_wire()
</span></span><span style="display:flex;"><span>req_str <span style="color:#f92672">=</span> base64<span style="color:#f92672">.</span>urlsafe_b64encode(req_raw)<span style="color:#f92672">.</span>decode(<span style="color:#e6db74">&#39;utf-8&#39;</span>)<span style="color:#f92672">.</span>rstrip(<span style="color:#e6db74">&#39;=&#39;</span>)
</span></span><span style="display:flex;"><span>print(<span style="color:#e6db74">&#34;Query path and encoded string to use with a GET request to a DNS-over-HTTPS server:&#34;</span>)
</span></span><span style="display:flex;"><span>print(<span style="color:#e6db74">&#34;/dns-query?dns=&#34;</span> <span style="color:#f92672">+</span> req_str)
</span></span><span style="display:flex;"><span>print(<span style="color:#e6db74">&#34;</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">Testing query with Cloudflare...&#34;</span>)
</span></span><span style="display:flex;"><span>resp <span style="color:#f92672">=</span> requests<span style="color:#f92672">.</span>get(<span style="color:#e6db74">&#34;https://cloudflare-dns.com/dns-query?dns=&#34;</span> <span style="color:#f92672">+</span> req_str)
</span></span><span style="display:flex;"><span>resp<span style="color:#f92672">.</span>raise_for_status()
</span></span><span style="display:flex;"><span>msg <span style="color:#f92672">=</span> dns<span style="color:#f92672">.</span>message<span style="color:#f92672">.</span>from_wire(resp<span style="color:#f92672">.</span>content)
</span></span><span style="display:flex;"><span>print(<span style="color:#e6db74">&#34;DNS response:&#34;</span>)
</span></span><span style="display:flex;"><span>print(msg)
</span></span></code></pre></div><h2 id="example-output">Example output</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ DOMAIN<span style="color:#f92672">=</span>example.com python3 ./doh.py
</span></span><span style="display:flex;"><span>Query path and encoded string to use with a GET request to a DNS-over-HTTPS server:
</span></span><span style="display:flex;"><span>/dns-query?dns<span style="color:#f92672">=</span>AAABAAABAAAAAAAAB2V4YW1wbGUDY29tAAABAAE
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Testing query with Cloudflare...
</span></span><span style="display:flex;"><span>DNS response:
</span></span><span style="display:flex;"><span>id <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span>opcode QUERY
</span></span><span style="display:flex;"><span>rcode NOERROR
</span></span><span style="display:flex;"><span>flags QR RD RA
</span></span><span style="display:flex;"><span>;QUESTION
</span></span><span style="display:flex;"><span>example.com. IN A
</span></span><span style="display:flex;"><span>;ANSWER
</span></span><span style="display:flex;"><span>example.com. <span style="color:#ae81ff">82324</span> IN A 93.184.216.34
</span></span><span style="display:flex;"><span>;AUTHORITY
</span></span><span style="display:flex;"><span>;ADDITIONAL
</span></span></code></pre></div><h2 id="references">References</h2>
<p><a href="https://github.com/dohwg/draft-ietf-doh-dns-over-https/issues/30#issuecomment-345850479">https://github.com/dohwg/draft-ietf-doh-dns-over-https/issues/30#issuecomment-345850479</a></p>
<p><a href="https://developers.cloudflare.com/1.1.1.1/encryption/dns-over-https/make-api-requests/dns-wireformat/">https://developers.cloudflare.com/1.1.1.1/encryption/dns-over-https/make-api-requests/dns-wireformat/</a></p>
<p><a href="https://datatracker.ietf.org/doc/html/rfc8484#section-4.1">https://datatracker.ietf.org/doc/html/rfc8484#section-4.1</a></p>]]></content:encoded></item></channel></rss>