
##########################################################################
##########################################################################
#
#  Barracuda Spam Firewall parser
#  Written by Hugo van der Kooij <hugo@vanderkooij.org> (HvdK)
#  Based on existing code of logwatch and the docuemntation on:
#  http://www.barracudanetworks.com/ns/downloads/BarracudaSyslog.pdf
#
# Revision 0.9  2007/08/27 HvdK
# Started the scanner for the logs of Barracuda Spam firewalls.

########################################################
## Copyright (c) 2008 Hugo van der Kooij <hugo@vanderkooij.org>
## Covered under the included MIT/X-Consortium License:
##    http://www.opensource.org/licenses/mit-license.php
## All modifications and contributions by other persons to
## this script are assumed to have been donated to the
## Logwatch project and thus assume the above copyright
## and licensing terms.  If you want to make contributions
## under your own copyright or a different license this
## must be explicitly stated in the contribution an the
## Logwatch project reserves the right to not accept such
## contributions.  If you have made significant
## contributions to this script and want to claim
## copyright please contact logwatch-devel@lists.sourceforge.net.
#########################################################

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

sub Round {
   my $input = $_[0];
   my $digits = $_[1];
   my $factor = (10**$digits);
   my $result = (int( ($input * $factor) + .5) / $factor);
   return $result;
}

# Text messages for the RECV and SCAN action codes:
$RSaction[0] = "Allowed Message(s)";
$RSaction[1] = "Aborted Message(s)";
$RSaction[2] = "Blocked Message(s)";
$RSaction[3] = "Quarantined Message(s)";
$RSaction[4] = "Tagged Message(s)";
$RSaction[5] = "Deferred Message(s)";
$RSaction[6] = "Per-User Quarantined Message(s)";
$RSaction[7] = "Whitelisted Message(s)";

# Text messages for the SEND action codes:
$Saction[0] = "UNKNOWN";
$Saction[1] = "Delivered Message(s)";
$Saction[2] = "Rejected Message(s)";
$Saction[3] = "Deferred Message(s)";
$Saction[4] = "Expired Message(s)";

# Text messages for the reason codes:
$Reason[0] = "Allowed";
$Reason[1] = "Virus detected";
$Reason[2] = "Banned attachment";
$Reason[3] = "RBL Match";
$Reason[4] = "Rate Control";
$Reason[5] = "Too Many Message(s) In Session";
$Reason[6] = "Timeout Exceeded";
$Reason[7] = "No such Domain";
$Reason[8] = "No such User";
$Reason[9] = "Subject Filter Match";
$Reason[10] = "UNKNOWN (10)";
$Reason[11] = "Client IP";
$Reason[12] = "Recipient Address Rejected";
$Reason[13] = "No valid Recipients";
$Reason[14] = "Domain Not Found";
$Reason[15] = "Sender Address Rejected";
$Reason[16] = "UNKNOWN (16)";
$Reason[17] = "Need Fully Qualified Recipient";
$Reason[18] = "Need Fully Qualified Sender";
$Reason[19] = "Unsupported Command";
$Reason[20] = "MAIL FROM Syntax Error";
$Reason[21] = "Bad Address Syntax";
$Reason[22] = "RCPT TO Syntax Error";
$Reason[23] = "Send EHLO/HELO First";
$Reason[24] = "Need MAIL Command";
$Reason[25] = "Nested MAIL Comand";
$Reason[26] = "UNKNOWN (26)";
$Reason[27] = "EHLO/HELO Syntax Error";
$Reason[28] = "UNKNOWN (28)";
$Reason[29] = "UNKNOWN (29)";
$Reason[30] = "Mail Protocol Error";
$Reason[31] = "Score";
$Reason[32] = "UNKNOWN (32)";
$Reason[33] = "UNKNOWN (33)";
$Reason[34] = "Header Filter Match";
$Reason[35] = "Sender Block/Accept";
$Reason[36] = "Recipient Block/Accept";
$Reason[37] = "Body Filter Match";
$Reason[38] = "Message Size Bypass";
$Reason[39] = "Intention Analysis Match";
$Reason[40] = "SPF/Caller-ID";
$Reason[41] = "Client Host Rejected";
$Reason[42] = "UNKNOWN (42)";
$Reason[43] = "UNKNOWN (43)";
$Reason[44] = "Authentication Not Enabed";
$Reason[45] = "Allowed Message Size Exceeded";
$Reason[46] = "Too Many Recipients";
$Reason[47] = "Need RCPT Command";
$Reason[48] = "DATA Syntax Error";
$Reason[49] = "Internal Error";
$Reason[50] = "Too Many Hops";
$Reason[51] = "UNKNOWN (51)";
$Reason[52] = "UNKNOWN (52)";
$Reason[53] = "UNKNOWN (53)";
$Reason[54] = "UNKNOWN (54)";
$Reason[55] = "Invalid Parameter Syntax";
$Reason[56] = "STARTTLS Syntax Error";
$Reason[57] = "TLS Allready Active";
$Reason[58] = "Too Many Errors";
$Reason[59] = "Need STARTTLS First";
$Reason[60] = "Spam Fingerprint Found";

while (defined($ThisLine = <STDIN>))
{
   if ( ($address, $id, $start_time, $end_time, $type, $info) = ($ThisLine =~ /([^\s]+)\s([^\s]+)\s(\d+)\s(\d+)\s(RECV|SCAN|SEND)\s(.*)$/) )
   {
      ($ip) = ($address =~ /\[(.*)\]/);
      $time = ($end_time - $start_time);
      if ( $type =~ /RECV/ )
      {
         if ( ($sender, $recipient, $action, $reason, $reason_extra) = ($info =~ /([^\s]+)\s([^\s]+)\s(\d+)\s(\d+)\s(.*)$/) ) {
            $RECVaction{$action}++;
            $RECVreason{$reason}++;
            $RECVreason_extra{$reason_extra}++;
            if ( ($reason_extra =~ /^-[| ]$/) or ($reason < 1) ) {
            } else {
               $RECVReasons{$reason_extra}++;
               $RECVReason{$reason_extra} = $reason;
            }
            $RECVtime_total{$action} = ($RECVtime_total{$action} + $time);
            $RECVtime_div{$action}++;
            if ($time < $RECVtime_min{$action}) {
               $RECVtime_min{$action} = $time;
            } elsif ($RECVtime_min{$action} eq "") {
               $RECVtime_min{$action} = $time;
            }
            if ($time > $RECVtime_max{$action}) {
               $RECVtime_max{$action} = $time;
            } elsif ($RECVtime_max{$action} eq "") {
               $RECVtime_max{$action} = $time;
            }
         } else {
            push @OtherList,$ThisLine;
         }
      } elsif ( $type =~ /SCAN/ ) {
         if ( ($enc, $sender, $recipient, $score, $action, $reason, $reason_extra, $subject) = ($info =~ /([^\s]+)\s([^\s]+)\s([^\s]+)\s([-\.\d+]+)\s(\d+)\s(\d+)\s(.*)\sSUBJ:(.*)$/) ) {
            if ( ($size) = ($reason_extra =~ /\sSZ:(\d+)$/) ) {
               $reason_extra =~ s/\sSZ:(\d+)$//;
               $SCANsize_total{$action} = ($SCANsize_total{$action} + $size);
               $SCANsize_div{$action}++;
            }
            $SCANaction{$action}++;
            $SCANreason{$reason}++;
            if ($score =~ /^-$/) {
            } else {
               $score_total{$action} = ($score_total{$action} + $score);
               $score_div{$action}++;
               if ($score < $score_min{$action}) {
                  $score_min{$action} = $score;
               } elsif ($score_min{$action} eq "") {
                  $score_min{$action} = $score;
               }
               if ($score > $score_max{$action}) {
                  $score_max{$action} = $score;
               } elsif ($score_max{$action} eq "") {
                  $score_max{$action} = $score;
               }
            }
            if ($reason_extra =~ /^-$/) {
            } else {
               $SCANReasons{$reason_extra}++;
               $SCANReason{$reason_extra} = $reason;
            }
            $SCANtime_total{$action} = ($SCANtime_total{$action} + $time);
            $SCANtime_div{$action}++;
            if ($time < $SCANtime_min{$action}) {
               $SCANtime_min{$action} = $time;
            } elsif ($SCANtime_min{$action} eq "") {
               $SCANtime_min{$action} = $time;
            }
            if ($time > $SCANtime_max{$action}) {
               $SCANtime_max{$action} = $time;
            } elsif ($SCANtime_max{$action} eq "") {
               $SCANtime_max{$action} = $time;
            }
         } else {
            push @OtherList,$ThisLine;
         }
      } elsif ( $type =~ /SEND/ ) {
         if ( ($enc, $action, $qid, $response) = ($info =~ /([^\s]+)\s(\d+)\s([^\s]+)\s(.*)$/) ) {
            $SENDaction{$action}++;
            if (($delivery) = ($response =~/^(\d+)\s.*/) ) {
               $SENDresponse{$delivery}++;
            }
         } else {
            push @OtherList,$ThisLine;
         }
      } else {
         push @OtherList,$ThisLine;
      }
   } elsif ( $ThisLine =~ /\s(RECV|SCAN|SEND)\s/) {
      push @OtherList,$ThisLine;
   }
}

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

if ($#OtherList >= 0)
{
   print "\n\n**Unmatched Entries**\n\n";
   print @OtherList;
}

if (keys %RECVaction) {
   print "\nReceive actions:\n";
   foreach $action (sort {$a cmp $b} keys %RECVaction) {
      print '   ' . $RSaction[$action] . ": ". $RECVaction{$action} . "\n";
   }
}

if (keys %SCANaction) {
   print "\nScanning actions:\n";
   foreach $action (sort {$a cmp $b} keys %SCANaction) {
      print '   ' . $RSaction[$action] . ": ". $SCANaction{$action} . "\n";
   }
}

if (keys %SENDaction) {
   print "\nSending actions:\n";
   foreach $action (sort {$a cmp $b} keys %SENDaction) {
      print '   ' . $Saction[$action] . ": ". $SENDaction{$action} . "\n";
   }
}

if (keys %RECVreason) {
   print "\nBlocked incoming messages:\n";
   foreach $reason (sort {$a cmp $b} keys %RECVreason) {
      if ($reason > 0) {
         print '   ' . $Reason[$reason] . " : ". $RECVreason{$reason} . "\n";
      }
   }
}

if ( ($Detail >= 10) and (keys %RECVReasons) ) {
   print "\nDetails of blocked incoming messages :\n";
   foreach $reason (sort {$a cmp $b} keys %RECVReasons) {
      print '   ' . $Reason[$RECVReason{$reason}] . " : " . $reason . " : ". $RECVReasons{$reason} . "\n";
   }
}

if (keys %RECVtime_total) {
   print "\nReceive timing:\n";
   foreach $action (sort {$a cmp $b} keys %RECVtime_total) {
      $RECVtime = ($RECVtime_total{$action} / $RECVtime_div{$action});
      print '   ' . $RSaction[$action] . " :\n";
      print "\tMinimum : " . $RECVtime_min{$action} . "\n";
      print "\tAverage : " . Round($RECVtime, 1) . "\n";
      print "\tMaximum : " . $RECVtime_max{$action} . "\n";
   }
}

if (keys %SCANreason) {
   print "\nDecisions while scanning:\n";
   foreach $reason (sort {$a cmp $b} keys %SCANreason) {
      print '   ' . $Reason[$reason] . " : ". $SCANreason{$reason} . "\n";
   }
}

if ( ($Detail >= 10) and (keys %SCANReasons) ) {
   print "\nDetails of decisions while scanning:\n";
   foreach $reason (sort {$a cmp $b} keys %SCANReasons) {
      print '   ' . $Reason[$SCANReason{$reason}] . " : " . $reason . " : ". $SCANReasons{$reason} . "\n";
   }
}

if (keys %SCANtime_total) {
   print "\nScan timing:\n";
   foreach $action (sort {$a cmp $b} keys %SCANtime_total) {
      $SCANtime = ($SCANtime_total{$action} / $SCANtime_div{$action});
      print '   ' . $RSaction[$action] . " :\n";
      print "\tMinimum : " . $SCANtime_min{$action} . "\n";
      print "\tAverage : " . Round($SCANtime, 1) . "\n";
      print "\tMaximum : " . $SCANtime_max{$action} . "\n";
   }
}

if (keys %SCANsize_total) {
   print "\nMessage sizes:\n";
   foreach $action (sort {$a cmp $b} keys %SCANsize_total) {
      $SCANsize = ($SCANsize_total{$action} / $SCANsize_div{$action});
      print '   ' . $RSaction[$action] . " :\n";
#      print "\tMinimum : " . $SCANsize_min{$action} . "\n";
      print "\tAverage : " . Round($SCANsize, 0) . "\n";
#      print "\tMaximum : " . $SCANsize_max{$action} . "\n";
   }
}


if (keys %SENDreason) {
   print "\nSending reasons:\n";
   foreach $reason (sort {$a cmp $b} keys %SENDreason) {
      print '   ' . $Reason[$reason] . " : ". $SENDreason{$reason} . "\n";
   }
}

if (keys %SENDresponse) {
   print "\nSending responses:\n";
   foreach $response (sort {$a cmp $b} keys %SENDresponse) {
      print '   ' . $response . ": ". $SENDresponse{$response} . "\n";
   }
}

if (keys %score_total) {
   print "\nScores:\n";
   foreach $action (sort {$a cmp $b} keys %score_total) {
      $score = ($score_total{$action} / $score_div{$action});
      print '   ' . $RSaction[$action] . " :\n";
      print "\tMinimum : " . $score_min{$action} . "\n";
      print "\tAverage : " . Round($score, 3) . "\n";
      print "\tMaximum : " . $score_max{$action} . "\n";
   }
}

exit(0);

# vi: shiftwidth=3 tabstop=3 syntax=perl et
# Local Variables:
# mode: perl
# perl-indent-level: 3
# indent-tabs-mode: nil
# End:
