#!/bin/sh
PATH=:/bin:/usr/bin:/usr/ucb:/usr/local/bin
# -------------------------------------------------------------
#  Copyright (c) 1989, 1993, 1994
#  Regents of the University of Michigan.  All rights reserved.
#
#  Redistribution and use is permitted provided that this notice 
#  is preserved and that due credit is given to the University of 
#  Michigan. The name of the University may not be used to endorse 
#  or promote products derived from this software without specific 
#  prior written permission. This software is provided "as is" 
#  without express or implied warranty.
#
#  DNS Statistics gatherer
#  Author:  Bryan Beecher
#  Last Modified:   6/14/94
#
#  To make use of this software, you need to be running a copy of
#  BIND 4.9 (or later) compiled with the QRYLOG option defined.
#
#  The assumption behind this script is that it will be run out
#  of crontab daily just before some sort of syslog manager
#  copies the current contents of LOGFILE elsewhere before
#  emptying the LOGFILE.  However, it can certainly be run on
#  a LOGFILE that is not emptied daily, and in this case it
#  would merely report the cumulative statistics.
# -------------------------------------------------------------

# -------------------------------------------------------------
#  C O N F I G U R A T I O N    S E C T I O N
# -------------------------------------------------------------

##
##  NOTE:  Ultrix users may want to change the first line
##         of this script from /bin/sh to /bin/sh5.
##

# -------------------------------------------------------------
#  Do we use 'getopts' or 'getopt'?
# -------------------------------------------------------------
GETOPT=getopts

# -------------------------------------------------------------
#  This is the name of the log.
# -------------------------------------------------------------
LOGFILE=/var/log/named

# -------------------------------------------------------------
#  This is the program we use to go from an IP addr to host
# -------------------------------------------------------------
QUERYPROG="/usr/local/bin/dig -x"

# -------------------------------------------------------------
#  This is the awk we use.
# -------------------------------------------------------------
AWK=/bin/awk

# -------------------------------------------------------------
#  This is the directory in which we should create temp files
# -------------------------------------------------------------
TMPDIR=/usr/tmp

# -------------------------------------------------------------
#  This is the default number of entries we want printed in
#  each category of statistics.
# -------------------------------------------------------------
STOPAT=25

# -------------------------------------------------------------
#  E N D    O F    C O N F I G U R A T I O N    S E C T I O N
# -------------------------------------------------------------

# -------------------------------------------------------------
#  handy files
# -------------------------------------------------------------
TMPFILE=$TMPDIR/.dnsstats$$

ADDRFILE=$TMPDIR/.addrs$$
NAMEFILE=$TMPDIR/.names$$
TYPEFILE=$TMPDIR/.types$$
WEEKFILE=$TMPDIR/.week$$
TOTALFILE=$TMPDIR/.total$$

# -------------------------------------------------------------
#  handle arguments
# -------------------------------------------------------------
#       -d <day>
#       This flag is used to append a dot-day suffix to the LOGFILE.
#       Handy where log files are kept around for the last week
#       and contain a day suffix.
#
#       -f <logfile>
#       Change the LOGFILE value altogether.
#
#       -n
#       Don't try to resolve IP addresses from in-addr.arpa names
#       to "regular" names.  Handy if the DNS is slow or you just
#       don't care about the domain names associated with the IP
#       addresses.
#
#       -w
#       Count up all of the DNS statistics for the whole week.
#
#       -c <#>
#       Print only the top-# of entries in each category.
#       Default is $STOPAT. 
#
#       -a
#       Print the entire list of entries in each category.
# -------------------------------------------------------------
NONAMES=0
PRINTALL=0

trap "rm -f $TMPFILE $ADDRFILE $NAMEFILE $TYPEFILE $WEEKFILE $TOTALFILE; exit 0" 0 1 2 3 15

gethostbyaddr() {
        $QUERYPROG $ADDRESS | $AWK ' BEGIN {
            msg = " ** Query failed ** "
            }
            $3 == "PTR" {
                msg = substr($4, 1, length($4) - 1)
            }
            END {
                printf(" %6d  %-39s [%s]\n", count, msg, address)
            }' count=$COUNT address=$ADDRESS - ;
}

if [ $GETOPT = "getopts" ] ; then
        while getopts ac:d:f:nw ARG ; do
                case $ARG in
                        a)      PRINTALL=1
                                ;;
                        c)      STOPAT=$OPTARG
                                ;;
                        d)      LOGFILE=$LOGFILE"."$OPTARG
                                ;;
                        f)      LOGFILE=$OPTARG
                                ;;
                        n)      NONAMES=1
                                ;;
                        w)      cat $LOGFILE* > $WEEKFILE
                                LOGFILE=$WEEKFILE
                                ;;

                esac
        done
        shift `expr $OPTIND - 1`
else
        set -- `getopt ac:d:f:nw $*`
        if [ $? != 0 ] ; then
                exit 2
        fi
        for ARG in $* ; do
                case $ARG in
                        -a)     PRINTALL=1
                                shift
                                ;;
                        -c)     STOPAT=$2
                                shift 2
                                ;;
                        -d)     LOGFILE=$LOGFILE"."$2
                                shift 2
                                ;;
                        -f)     LOGFILE=$2
                                shift 2
                                ;;
                        -n)     NONAMES=1
                                shift
                                ;;
                        -w)     cat $LOGFILE* > $WEEKFILE
                                LOGFILE=$WEEKFILE
                                shift
                                ;;
                        --)     shift
                                break
                                ;;
                esac
        done
fi

# -------------------------------------------------------------
#  divide the log file into three files:
#       one for source addrs of incoming querys
#       one for domain names that were queried upon
#       one for query types
# -------------------------------------------------------------
$AWK '
{
        if ((n == 0) && ($5 == "last"))
                next
        else if ($5 == "last") {
                total += $8
                sender[info[2]] += $8
                query[info[3]] += $8
                querytype[info[4]] += 8
        }
        else if ($6 == "XX") {
                n = split($0, info, "/")
                if (n == 4) {
                        total++
                        sender[info[2]]++
                        query[info[3]]++
                        querytype[info[4]]++
                }
        }
        else {
                n = 0
        }
}
END {
        if (total > 0) {
                for (i in sender)
                        print sender[i], i >f1
                for (i in query)
                        print query[i], i >f2
                for (i in querytype)
                        print querytype[i], i >f3
                print total >f4
        }
}' f1=$ADDRFILE f2=$NAMEFILE f3=$TYPEFILE f4=$TOTALFILE $LOGFILE

# -------------------------------------------------------------
#  Bail out if info was not produced
# -------------------------------------------------------------
if [ ! -s $ADDRFILE -a -s $NAMEFILE -a -s $TYPEFILE -a -s $TOTALFILE ] ; then
        echo "No data on which to operate"
        rm -f $TMPFILE $ADDRFILE $NAMEFILE $TYPEFILE $WEEKFILE $TOTALFILE
        exit 0
fi

# -------------------------------------------------------------
#  Print some general information
# -------------------------------------------------------------
echo "DNS stats for" `hostname` "for period ending" `ls -l $LOGFILE | $AWK '{ print $5, $6, $7 }'`
echo "Total queries received: " `cat $TOTALFILE`
echo
echo "Part I -- query sources"
echo

# -------------------------------------------------------------
#  First, tell who was querying this nameserver
# -------------------------------------------------------------
if [ $NONAMES -eq 0 ] ; then
        echo " Number   Source (by name if available)           IP address"
        echo " ------   -----------------------------           ----------"
        sort -rn $ADDRFILE > $TMPFILE
        if [ $PRINTALL -eq 1 ] ; then
                mv $TMPFILE $ADDRFILE
        else
                head -$STOPAT $TMPFILE > $ADDRFILE
        fi
        while [ 1 ] ; do
                read COUNT ADDRESS
                if [ $? -ne 0 ] ; then
                        break
                fi
                gethostbyaddr
        done < $ADDRFILE
else
        echo " Number  IP address"
        echo " ------  ----------"
        sort -rn $ADDRFILE > $TMPFILE
        if [ $PRINTALL -eq 1 ] ; then
                $AWK '{ printf(" %6d  [%s]\n", $1, $2) }' $TMPFILE
        else
                head -$STOPAT $TMPFILE | $AWK '{ printf(" %5d  [%s]\n", $1, $2) }'
        fi
fi

# -------------------------------------------------------------
#  Second, tell what names were being queried upon
# -------------------------------------------------------------
echo
echo "Part II -- queried names"
echo
echo " Number  Queried name"
echo " ------  ------------"
sort -rn $NAMEFILE > $TMPFILE
if [ $PRINTALL -eq 1 ] ; then
        $AWK '{ printf(" %6d  %s\n", $1, $2) }' $TMPFILE
else
        head -$STOPAT $TMPFILE | $AWK '{ printf(" %6d  %s\n", $1, $2) }'
fi

# -------------------------------------------------------------
#  Third, tell what sort of queries there were
# -------------------------------------------------------------
echo
echo "Part III -- query types"
echo
echo " Number  Type"
echo " ------  ----"
sort -rn $TYPEFILE > $TMPFILE
if [ $PRINTALL -eq 1 ] ; then
        $AWK '{ printf(" %6d  %s\n", $1, $2) }' $TMPFILE
else
        head -$STOPAT $TMPFILE | $AWK '{ printf(" %6d  %s\n", $1, $2) }'
fi

# -------------------------------------------------------------
#  Last, tidy things up
# -------------------------------------------------------------
rm -f $TMPFILE $ADDRFILE $NAMEFILE $TYPEFILE $WEEKFILE $TOTALFILE
