NSLU2-Linux
view · edit · print · history

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

view · edit · print · history · Last edited by tlhackque.
Originally by tlhackque.
Page last modified on May 15, 2008, at 02:59 PM