doh icon indicating copy to clipboard operation
doh copied to clipboard

🍩 DNS over HTTPS command-line client

doh

GitHub license CircleCI Status go report PRs Welcome

🍩 DNS over HTTPs command-line client

Using cloudflare, google, and quad9 the doh command-line utility can concurrently lookup all three sources for one or more given domain(s). You can even specify your own custom source to use.

Note: Since doh outputs everything as JSON, it pairs really well with tools like jq to parse relevant parts of the output for your purposes.

Install

To get started, you will need go installed and properly configured.

$ go get github.com/picatz/doh

Update

As new updates come out, you can update doh using the -u flag with go get.

$ go get -u github.com/picatz/doh

Help Menus

The --help command-line flag can show you the top-level help menu.

$ doh --help
Usage:
  doh [command]

Available Commands:
  help        Help about any command
  query       Query domains for DNS records in JSON

Flags:
  -h, --help   help for doh

Use "doh [command] --help" for more information about a command.

To get more information for the query command:

$ doh query --help
Query domains for DNS records in JSON

Usage:
  doh query [domains] [flags]

Flags:
      --custom-only                 query custom source only
      --custom-source-name string   optional custom source name (default "custom")
      --custom-source-url string    custom source base url
  -h, --help                        help for query
      --joined                      join results into a JSON object
      --labels                      show source of the dns record
      --limit int                   limit the number of responses from backend sources (default 1)
      --lock int                    number of concurrent workers (default 4)
      --no-limit                    do not limit results
      --no-timeout                  do not timeout
      --resolver-addr string        custom resolver address:port to use (8.8.8.8:53)
      --resolver-network string     custom resolver network transport to use (udp/tcp) (default "udp")
      --sources strings             sources to use for query (default [google,cloudflare,quad9])
      --timeout int                 number of seconds until timeout (default 30)
      --type string                 dns record type to query for ("A", "AAAA", "MX" ...) (default "A")
      --verbose                     show errors and other available diagnostic information

Example Usage

Let's say I'm curious about google.com's IPv4 address and want to use doh to find out what it is.

$ doh query google.com
{"Status":0,"TC":false,"RD":true,"RA":true,"AD":false,"CD":false,"Question":[{"name":"google.com.","type":1}],"Answer":[{"name":"google.com.","type":1,"TTL":100,"data":"172.217.8.206"}]}

You can see the source of the DNS record using the --labels flag:

$ doh query google.com --labels
{"label":"quad9","resp":{"Status":0,"TC":false,"RD":true,"RA":true,"AD":false,"CD":false,"Question":[{"name":"google.com.","type":1}],"Answer":[{"name":"google.com.","type":1,"TTL":56,"data":"172.217.8.206"}]}}

You can wait for responses from all sources with the --no-limit flag:

$ doh query google.com --labels --no-limit
{"label":"quad9","resp":{"Status":0,"TC":false,"RD":true,"RA":true,"AD":false,"CD":false,"Question":[{"name":"google.com.","type":1}],"Answer":[{"name":"google.com.","type":1,"TTL":40,"data":"216.58.216.238"}]}}
{"label":"google","resp":{"Status":0,"TC":false,"RD":true,"RA":true,"AD":false,"CD":false,"Question":[{"name":"google.com.","type":1}],"Answer":[{"name":"google.com.","type":1,"TTL":213,"data":"108.177.111.113"},{"name":"google.com.","type":1,"TTL":213,"data":"108.177.111.101"},{"name":"google.com.","type":1,"TTL":213,"data":"108.177.111.100"},{"name":"google.com.","type":1,"TTL":213,"data":"108.177.111.138"},{"name":"google.com.","type":1,"TTL":213,"data":"108.177.111.139"},{"name":"google.com.","type":1,"TTL":213,"data":"108.177.111.102"}]}}
{"label":"cloudflare","resp":{"Status":0,"TC":false,"RD":true,"RA":true,"AD":false,"CD":false,"Question":[{"name":"google.com.","type":1}],"Answer":[{"name":"google.com.","type":1,"TTL":195,"data":"172.217.1.46"}]}}

To get just all of the IPs from all of those sources, we could do the following:

$ doh query google.com --no-limit --joined | jq 'map(.Answer | map(.data)) | flatten | .[]' --raw-output
172.217.8.206
108.177.111.139
108.177.111.113
108.177.111.138
108.177.111.101
108.177.111.100
108.177.111.102
172.217.4.206

If we want to filter the output to just the first IP address in the first JSON record with jq:

$ doh query google.com | jq .Answer[0].data --raw-output
172.217.8.206

Now, perhaps google.com isn't the only record we're also interested in, since we also want bing.com, which is where the cool kids are at.

$ doh query bing.com apple.com --limit 2 | jq '(.Answer[0].name|rtrimstr(".")) + "\t" + .Answer[0].data' --raw-output
apple.com	172.217.8.206
bing.com	204.79.197.200

To get IPv6 records, we'll need to specify the --type flag, like so:

$ doh query google.com --type AAAA

To get MX records:

$ doh query google.com --type MX

To get ANY records (which is only implemented by the google source):

$ doh query google.com --type ANY --sources=google

To use a custom DNS over HTTPs source (in this case re-using the google source https://dns.google.com/resolve as a custom one):

$ doh query google.com --custom-only --custom-source-url="https://dns.google.com/resolve" --labels
{"label":"custom","resp":{"Status":0,"TC":false,"RD":true,"RA":true,"AD":false,"CD":false,"Question":[{"name":"google.com.","type":1}],"Answer":[{"name":"google.com.","type":1,"TTL":123,"data":"216.58.192.142"}]}}