#!/usr/bin/perl
##########################################################################
# $Id: named,v 1.34 2004/02/03 18:39:34 kirk Exp $
##########################################################################
# $Log: named,v $
# Revision 1.34  2004/02/03 18:39:34  kirk
# Patches from [ISO-8859-2] Pawe? Go?aszewski" <blues@ds.pg.gda.pl>
#
# Revision 1.33  2004/02/03 04:18:55  kirk
# Patch from David Golden <david@hyperbolic.net>
#
# Revision 1.32  2004/02/03 03:36:39  kirk
# Patches from Anssi Kolehmainen <kolean-5.listat@pp.inet.fi>
#
# Revision 1.31  2004/02/03 02:45:26  kirk
# Tons of patches, and new 'oidentd' and 'shaperd' filters from
# Pawe? Go?aszewski" <blues@ds.pg.gda.pl>
#
##########################################################################

########################################################
# This was written and is maintained by:
#    Kirk Bauer <kirk@kaybee.org>
#
# Please send all comments, suggestions, bug reports,
#    etc, to kirk@kaybee.org.
########################################################

use Logwatch ':ip';

$Debug = $ENV{'LOGWATCH_DEBUG'};
$DoLookup = $ENV{'named_ip_lookup'};
$Detail = $ENV{'LOGWATCH_DETAIL_LEVEL'} || 0;

if ( $Debug >= 5 ) {
    print STDERR "\n\nDEBUG: Inside Named Filter \n\n";
}

while (defined($ThisLine = <STDIN>)) {
   if (
      ($ThisLine =~ /RR negative cache entry/) or
      ($ThisLine =~ /^ns_....: .* NS points to CNAME/) or
      ($ThisLine =~ /^accept: connection reset by peer/) or
      ($ThisLine =~ /Connection reset by peer/) or
      ($ThisLine =~ /transfered serial/) or
      ($ThisLine =~ /^There may be a name server already running/) or
      ($ThisLine =~ /^exiting/) or
      ($ThisLine =~ /^NSTATS /) or
      ($ThisLine =~ /Cleaned cache of \d+ RRs/) or
      ($ThisLine =~ /USAGE \d+ \d+ CPU=\d+.*/) or
      ($ThisLine =~ /^XSTATS /) or
      ($ThisLine =~ /^Ready to answer queries/) or
      ($ThisLine =~ /^Forwarding source address is/) or
      ($ThisLine =~ /^bad referral/) or
      ($ThisLine =~ /prerequisite not satisfied/) or
      ($ThisLine =~ /^(rcvd|Sent) NOTIFY/) or
      ($ThisLine =~ /^ns_resp: TCP truncated/) or
      ($ThisLine =~ /No possible A RRs/) or
      ($ThisLine =~ /points to a CNAME/) or
      ($ThisLine =~ /^dangling CNAME pointer/) or
      ($ThisLine =~ /^listening on/) or
      ($ThisLine =~ /^unrelated additional info/) or
      ($ThisLine =~ /^Response from unexpected source/) or
      ($ThisLine =~ /^No root nameservers for class IN/) or
      ($ThisLine =~ /^recvfrom: No route to host/) or
      ($ThisLine =~ /Connection refused/) or
      ($ThisLine =~ /lame server resolving/) or
      ($ThisLine =~ /transfer of/) or
      ($ThisLine =~ /^using \d+ CPU/) or
      ($ThisLine =~ /^loading configuration/) or
      ($ThisLine =~ /^command channel listening/) or
      ($ThisLine =~ /^no IPv6 interfaces found/) or
      ($ThisLine =~ /^running/) or
      ($ThisLine =~ /^exiting/) or
      ($ThisLine =~ /^no longer listening/) or
      ($ThisLine =~ /^the default for the .* option is now/) or
      ($ThisLine =~ /^stopping command channel on [0-9.#]/) or
      ($ThisLine =~ /^Malformed response from/) or
      ($ThisLine =~ /client .+#\d+: query:/) or
      # Do we really want to ignore these?
      #($ThisLine =~ /unknown logging category/) or
      ($ThisLine =~ /^could not open entropy source/) or
      ($ThisLine =~ /\/etc\/rndc.key: file not found/) or
      ($ThisLine =~ /sending notifies/) or
      # file syntax error get reported twice and are already caught below
      ($ThisLine =~ /loading master file/)
   ) {
      # Don't care about these...
   } elsif (
      ($ThisLine =~ /^starting\..*named/) or
      ($ThisLine =~ /^starting BIND/) or
      ($ThisLine =~ /^named startup succeeded/)
   ) {
      $StartNamed++;
   } elsif ( $ThisLine =~ /^(reloading nameserver|named reload succeeded)/ ) {
      $ReloadNamed++;
   } elsif (
      ($ThisLine =~ /^shutting down/) or
      ($ThisLine =~ /^named shutting down/ ) or
      ($ThisLine =~ /^named shutdown succeeded/ )
   ) {
      $ShutdownNamed++;
   } elsif ( ($Host, $Zone) = ( $ThisLine =~ /client ([^\#]+)#[^\:]+: zone transfer '(.+)' denied/ ) ) {
      $DeniedZoneTransfers{$Host}{$Zone}++;
   } elsif ( ($Zone) = ( $ThisLine =~ /cache zone \"(.*)\" loaded/ ) ) {
      $ZoneLoaded{"cache $Zone"}++;
   } elsif ( ($Zone) = ( $ThisLine =~ /cache zone \"(.*)\" .* loaded/ ) ) {
      $ZoneLoaded{"cache $Zone"}++;
   } elsif ( ($Zone) = ( $ThisLine =~ /primary zone \"(.+)\" loaded/ ) ) {
      $ZoneLoaded{$Zone}++;
   } elsif ( ($Zone) = ( $ThisLine =~ /master zone \"(.+)\" .* loaded/ ) ) {
      $ZoneLoaded{$Zone}++;
   } elsif ( ($Zone) = ( $ThisLine =~ /secondary zone \"(.+)\" loaded/ ) ) {
      $ZoneLoaded{"secondary $Zone"}++;
   } elsif ( ($Zone) = ( $ThisLine =~ /slave zone \"(.+)\" .* loaded/ ) ) {
      $ZoneLoaded{"secondary $Zone"}++;
   } elsif ( ($Zone) = ( $ThisLine =~ /^zone (.+)\: loaded serial/ ) ) {
      $ZoneLoaded{$Zone}++;
   } elsif ( (undef,$Addr,undef,$Server) = ( $ThisLine =~ /ame server (on|resolving) '(.+)' \(in .+\):\s+(\[.+\]\.\d+)?\s*'?(.+)'?:?/ ) ) {
      $LameServer{"$Addr ($Server)"}++;
   } elsif ( ($Zone) = ( $ThisLine =~ /Zone \"(.+)\" was removed/ ) ) {
      $ZoneRemoved{$Zone}++;
   } elsif ( ($Host) = ( $ThisLine =~ /^([^ ]+) has CNAME and other data \(invalid\)/ ) ) {
      push @CNAMEAndOther, $Host;
   } elsif ( ($File,$Line,$Entry,$Error) = ( $ThisLine =~ /dns_master_load: ([^:]+):(\d+): ([^ ]+): (.+)$/ ) ) {
      $ZoneFileErrors{$File}{"$Entry: $Error"}++;
   } elsif ( ($Way,$Host) = ( $ThisLine =~ /^([^ ]+): sendto\(\[([^ ]+)\].+\): Network is unreachable/ ) ) {
      $FullHost = LookupIP ($Host);
      $NetworkUnreachable{$Way}{$FullHost}++;
   } elsif ( ($Zone,$Message) = ( $ThisLine =~ /^client [^\#]+#[^\:]+: updating zone '([^\:]+)': (.*)$/ ) ) {
      $ZoneUpdates{$Zone}{$Message}++;
   } elsif ( ($Host,$Zone) = ( $ThisLine =~ /approved AXFR from \[(.+)\]\..+ for \"(.+)\"/ ) ) {
      $FullHost = LookupIP ($Host);
      $AXFR{$Zone}{$FullHost}++;
   } elsif ( ($Client) = ( $ThisLine =~ /client (.*)#\d+: query \(cache\) denied/ ) ) {
      $FullClient = LookupIP ($Client);
      $DeniedQuery{$FullClient}++;
   } elsif ( ($Rhost, $Ldom) = ($ThisLine =~ /^client ([\d\.]+)#\d+: update '(.*)' denied/)) {
      $UpdateDenied{"$Rhost ($Ldom)"}++;
   } elsif ( ($Zone) = ($ThisLine =~ /^zone '([0-9a-zA-Z.-]+)' allows updates by IP address, which is insecure/)) {
      $InsecUpdate{$Zone}++;
   } elsif ( ($Zone) = ($ThisLine =~ /^zone ([0-9a-zA-Z.\/-]+): journal rollforward failed: journal out of sync with zone/)) {
      $JournalFail{$Zone}++;
   } elsif ( ($Channel,$Reason) = ($ThisLine =~ /^couldn't add command channel (.+#\d+): (.*)$/)) {
      $ChannelAddFail{$Channel}{$Reason}++;
   } elsif ( ($Zone,$Host,$Reason) = ($ThisLine =~ /^zone ([^ ]*)\/IN: refresh: failure trying master ([^ ]*)#\d+: (.*)/) ) {
      $MasterFailure{"$Zone from $Host"}{$Reason}++;
   } elsif ( ($Zone) = ($ThisLine =~ /^zone ([^\/]+)\/.+: refresh: non-authoritative answer from master/)) {
      $NonAuthoritative{$Zone}++;
   } else {
      # Report any unmatched entries...
      # remove PID from named messages
      $ThisLine =~ s/^(client [.0-9]+)\S+/$1/;
      chomp($ThisLine);
      $OtherList{$ThisLine}++;
   }
}

#######################################

if ( ( $Detail >= 5 ) and ($StartNamed) ) {
   print "Named started: $StartNamed Time(s)\n";
}

if ( ( $Detail >= 5 ) and ($ReloadNamed) ) {
   print "Named reloaded: $ReloadNamed Time(s)\n";
}

if ( ( $Detail >= 5 ) and ($ShutdownNamed) ) {
   print "Named shutdown: $ShutdownNamed Time(s)\n";
}

if ( ( $Detail >= 5 ) and (keys %ZoneLoaded) ) {
   print "\nLoaded Zones:\n";
   foreach $ThisOne (sort {$a cmp $b} keys %ZoneLoaded) {
      print "   $ThisOne: $ZoneLoaded{$ThisOne} Time(s)\n";
   }
}

if ( ($Detail >= 5) and (keys %ChannelAddFail) ) {
   print "\nCan't add command channel:\n";
   foreach $Channel (sort {$a cmp $b} keys %ChannelAddFail) {
      print "   $Channel:\n";
      foreach $Reason (sort {$a cmp $b} keys %{$ChannelAddFail{$Channel}}) {
         print "      $Reason: $ChannelAddFail{$Channel}{$Reason} Time(s)\n";
      }
   }
}

if ( ($Detail >= 5) and (keys %MasterFailure) ) {
   print "\nFailure trying to refresh zone:\n";
   foreach $Zone (sort {$a cmp $b} keys %MasterFailure) {
      print "   $Zone:\n";
      foreach $Reason (sort {$a cmp $b} keys %{$MasterFailure{$Zone}}) {
         print "      $Reason: $MasterFailure{$Zone}{$Reason}++ Time(s)\n";
      }
   }
}

if ( ( $Detail >= 5 ) and (keys %DeniedZoneTransfers) ) {
   print "\nDenied Zone Transfers:\n";
   foreach my $Host (keys %DeniedZoneTransfers) {
      print "   $Host: ";
      foreach my $Zone (keys %{$DeniedZoneTransfers{$Host}}) {
         print $DeniedZoneTransfers{$Host}{$Zone}. ' ';
      }
      print "\n";
   }
}

if ( ( $Detail >= 5 ) and (keys %ZoneRemoved) ) {
   print "\nRemoved Zones:\n";
   foreach $ThisOne (sort {$a cmp $b} keys %ZoneRemoved) {
      print "   $ThisOne: $ZoneRemoved{$ThisOne} Time(s)\n";
   }
}

if ( ( $Detail >= 5 ) and (keys %AXFR) ) {
   print "\nZone Transfers:\n";
   foreach $ThisOne (keys %AXFR) {
      print "   Zone: $ThisOne\n";
      foreach $Temp (keys %{$AXFR{$ThisOne}}) {
         print "      by $Temp: $AXFR{$ThisOne}{$Temp} Time(s)\n";
      }
   }
}

if ( ( $Detail >= 5 ) and (keys %DeniedQuery) ) {
   print "\nQueries (cache) that were denied:\n";
   foreach $ThisOne (keys %DeniedQuery) {
      print "   from $ThisOne: $DeniedQuery{$ThisOne} Time(s)\n";
   }
}

if ( ( $Detail >= 10 ) and (@CNAMEAndOther) ) {
   print "\nThese hosts have CNAME and other data (invalid):\n";
   foreach $ThisOne (@CNAMEAndOther) {
      print "   $ThisOne\n";
   }
}

if ( ( $Detail >= 5 ) and (keys %ZoneFileErrors) ) {
   print "\nSyntax errors in zone files:\n";
   for $File (keys %ZoneFileErrors) {
      print "   $File\n";
      for $Error ( keys %{$ZoneFileErrors{$File}} ) {
         print "      \"$Error\" " . $ZoneFileErrors{$File}{$Error} . " Time(s)\n";
      }
   }
}

if ( ( $Detail >= 10 ) and (keys %LameServer) ) {
   print "\nThese addresses had lame server references:\n";
   foreach $ThisOne (keys %LameServer) {
      print "   $ThisOne: $LameServer{$ThisOne} Time(s)\n";
   }
}

if ( ( $Detail >= 10 ) and (keys %NonAuthoritative) ) {
   print "\nNon-authoritative answer from master for these zones:\n";
   foreach $ThisOne (keys %NonAuthoritative) {
      print "   " . $ThisOne . ": " . $NonAuthoritative{$ThisOne} . " Time(s)\n";
   }
}

if ( ( $Detail >= 10 ) and (keys %NetworkUnreachable) ) {
   print "\nNetwork is unreachable for:\n";
   foreach $ThisOne (sort {$a cmp $b} keys %NetworkUnreachable) {
      print "   $ThisOne:\n";
      foreach $Host (sort {$a cmp $b} keys %{$NetworkUnreachable{$ThisOne}}) {
         print "      $Host: $NetworkUnreachable{$ThisOne}{$Host} Time(s)\n";
      } 
   }
}

if ( ( $Detail >= 5 ) and (keys %ZoneUpdates) ) {
   print "\nZone Updates:\n";
   foreach $ThisOne (sort {$a cmp $b} keys %ZoneUpdates) {
      print "   $ThisOne:\n";
      foreach $Message (sort {$a cmp $b} keys %{$ZoneUpdates{$ThisOne}}) {
         print "      $Message: $ZoneUpdates{$ThisOne}{$Message} Time(s)\n";
      } 
   }
}

if ( keys %UpdateDenied ) {
   print "\nZone update refused:\n";
   foreach $ThisOne (sort {$a cmp $b} keys %UpdateDenied) {
      print "   $ThisOne: $UpdateDenied{$ThisOne} Time(s)\n";
   }
}

if ( keys %InsecUpdate ) {
   print "\nInsecure zones (dynamic update allowed by IP address):\n";
   foreach $ThisOne (sort {$a cmp $b} keys %InsecUpdate) {
      print "   " . $ThisOne . ": " . $InsecUpdate{$ThisOne} . " Time(s)\n";
   }
}

if ( keys %JournalFail ) {
   print "\nJournall rollforward failed:\n";
   foreach $ThisOne (sort {$a cmp $b} keys %JournalFail) {
      print "   " . $ThisOne . ": " . $JournalFail{$ThisOne} . " Time(s)\n";
   }
}

if (keys %OtherList) {
   print "\n**Unmatched Entries**\n";
   foreach $line (sort {$a cmp $b} keys %OtherList) {
      print "   $line: $OtherList{$line} Time(s)\n";
   }
}

exit(0);

# vi: shiftwidth=3 tabstop=3 et

