Crafting a DNS-over-HTTPS GET request manually

Let's say I want to create an external health check for AdGuard that exercises both the HTTP interface and the DNS resolution, because I do. Unlike Cloudflare, AdGuard doesn't take an easy name parameter, and I found it quite difficult to find existing documentation on how to craft a DoH request manually. Here's how to do it in Python.

Requirements

One-liner

Change example.com:

DOMAIN="example.com" python3 -c "import os, dns.message, requests, base64; req = dns.message.make_query(os.environ['DOMAIN'], 'A', id=0).to_wire(); print(base64.urlsafe_b64encode(req).decode('utf-8').rstrip('='))"

Script

Set DOMAIN environment variable before running:

import os
import dns.message
import requests
import base64

req_raw = dns.message.make_query(os.environ['DOMAIN'], 'A', id=0).to_wire()
req_str = base64.urlsafe_b64encode(req_raw).decode('utf-8').rstrip('=')
print("Query path and encoded string to use with a GET request to a DNS-over-HTTPS server:")
print("/dns-query?dns=" + req_str)
print("\nTesting query with Cloudflare...")
resp = requests.get("https://cloudflare-dns.com/dns-query?dns=" + req_str)
resp.raise_for_status()
msg = dns.message.from_wire(resp.content)
print("DNS response:")
print(msg)

Example output

$ DOMAIN=example.com python3 ./doh.py
Query path and encoded string to use with a GET request to a DNS-over-HTTPS server:
/dns-query?dns=AAABAAABAAAAAAAAB2V4YW1wbGUDY29tAAABAAE

Testing query with Cloudflare...
DNS response:
id 0
opcode QUERY
rcode NOERROR
flags QR RD RA
;QUESTION
example.com. IN A
;ANSWER
example.com. 82324 IN A 93.184.216.34
;AUTHORITY
;ADDITIONAL

References

https://github.com/dohwg/draft-ietf-doh-dns-over-https/issues/30#issuecomment-345850479

https://developers.cloudflare.com/1.1.1.1/encryption/dns-over-https/make-api-requests/dns-wireformat/

https://datatracker.ietf.org/doc/html/rfc8484#section-4.1

Filed in: #python #dns #adguard