Originally published January 10, 2019 @ 6:35 pm

This is not an entirely proper way to benchmark a DNS server, but, in a pinch, it should give you some idea of its responsiveness and stability.

The commands below require dig (provided by the bind-utils package), and shuf (provided by the coreutils package). Pretty standard stuff you should already have installed.

The first step is to grab a large sample of domain names. Here’s one of them:

curl -q -s0 -k https://raw.githubusercontent.com/opendns/public-domain-lists/master/opendns-random-domains.txt > /tmp/opendns-random-domains.txt

In my case, the three internal DNS servers are 192.168.122.2-4. The shuf -i 2-4 -n 1 command below will pick one of those at random for every lookup. Here we do lookups on three batches of one hundred random domains – one at a time – and measure average response time, while waiting for five minutes between each job. From this you could sort-of calculate a 15-minute average.

c=100
for i in $(seq 1 3)
do
  for i in $(shuf -n ${c} /tmp/opendns-random-domains.txt)
  do 
    dig ${i} +time=1 +tries=1 -t A @192.168.122.$(shuf -i 2-4 -n 1) +nonssearch 2>/dev/null | grep -oP "(?<=Query time: )[0-9]{1,}(?= msec$)"
  done | awk -v c="$c" '{sum += $1} END {printf "%.0f ms\n", sum/c}'
  sleep 300
done

I suppose it may be interesting to see how DNS performance changes if you lookup more than one domain in a single pass. Similar to the previous example, the command below will launch 10 instances of dig in parallel. If working with external DNS servers, be careful not to get blacklisted.

c=100
for i in $(seq 1 3)
do
  shuf -n ${c} /tmp/opendns-random-domains.txt | \
  xargs -n1 -P10 -I%  dig % +time=1 +tries=1 -t A @192.168.122.$(shuf -i 2-4 -n 1) +nonssearch 2>/dev/null | \
  grep -oP "(?<=Query time: )[0-9]{1,}(?= msec$)" | awk -v c="$c" '{sum += $1} END {printf "%.0f ms\n", sum/c}'
  sleep 300
done

Here’s a different way of doing the same operation as in the previous example, but without using xargs. Why? It’s just a clever example of using custom file handles to read multiple lines of input in a single loop iteration and assign each line to a different variable. You should really play with this.

c=100
for i in $(seq 1 3)
do
  tmpfile=$(mktemp)
  shuf -n ${c} /tmp/opendns-random-domains.txt > ${tmpfile}
  exec 5<${tmpfile}
  while read line0 <&5
  do
    read line1 <&5
    read line2 <&5
    read line3 <&5
    read line4 <&5
    read line5 <&5
    read line6 <&5
    read line7 <&5
    read line8 <&5
    read line9 <&5
    dig ${line0} +time=1 +tries=1 -t A @192.168.122.$(shuf -i 2-4 -n 1) +nonssearch 2>/dev/null | grep -oP "(?<=Query time: )[0-9]{1,}(?= msec$)" &
    dig ${line1} +time=1 +tries=1 -t A @192.168.122.$(shuf -i 2-4 -n 1) +nonssearch 2>/dev/null | grep -oP "(?<=Query time: )[0-9]{1,}(?= msec$)" &
    dig ${line2} +time=1 +tries=1 -t A @192.168.122.$(shuf -i 2-4 -n 1) +nonssearch 2>/dev/null | grep -oP "(?<=Query time: )[0-9]{1,}(?= msec$)" &
    dig ${line3} +time=1 +tries=1 -t A @192.168.122.$(shuf -i 2-4 -n 1) +nonssearch 2>/dev/null | grep -oP "(?<=Query time: )[0-9]{1,}(?= msec$)" &
    dig ${line4} +time=1 +tries=1 -t A @192.168.122.$(shuf -i 2-4 -n 1) +nonssearch 2>/dev/null | grep -oP "(?<=Query time: )[0-9]{1,}(?= msec$)" &
    dig ${line5} +time=1 +tries=1 -t A @192.168.122.$(shuf -i 2-4 -n 1) +nonssearch 2>/dev/null | grep -oP "(?<=Query time: )[0-9]{1,}(?= msec$)" &
    dig ${line6} +time=1 +tries=1 -t A @192.168.122.$(shuf -i 2-4 -n 1) +nonssearch 2>/dev/null | grep -oP "(?<=Query time: )[0-9]{1,}(?= msec$)" &
    dig ${line7} +time=1 +tries=1 -t A @192.168.122.$(shuf -i 2-4 -n 1) +nonssearch 2>/dev/null | grep -oP "(?<=Query time: )[0-9]{1,}(?= msec$)" &
    dig ${line8} +time=1 +tries=1 -t A @192.168.122.$(shuf -i 2-4 -n 1) +nonssearch 2>/dev/null | grep -oP "(?<=Query time: )[0-9]{1,}(?= msec$)" &
    dig ${line9} +time=1 +tries=1 -t A @192.168.122.$(shuf -i 2-4 -n 1) +nonssearch 2>/dev/null | grep -oP "(?<=Query time: )[0-9]{1,}(?= msec$)" &
  done | awk -v c="$c" '{sum += $1} END {printf "%.0f ms\n", sum/c}'
  exec 5<&-
  /bin/rm ${tmpfile}
  sleep 300
done