Skip to content

Getting Started ‐ CLI

Phillip Stephens edited this page Jun 25, 2024 · 3 revisions

Getting Started with ZDNS - CLI

Getting Started Guide for using ZDNS as a CLI tool

Resources

Resources

If after consulting this documentation you still have questions, please feel free to open an issue on the GitHub repo so we can update this community resource!

Overview

A common question is "Why use ZDNS over dig or nslookup?" For one-off queries, these tools or the DNS lookup functionality in your language of choice are more than sufficient.

However, ZDNS is designed to efficiently handle large numbers of DNS queries in parallel.

Several features to improve the performance of large volumes of DNS queries are:

  • Shared Cache - each thread shares an LRU cache to store the most common domain name -> IP mappings. This greatly speeds up iterative lookups.
  • UDP socket re-use - dig and friends will open new socket for each query. ZDNS re-uses the same socket for multiple queries, reducing overhead.
  • Efficient Multi-threading + Parallelism - ZDNS is designed to be multi-threaded. You could run multiple dig queries in parallel but each dig instance is its own process. This is much heavier than a goroutine in ZDNS.

Installation

Please refer to the README for installation instructions.

Basic Usage

Let's start with the most basic lookup, a single A record lookup.

echo "google.com" | ./zdns A

By default, ZDNS takes input from std. in and this domain names are new-line separated (i.e. you can do echo "google.com\nexample.com" | ./zdns A) You can also have all domains in a file and use cat to pipe the file into ZDNS.

The result will be a JSON object with additionals, answers, authorities, and other info.

{"data":{"additionals":[{"flags":"","type":"EDNS0","udpsize":1232,"version":0}],"answers":[{"answer":"93.184.215.14","class":"IN","name":"example.com","ttl":72,"type":"A"}],"protocol":"udp","resolver":"1.1.1.1:53"},"name":"example.com","status":"NOERROR","timestamp":"2024-06-25T15:35:33-07:00"}
{"data":{"additionals":[{"flags":"","type":"EDNS0","udpsize":1232,"version":0}],"answers":[{"answer":"142.250.189.206","class":"IN","name":"google.com","ttl":255,"type":"A"}],"protocol":"udp","resolver":"1.1.1.1:53"},"name":"google.com","status":"NOERROR","timestamp":"2024-06-25T15:35:33-07:00"}

You'll notice that the "resolver" is 1.1.1.1 in both. By default, ZDNS performs external lookups using the OS's default DNS resolver(s).

External Lookups

External lookups utilize a recursive resolver (1.1.1.1 or 8.8.8.8, for example) to perform the lookup. As mentioned before, the default is the OS's default DNS resolver(s). You can override this behavior using the --name-servers flag and a comma-delimited list of IP addresses:ports. Port 53 is inferred if no port is provided.

echo "google.com" | ./zdns A --name-servers 8.8.8.8
{"data":{"additionals":[{"flags":"","type":"EDNS0","udpsize":512,"version":0}],"answers":[{"answer":"142.251.32.46","class":"IN","name":"google.com","ttl":300,"type":"A"}],"protocol":"udp","resolver":"8.8.8.8:53"},"name":"google.com","status":"NOERROR","timestamp":"2024-06-25T15:39:22-07:00"}

Additionally, you can provide a file containing a comma-delimited list of nameservers by passing @/path/to/file to the --name-servers flag. See ./zdns --help for more info.

Iterative Lookups

Alternatively, ZDNS can perform all recursion necessary for a lookup without relying on an external resolver. This is called an iterative lookup.

echo "google.com" | ./zdns A --iterative
{"data":{"answers":[{"answer":"142.250.191.46","class":"IN","name":"google.com","ttl":300,"type":"A"}],"protocol":"udp","resolver":"216.239.34.10:53"},"name":"google.com","status":"NOERROR","timestamp":"2024-06-25T15:49:05-07:00"}

While this looks nearly-identical to the external lookup, we can increase the result verbosity to see the trace of name-servers it queries to know it's querying the root servers and following the DNS resolution process.

echo "google.com" | ./zdns A --iterative --result-verbosity "trace" | jq

There will be quite a bit of output, but you should see a random root server queried and return all .com TLD servers, then a random .com TLD server queried and return all google.com authoritative servers, and finally a random google.com authoritative server queried and return the IP address. This is the default behavior of ZDNS when performing an iterative lookup.

The root server's response on what the .com TLD servers are is cached in the LRU cache to speed up future lookups. We can check this by querying two different domains under .com and the "cached" field in the output will be true. Since domains are queried in parallel, reduce the workers to 1 to see the cache in action.

echo "google.com\nyahoo.com" | ./zdns A --iterative --result-verbosity "trace" --threads=1 | jq | grep "cached"

Multi-Threaded Usage

By default, ZDNS uses 1000 threads to perform lookups. This is a good default for most use-cases, but you can adjust this with the --threads flag. It may be tempting to increase this number to improve performance, but keep in mind that if enough threads are running, it could increase the chances of a rate-limiting event from the name-servers or a single domain resolution timing out. Increasing the number of threads will also increase the memory usage of ZDNS, if this is a concern for you.

By default, ZDNS will set GOMAXPROCS to be the number of cores on the machine. This can be overridden with the --go-processes flag. Setting this to a higher number than the number of cores is not advised. Decreasing this flag is only advised if you need to reserve space for other processes on the machine while ZDNS is running.

Logging

By default, ZDNS logs to stderr. You can change this with the --log-file flag.

You can increase the verbosity of the logs with the --verbosity flag. The default is 3 on a 1-5 where 5 is the most verbose. This can be helpful when debugging issues.