-
Notifications
You must be signed in to change notification settings - Fork 126
Getting Started ‐ CLI
Getting Started Guide for using ZDNS as a CLI tool
- README for more info on installation and the intent of this tool
- Getting Started - Library
- Getting Started - CLI
-
--help
flag for more info on all available flags and options for the CLI
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!
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 eachdig
instance is its own process. This is much heavier than a goroutine in ZDNS.
Please refer to the README for installation instructions.
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 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.
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"
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.
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.