Skip to content

DoH: how does DNS resolution work in ClearAddress?

Hi there!

Recently, valterpatrick suggested I show how DNS queries are made on the clear-address.rda.run website. I really appreciate the question, as it opens up a fascinating and very useful topic: DNS over HTTPS (DoH).

Instead of using the traditional DNS protocol, which typically operates unencrypted on port 53, the site uses the public APIs of DNS resolvers that support HTTPS. In practice, this means a DNS query is transformed into a simple HTTP call. This adds an extra layer of privacy and security, as the query is disguised as regular HTTPS traffic.

The Magic Behind the Curtain: The APIs

The backend of Clear Address makes calls to DoH resolvers, such as those offered by Cloudflare and Google. The choice between them may vary, but the principle is exactly the same.

The idea is to send a GET request to a specific endpoint, passing the domain and the desired record type as URL parameters. The server responds with a JSON containing the query results.

DoH via Cloudflare

Cloudflare is one of the most well-known providers of DoH services. The complete documentation can be found here.

To query the AAAA record (IPv6 address) of clear-address.rda.run itself, the API URL looks like this:

js
https://cloudflare-dns.com/dns-query?name=clear-address.rda.run&type=AAAA

Using cURL

You can test this directly in your terminal with cURL. The Accept header is important to ensure Cloudflare returns a JSON response.

bash
curl -H 'Accept: application/dns-json' 'https://cloudflare-dns.com/dns-query?name=clear-address.rda.run&type=AAAA'

Using Go

On the backend, the process is similar. Here is a simple example in Go of how to make this call and process the response.

go
package main

import (
    "encoding/json"
    "fmt"
    "io"
    "net/http"
)

// Struct to receive the Cloudflare API response
type DNSResponse struct {
    Status    int  `json:"Status"`
    TC        bool `json:"TC"`
    RD        bool `json:"RD"`
    RA        bool `json:"RA"`
    AD        bool `json:"AD"`
    CD        bool `json:"CD"`
    Question []struct {
        Name string `json:"name"`
        Type int    `json:"type"`
    } `json:"Question"`
    Answer []struct {
        Name string `json:"name"`
        Type int    `json:"type"`
        TTL  int    `json:"TTL"`
        Data string `json:"data"`
    } `json:"Answer"`
}

func main() {
    // Cloudflare API URL for the desired query
    url := "https://cloudflare-dns.com/dns-query?name=clear-address.rda.run&type=AAAA"

    // Create a new request
    req, err := http.NewRequest("GET", url, nil)
    if err != nil {
        panic(err)
    }

    // Add the 'Accept' header to specify the response format
    req.Header.Add("Accept", "application/dns-json")

    // Execute the request
    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()

    // Read the response body
    body, err := io.ReadAll(resp.Body)
    if err != nil {
        panic(err)
    }

    // Decode the JSON into our struct
    var dnsResp DNSResponse
    if err := json.Unmarshal(body, &dnsResp); err != nil {
        panic(err)
    }

    // Print the results
    fmt.Println("DNS query results:")
    if len(dnsResp.Answer) > 0 {
        for _, answer := range dnsResp.Answer {
            fmt.Printf(" - Domain: %s\n", answer.Name)
            fmt.Printf("    Type: AAAA\n")
            fmt.Printf("    TTL: %d\n", answer.TTL)
            fmt.Printf("    IPv6 Address: %s\n", answer.Data)
        }
    } else {
        fmt.Println("No AAAA records found.")
    }
}

DoH via Google

Google offers a similar service, documented here. The logic is the same, only the URL changes.

js
https://dns.google/resolve?name=clear-address.rda.run&type=AAAA

The implementation, whether with cURL or Go, would follow the same pattern, adapting only to the structure of the Google response JSON, which may have minor differences.

Backend vs. Frontend

An important note: in Clear Address, these calls are always made by the backend. I've never tried to make them directly from the frontend (via JavaScript in the browser), so I can't say if there would be issues with CORS (Cross-Origin Resource Sharing) or if a specific header would be needed for the request to be accepted. Using the backend simplifies the process and keeps the business logic on the server.

Conclusion

And that's it! Without great complexity, Clear Address simply uses the public and well-documented APIs of major players to resolve domain names securely and privately.