Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

AXFR zones off a local BIND9 or NSD3 server as they're updated

6 views
Skip to first unread message

Ivan Shmakov

unread,
May 16, 2013, 6:52:28 AM5/16/13
to
Archive-name: logaxfr.sh-2013-is
Submitted-by: onei...@gmail.com
Last-modified: 2013-04-27 19:14:11 +00:00
Copyright-Notice: Both the README and the code are under the
CC0 Public Domain Dedication.

[Followup-To: set to news:alt.sources.d, as per the
news:alt.sources' charter, and also news:comp.protocols.tcp-ip,
news:comp.unix.shell.]


Synopsis

$ [DIG=/where/is/dig] logaxfr PREFIX [LOGFILE]


Summary

This simplistic Bash script monitors the log file specified (or
/var/log/daemon.log by default) and executes dig(1) to perform
an AXFR query, saving its results under the prefix specified.
The initial part of the command to be executed can be passed via
the DIG environment variable, and defaults to "dig".

The log file's inode is periodically checked to remain the same.
Once it's changed, the log file is re-opened, so that this
script doesn't need to be restarted when the logs are rotated.

Please note that should the prefix given designate a directory,
a trailing slash (/) will be appended to it automatically.
(No such change is done otherwise.)

Assuming a prefix of "hist/axfr/" (or "hist/axfr"), the
resulting filename for the example.org zone could be like:

hist/axfr/example.org-1368701214.axfr


Bugs

The AXFR query is sent to ::1 (the IPv6 localhost address)
irrespective of the actual log file data. The latter may
indicate a different host, should the host the script is run on
be collecting syslog data from other hosts as well.

The log entry parsers are rather crude.

It should be possible to specify the filename suffix, too.

Please also check the FIXME: comments within the code.

README.ykjymdfg98oh6fgsknarsdffon ends here

#!/bin/bash
### ykjymdfg98oh6fgsknarsdffon.sh -*- Sh -*-

### Ivan Shmakov, 2013

## To the extent possible under law, the author(s) have dedicated all
## copyright and related and neighboring rights to this software to the
## public domain worldwide. This software is distributed without any
## warranty.

## You should have received a copy of the CC0 Public Domain Dedication
## along with this software. If not, see
## <http://creativecommons.org/publicdomain/zero/1.0/>.

### Code:

progname=${0##*/}

if [ $# -lt 1 -o $# -gt 2 ] ; then
printf 'Usage:\n %s PREFIX [LOGFILE]' >&2
exit 1
fi

p=${1}
s=.axfr
in=${2-/var/log/daemon.log}

: "${DIG:=dig}"
## FIXME: allow user to set these via CLI?
dig_options='@::1'

exec < "$in"
ino=$(stat -L -c %i -- -)

## NB: check if a directory is to be used
if test -d "$p" -a "$p" = "${p%/}" ; then
p=${p}/
fi

try_bind9 () {
## try_bind9 ${log-line} => [ sets ${zone} ${serial} ] matches?
local x="$1" se1 z1
## FIXME: too ad-hoc-ish an approach
case "$x" in
## BIND9 log message
(*/IN*serial*) ;;
(*) return 1 ;;
esac
## .
se1=${x##*serial } \
&& serial=${se1%%[^0-9]*} \
&& z1=${x%/IN*} \
&& zone=${z1##* } \
&& test -n "$zone" -a "$zone" != "$x" -a "$zone" != "$z1"
}

try_nsd3 () {
## try_nsd3 ${log-line} => [ sets ${zone} ${serial} ] matches?
local x="$1" se1 z1
## FIXME: too ad-hoc-ish an approach
case "$x" in
## NSD3 log message
(*Zone*serial*updated*to*) ;;
(*) return 1 ;;
esac
## .
se1=${x##*serial*updated*to } \
&& serial=${se1%%[^0-9]*} \
&& test "$serial". = "$se1" \
&& z1=${x% serial*} \
&& zone=${z1##* } \
&& test -n "$zone" -a "$zone" != "$x" -a "$zone" != "$z1"
}

declare -A -- zs ss
while : ; do
if ! read -r -t 5 -- x ; then
## check any of the zones are to be AXFR'ed
if [ "${#zs[@]}" -gt 0 ] ; then
date --rfc-3339=seconds >&2
for z in "${!zs[@]}" ; do
printf ' %-15s (%s)\n' "$z" "${ss[$z]}"
done >&2
for n in "${!zs[@]}" ; do
## FIXME: the output grep'ed may be unwanted
${DIG} ${dig_options} axfr "$n" \
| tee -- "${p}${n}-$(date +%s)${s}" \
| grep -F -- SOA
done
zs=()
fi
sleep 7s
## check if the log file needs to be reopened
## FIXME: better error detection?
inew=$(stat -L -c %i -- - < "$in") \
|| continue
if [ "$inew" != "$ino" ] ; then
## NB: inew may change here; avoid TOCTTOU
exec < "$in"
inew=$(stat -L -c %i -- -)
date --rfc-3339=seconds >&2
printf ' reopen %s (%s; was: %s)\n' \
"$in" "$inew" "$ino" >&2
ino=${inew}
fi
continue
fi
if try_bind9 "$x" \
|| try_nsd3 "$x" ; then
if [ "$serial" != "${ss[$zone]}" ] ; then
zs[$zone]=1
fi
ss[$zone]=${serial}
fi
done

### Emacs trailer
## Local variables:
## coding: us-ascii
## fill-column: 72
## indent-tabs-mode: nil
## ispell-local-dictionary: "american"
## End:
### ykjymdfg98oh6fgsknarsdffon.sh ends here

--
FSF associate member #7257
0 new messages