Although infrequent, the root nameserver configurations do change from time to time.
DNS needs to know how to find the root servers, which it does with a semi-static file.
The following script will update that file (which goes by various names - I call it ROOT.HINT) periodically.
By making it an optware startup file, it will run once on each boot (After named is running!). I also schedule it to run monthly under crond.
'Watchnet' is a cron script that runs every few minutes (for other reasons). It looks for /tmp/dnsrootfailed & runs S10dnsroot if it finds it. This is a cheap way to retry on failure without editing crontab. If you don't have such a process, you probably want to do something different (more efficient).
I recommend using the ipkg wget, as the busybox built-in doesn't support timeouts.
There are various configuration options - make sure that you set them for your environment.
Note: Be sure to chmod +x the script file.
Note: this script contains literal tab characters. Be very careful how you cut & paste. In particular, [ ] sequences in the script are actually [<space><tab>].
enjoy
--tlhackque
/opt/etc/init.d/S10dnsroot
#!/bin/sh
# *** Note that under cron, there may be no stdout
#
# Update the DNS ROOT.HINT file
# Run once on each boot (AFTER named is running), then
# monthly (TTL on hint records is 42 days, so this should suffice)
#
#
# Two mechanisms are provided.
# The first uses FTP, which is simple, but deprecated by some.
# The second uses DNS - a popular method, but not as easy to get right.
#
# Despite all the extra work, DNS runs faster (on a fast SLUG) than FTP.
# FTP takes roughly 12 seconds vs. 4 for DNS!
#
#USEDNS=yes
#
# Where ROOT.HINT lives
#
DNSDIR=/opt/etc/named
HINT=ROOT.HINT
#
# At startup, simply flag watchnet to run once we're up.
# This is to prevent startup hangs. We can get by on the
# old hints file for quite some time.
#
if [ "$1" = 'start' ]; then
touch /tmp/dnsrootfailed
exit
fi
rm -f /tmp/dnsrootfailed
#
# WGET - if full version is installed, limit tries and timeouts
# to prevent hanging watchnet.
#
if [ -x /opt/bin/wget ]; then
WGET="/opt/bin/wget -t 2 -T 60"
else
WGET=/usr/bin/wget
fi
if [ -z "$USEDNS" ]; then
#
# This works, though it may be somewhat harder on the servers
#
ROOTSRC=ftp://ftp.internic.net/domain/named.root
if $WGET -q -O $DNSDIR/${HINT}.tmp $ROOTSRC ; then
if ! /opt/bin/diff -q $DNSDIR/${HINT} $DNSDIR/${HINT}.tmp >/dev/null 2>&1 ; then
chmod u=rw,g=r,o= $DNSDIR/${HINT}.tmp
cp -f -p $DNSDIR/${HINT} $DNSDIR/${HINT}.old
mv -f $DNSDIR/${HINT}.tmp $DNSDIR/${HINT}
/opt/bin/logger -t dnsroot[$$] -p "daemon.info" "Updated $DNSDIR/${HINT} from $ROOTSRC"
if [ -n "`pidof named`" ]; then
/opt/sbin/rndc -s 127.0.0.1 reconfig
fi
fi
else
touch /tmp/dnsrootfailed
/opt/bin/logger -t dnsroot[$$] -p "daemon.warning" "Unable to get root server list. Watchnet will retry."
fi
rm -f $DNSDIR/${HINT}.tmp
exit
fi
#
# Based on several popular scripts that claim to work - but don't anymore.
#
# The theory is that asking DNS is less work than ftp'ing the file
# As you'll see, this ain't so
#
# Get our primary IP address (SLUG oddity - can be done differently)
. /etc/sysconfig/network-scripts/ifcfg-ixp0
HOSTNAME=`hostname`
#
# Root server list - doesn't need to be accurate, just need any 1 to work
#
ROOTS="A B C D E F G H I J K L M"
NROOTS=13
#
# E-mail notifications = don't set if not wanted.
#
#TO=hostmaster@litts.net
#
# Zone file TTL for root records
#
TTL=3600000
#
# Buffer size to minimize extra queries
#
BUFSIZE=+bufsize=32768
if [ -n "$TO" ]; then
#
# Slug oddity: could be /bin/hostname, except that we can't get an FQDN on the slug,
# and mail servers require one.
#
FROM=`/opt/bin/host $IPADDR | grep ' domain name pointer ' | sed -e's|^.* pointer \(.*\)$|\1|'`
if [ -n "$FROM" ]; then
FROM="\"DNS root update\"<root@$FROM>"
else
FROM="\"DNS root update\"<root@$HOSTNAME>"
fi
fi
#
# Start subshell - output to mail body
(
# Select a random ordering of the root server list
n=`( echo $$ ; ps ; date ) | cksum | cut -f1 -d" "`
n=`expr \( $n % $NROOTS \) + 1`
m=`expr $n + 1`
b=`echo "$ROOTS" | cut -d " " -f "-$n"`
e=`echo "$ROOTS" | cut -d " " -f "$m-"`
ROOTS="$e $b"
# Look for a root server that can supply the NS records for the root zone
sts=1
for s in $ROOTS ; do
ROOTSRC=$s.root-servers.net.
/opt/bin/dig @$ROOTSRC +norecurse $BUFSIZE . ns >$DNSDIR/${HINT}.raw 2> $DNSDIR/${HINT}.errors
sts=$?
if [ $sts -eq 0 ]; then
rm -f $DNSDIR/${HINT}.errors
break
fi
done
if [ $sts -ne 0 ]; then
/opt/bin/logger -t dnsroot[$$] -p "daemon.warning" "Unable to reach any root server, $DNSDIR/${HINT} was not validated"
touch /tmp/dnsrootfailed
cat <<EOF
No root server in {$ROOTS}.root-servers.net. was reachable.
$DNSDIR/${HINT} could not be validated.
dig returned:
EOF
cat $DNSDIR/${HINT}.errors $DNSDIR/${HINT}.raw
rm -f $DNSDIR/${HINT}.errors $DNSDIR/${HINT}.raw
exit 1
fi
# Strip comments and blank lines to make inital new zone
# With IPV6 in the root zone, we may not get all the A/AAAA records in one query
# Note: in parsing the dig output, we match on space and/or tab. Due to sed limitations,
# the tab is literal. Be very careful cutting/pasting. The [] contain a space and a tab.
#
grep -v '^;' $DNSDIR/${HINT}.raw | sed -e'/^[ ]*$/d' | /opt/bin/sort > $DNSDIR/${HINT}.new
# Correct TTL - we want the zone file's value, not a cached TTL
sed -i -e"s/^\(.*[ ]\)[0-9][0-9]*\([ ][ ]*IN[ ].*\)$/\1$TTL\2/" $DNSDIR/${HINT}.new
# Extract just the NS records
grep '^\.[ ][ ].*NS[ ][ ]*.*$' $DNSDIR/${HINT}.new >$DNSDIR/${HINT}.ns
# For each NS record
while [ -s $DNSDIR/${HINT}.ns ]; do
NS=`head -n 1 $DNSDIR/${HINT}.ns | sed -e's|.*[ ]NS[ ][ ]*\(.*\)$|\1|'`
sed -i -e'1d' $DNSDIR/${HINT}.ns
# Check for A record in zone
# At this writing, all root servers have IPV4 address records.
# However, we can't tell if we got everything due to the server
# limiting the response size or because they don't exist.
# So we will ask again for each of the missing records.
if ! grep -q -i "^$NS[ ].*[ ]A[ ]" $DNSDIR/${HINT}.new ; then
MISSING="$MISSING $NS A"
fi
# Check for AAAA record in zone
# At this writing, not all root servers have IPV6 address records.
# However, we can't tell if we got everything due to the server
# limiting the response size or because they don't exist.
# So we will ask again for each of the missing records.
if ! grep -q -i "^$NS[ ].*[ ]AAAA[ ]" $DNSDIR/${HINT}.new ; then
MISSING="$MISSING $NS AAAA"
fi
done
rm $DNSDIR/${HINT}.ns
if [ -n "$MISSING" ]; then
# Try to get missing A/AAAA records - ignore SOA, etc.
/opt/bin/dig @$ROOTSRC +norecurse $BUFSIZE +noauthority $MISSING >$DNSDIR/${HINT}.raw 2> /dev/null
grep -v '^;' $DNSDIR/${HINT}.raw | sed -e'/^[ ]*$/d' | grep '[ ]A\+[ ]' >> $DNSDIR/${HINT}.new
fi
#
# Root servers send random case. Make all upper case, sort & remove duplicate records.
# Unfortunately, this will also up-case IPV6 addresses - but that doesn't really matter.
#
sed -e'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/' $DNSDIR/${HINT}.new | /opt/bin/sort | uniq >$DNSDIR/${HINT}.tmp
mv -f $DNSDIR/${HINT}.tmp $DNSDIR/${HINT}.new
rm -f $DNSDIR/${HINT}.raw
chown root.root $DNSDIR/${HINT}.new
chmod u=rw,g=r,o= $DNSDIR/${HINT}.new
if diff -q $DNSDIR/${HINT} $DNSDIR/${HINT}.new >/dev/null 2>&1 ; then
rm -f $DNSDIR/${HINT}.new
exit 0
else
cp -f -p $DNSDIR/${HINT} $DNSDIR/${HINT}.old
mv -f $DNSDIR/${HINT}.new $DNSDIR/${HINT}
/opt/bin/logger -t dnsroot[$$] -p "daemon.info" "Updated $DNSDIR/${HINT} from $ROOTSRC"
if [ -n "`pidof named`" ]; then
/opt/sbin/rndc -s 127.0.0.1 reconfig
fi
cat <<EOF
$DNSDIR/${HINT} was updated from $ROOTSRC
EOF
fi
) > /tmp/dnsroot.mail 2>&1
if [ -s /tmp/dnsroot.mail ]; then
if [ -n "$TO" ]; then
cat /tmp/dnsroot.mail | smtpclient -e '' -f "$FROM" -s "DNS root update results" $TO
fi
fi
rm -f /tmp/dnsroot.mail
exit 0
/sbin/watchnet (Extract)
#!/bin/sh
if [ -e /tmp/dnsrootfailed ]; then
/opt/etc/init.d/S10dnsroot
fi
/etc/crontab
17 1 7 * * root /opt/etc/init.d/S10dnsroot
3,13,23,33,43,53 * * * * root /opt/sbin/watchnet &>/dev/null