A fair chunk of the time is just starting the dig
command for every single address.
Better would be to handle a reasonable number of addresses in one command, and then post-process that to produce the output you desire. (If you don't like the raw output, add the sed
command I've given at the end of this answer.)
For a /16 network I suggest something like this:
#!/bin/bash
N=192.168.
printf %s\\n -x"$N"{0..255}.{0..255} |
xargs -r -n64 -P64 dig +noall +ans +nocl +nottl
The -n64
means that each invocation of dig
will output around 3800 bytes depending on how long the resulting names are. As long as the entire output of each dig
is less than 4096 bytes, it will be written as a single write()
syscall.
If you have long domain names causing interleaving, the simplest fix is to reduce the -n
option, but that will make it somewhat slower.
Another way to mitigated this is to have each invocation of dig
write to a separate output file, and then combine them at the end. For example:
#!/bin/bash
d="/tmp/tmpdir$$"
mkdir "$d"
N=192.168.
p=0 w=64
for (( c=0 ; c < 256 ; ++c )) do
(( ++p <= w )) || wait -n
dig +noall +ans +nocl +nottl "$N$c".{0..255} > "$d/$c" &
done
wait
cat "$d"/*
Lastly, if you have administrative access to the authoritative nameserver, then you could configure the nameserver to allow zone transfers to a host of your choosing. Then you could get everything in a few seconds using a single command:
#!/bin/bash
dig @ns.your.domain. +noall +ans +nocl +nottl axfr 168.192.in-addr.arpa
The commands I've given above all output the raw dig
format; you can convert this to the requested format using a filter like this:
#!/bin/bash
(command as above ...) |
sed 's/^\([0-9.]*\)\..*[[:space:]]/\1\t/
s/\.$//
s/^\(.*\)\.\(.*\)\.\(.*\)\.\(.*\)\t/\4.\3.\2.\1\t/
s/\t/ resolves to /'