D'oh! - DNS over HTTPS in Rust


Last weekend I met Daniel Stenberg, author of curl, at FOSDEM and we talked a bit about curl, Firefox and also Rust. One recent project he was working on was DNS over HTTPS support for Firefox and eventually for curl as well.

DNS over HTTPS, short DOH, is a recent idea to do DNS queries over HTTPS for privacy, performance and security reasons. There's an RFC draft in version 31 at the IETF describing it in more detail.
The tl;dr: Send the DNS protocol in an HTTP POST request or base64-encoded in a GET request, get back the DNS protocol in the response body and parse it.

Given how simple that sounds, I decided to implement a minimal DOH client in Rust. I present to you:

dnsoverhttps

It exports one function to resolve a hostname to its IPv6 and IPv4 addresses:

extern crate dnsoverhttps;

fn main() {
    let name = "example.com";
    let addr = dnsoverhttps::resolve_host(name);

    for a in addr {
        println!("{} has address {}", name, a);
    }
}

It currently uses dns.google.com (or to be exact one of its IPs: 172.217.21.110) and skips TLS certificate checks (because the underlying HTTP request library has no option to pass in the hostname yet). It will also always query for both IPv6 and IPv4 addresses (AAAA and A records respectively). CNAME records will work as well by the mere fact that the server recursively resolves it and dnsoverhttps simply takes the found IP address. Other records are not supported at the moment.

For a quick try you can install the bundled CLI tool to replace the host tool to resolve hostnames.

cargo install dnsoverhttps

And then execute it:

$ ~/.cargo/bin/host example.com
example.com has address 2606:2800:220:1:248:1893:25c8:1946
example.com has address 93.184.216.34

Documentation is available online.


1

dnsoverhttps currently implements Version 2, because that's what dns.google.com supports. Version 3 changes the query parameter from body to dns.